I'm working on a page which contains various Youtube video thumbnails in divs which have links attached to each <img> and has Masonry on the container to control class switching to resize selected videos.
The idea is that the div is clicked on, which triggers masonry to change the CSS and also triggers an .ajax() request to django which returns the template for the selected video. (And will perform the opposite when you select it again to return the thumbnail template and reset to normal with Masonry).
As it stands I have two javascript functions, one triggering masonry on the div, and then another triggering the ajax request on the video link;
<script>
(function($) {
$(document).ready(function() {
var container = $('.masonry'), masonry;
//masonry = new Masonry(container[0]);
container.masonry('stamp', container.find('.stamp'));
container.unbind('click').on('click', '.item', function() {
var $this = $(this),
this_link = $this.find('a'),
$this_link = $(this_link),
this_href = $this_link.attr('href'),
video_id = $this.attr('data-video-id'),
gigante = $this.hasClass('gigante'),
selector = 'div#panel-area.video_grid div.masonry div.item.' + video_id;
$.ajax({
type: "GET",
async: false,
url: this_href,
timeout: 5000,
data: {'g': gigante},
dataType: 'html',
success : function(data) {
$(selector).html(data);
container.find('.item').removeClass('gigante');
$(this).toggleClass('gigante', !gigante);
container.masonry();
console.log('Selector: ' + selector + '\nSuccess');
}
})
.done(function(msg){
console.log('Done: ' + msg);
})
.fail(function(jqXHR, textStatus){
console.log('Failed: ' + textStatus);
});
});
});
})(jQuery); </script>
And the HTML;
<div class="masonry js-masonry" data-masonry-options='{ "stamp": ".stamp", "isOriginLeft": false }'>
<div class="mpus stamp">
</div>
<div class="item video {{ object.id }}" data-video-id="{{ object.id }}">
<a class="GetYoutubeVideo" href="{% url 'show_video' video_id=object.id %}">
<i class="icon-play-circled play"></i>
<span class="title">
{{ object.get_title|slice:":20" }}...<br/>
{{ object.date_created|date:"d/m/Y" }}
</span>
{{ object.get_img_tag }}
</a>
</div>
</div>
I'm a javascript novice essentially so I'm sure this is a very basic issue. When I run this through chrome dev tools with async disabled I see the ajax request return the expected content, but then ultimately end up on the target page instead of loading the content in to $(selector) as expected.
When I enable async it just instantly fails. I've been reading docs for ages but don't feel like I'm getting anywhere. Any help would be appreciated.
To avoid the default click action, modify your click handler as follows:
.on('click', '.item', function(e) {
var ...
e.preventDefault();
$.ajax({
...
)}
...
})
Adding e to the click handler function means that the event details are available within the function - on the e object you run the preventDefault() method which prevents default actions from occuring - for instance, a hyperlink will no longer navigate to its target.
The event occurs in generally the following manner, though this is don't an in-depth summation:
A click event occurs on the a element.
The click event starts a search for an event handler. Should one not be found on the element that caused then the event will 'bubble' up the DOM tree, one level at a time until it either reaches the root DOM element and cannot go further, or a click handler is found.
At any point, should a click handler be found then the code in the click handler is executed. If the click handler sets preventDefault() on the event object, or returns false then no further action is taken.
If the click handler neither returns false nor sets preventDefault() then the original browser default action will be executed in addition to your own event handler.
Your code in full with modifications:
<script>
(function($) {
$(document).ready(function() {
var container = $('.masonry'), masonry;
//masonry = new Masonry(container[0]);
container.masonry('stamp', container.find('.stamp'));
container.unbind('click').on('click', '.item', function(e) {
var $this = $(this),
this_link = $this.find('a'),
$this_link = $(this_link),
this_href = $this_link.attr('href'),
video_id = $this.attr('data-video-id'),
gigante = $this.hasClass('gigante'),
selector = 'div#panel-area.video_grid div.masonry div.item.' + video_id;
e.preventDefault();
$.ajax({
type: "GET",
async: false,
url: this_href,
timeout: 5000,
data: {'g': gigante},
dataType: 'html',
success : function(data) {
$(selector).html(data);
container.find('.item').removeClass('gigante');
$(this).toggleClass('gigante', !gigante);
container.masonry();
console.log('Selector: ' + selector + '\nSuccess');
}
})
.done(function(msg){
console.log('Done: ' + msg);
})
.fail(function(jqXHR, textStatus){
console.log('Failed: ' + textStatus);
});
});
});
})(jQuery);
</script>
Is there a point to setting a functioning href attribute on your <a> element if you don't want it to actually go to that URL directly?
Not trying to be clever, just asking if there is a specific reason for it.
Because the problem is that your <a> element is still performing its 'normal' duties, i.e. changing the page. I can see you retrieve that URL via jQuery at a later stage, but that shouldn't matter.
Change your used attribute to something that is NOT href:
<a class="GetYoutubeVideo" href="#" data-custom-url="{% url 'show_video' video_id=object.id %}">
And when you need to retrieve the value, just use that new attribute name:
this_href = $this_link.attr('data-custom-url')
Note that I'm not 100% sure if you need the href attribute for masonry. From what I can see from your code examples, it doesn't matter, as long as you're able to retrieve the value from an attribute (not specifically href).
By NOT using the href attribute, you're making sure your <a> element doesn't have an actual URL to refer to when it gets clicked.
Is this an acceptable solution/workaround?
You could simply return false in your click event callback:
container.unbind('click').on('click', '.item', function() {
...
#ajax
...
return false;
});
I believe it's a more practical way of stopping the default action and preventing the event from bubbling up.
Related
As the title states, I wrote an ajax function that should scroll to the position the user were before getting redirected.
I wrote an alert for test scenario and it does trigger but the scroll keeps getting back to the top, whave have I done wrong here?
JavaScript:
$.ajax({
type: "GET",
url: "AdminListUsers.aspx?column=Disabled&direc=False&a=chstat&z=+",
success: function loadDoc() {
window.scrollTo(window.pageXOffset, window.pageYOffset);
}
});
C#:
var toggleUrl = "AdminListUsers.aspx?column=" + (IsClicked.FirstOrDefault().Key ?? "Name") + "&direc=" + (IsClicked.FirstOrDefault().Value) + "&a=chstat&q=" + id.ToString() + "&d=" + disabled + "&z=" + Server.UrlEncode(txtSearchFor.Text);
var hl = new HyperLink();
hl.Text = status;
hl.Style.Add(HtmlTextWriterStyle.Color, (disabled ? "red" : "green"));
hl.NavigateUrl = toggleUrl;
hl.Attributes.Add("onclick", "loadDoc();return true;");
cell.Controls.Add(hl);
tr.Cells.Add(cell);
The problem is because it's actually navigating to the link specified in the hyperlink. Then it's also trying to do the ajax request as well.
If ajax is to be used there's no need to have a navigateURL specified, and the default behaviour of the hyperlink needs to be suppressed by the script. Otherwise you'll get a full page refresh and a jQuery ajax request simultaneously. Since you've got jQuery installed you can do this most easily like this:
C#:
var hl = new HyperLink();
hl.Text = status;
hl.ID = "myLink";
hl.Style.Add(HtmlTextWriterStyle.Color, (disabled ? "red" : "green"));
hl.NavigateUrl = "#";
cell.Controls.Add(hl);
tr.Cells.Add(cell);
JS (using unobtrusive event handling):
$(document).ready(function() {
$("#<%= myLink.ClientID %>").click(function(event) {
event.preventDefault(); //stop the normal behaviour of the link
$.ajax({
type: "GET",
url: "AdminListUsers.aspx?column=Disabled&direc=False&a=chstat&z=+",
success: function() {
window.scrollTo(window.pageXOffset, window.pageYOffset);
}
});
});
});
This will stop the link from causing the whole page to be redirected, and just allow the content to be loaded via ajax.
N.B. If you are creating multiple instances of the hyperlink in a table, you would need to use classes rather than IDs to allow jQuery to locate it.
However, I would question what "AdminListUsers.aspx?column=Disabled&direc=False&a=chstat&z=+" actually returns. ormally an aspx page returns a whole HTML page including the <html>, <body> tags etc - if you put this inside another element such as a <div>, it makes your page invalid - you cannot nest <html> tags. If you want to use ajax, you should use a WebMethod (or other type of webservice) to return only the HTML that should actually be inserted into the element.
I have php page "Home.php", that present user posts(using ajax).
This is how I get the posts:
<script type="text/javascript">
function loadmore()
{
var val = document.getElementById("result_no").value;
var userval = document.getElementById("user_id").value;
$.ajax({
type: 'post',
url: 'fetch.php',
data: {
getresult:val,
getuserid:userval
},
success: function (response) {
var content = document.getElementById("result_para");
content.innerHTML = content.innerHTML+response;
// We increase the value by 2 because we limit the results by 2
document.getElementById("result_no").value = Number(val)+10;
}
});
}
</script>
<div id="content">
<div id="result_para">
</div>
</div>
In every post, there is a like button(which also uses ajax). This is how I save the likes:
<script type="text/javascript">
function likethis(likepostid)
{
$.ajax({
type: 'post',
url: 'fetchlikes.php',
data: {
getpostid:likepostid
},
success: function (response) {
}
});
}
</script>
Before I used ajax to present posts, all worked well. But now when I press the like button, it DOES save the like, BUT the javascript/jquery doesn't work. I tried to make alert when I pressed the LIKE button, but it didn't work.
This is the index.js code(the javascript). It add +1 likes, when the user press the button:
$('.btn-counter_likecount').on('click', function(event, count) {
event.preventDefault();
//alert("hello");
var $this = $(this),
count = $this.attr('data-count'),
active = $this.hasClass('active'),
multiple = $this.hasClass('multiple-count_likecount');
$.fn.noop = $.noop;
$this.attr('data-count', ! active || multiple ? ++count : --count )[multiple ? 'noop' : 'toggleClass']('active');
});
EDIT fetchlikes.php:
<?php
mysql_connect('localhost','root','');
mysql_select_db('blabla');
$postid=$_POST['getpostid'];
mysql_query("UPDATE user_post SET likes_count=likes_count+1 WHERE post_id='$postid'");
?>
Because your posts are being loaded dynamically, the javascript where you bind the event is running before the posts are actually loaded, thus the buttons don't exist when you try to bind the event. You can use delegated events in jQuery to handle this.
Your previous code
$('.btn-counter_likecount').on('click', function(event, count) {
....
});
New Code
$('#result-para').on('click','.btn-counter_likecount',function(event, count) {
....
}
This way the event will actually be bound to a parent element that already exists when jQuery's ready() function runs. This way, the event handler checks for matching elements when the event is fired rather than when the event is bound.
For further reading, look into jQuery's delegated events
I'm trying to load different views without reloading using JQUERY > AJAX.
It works fine the first time but the second time it triggers the call and loads the page.
Here is the button I'm using:
First time:
<p>Start <i class="icon arrow-right"></i></p>
Second time:
<p><a href="{{ URL::route('onboarding-ajax', ['i' => '3']) }}" id="next-step" next-step-id="3" >Next</a></p>
Here is the script:
<script>
$('#next-step').on('click', function (e) {
e.preventDefault();
var that = $(this),
url = that.attr('href'),
type = 'GET',
step_id = that.attr('next-step-id'),
width = (step_id / 32)*100 + '%';
$.ajax({
url: url,
type: type,
success: function(response) {
$('#content-wrap').html(response.view);
window.history.pushState({path:url},'',url);
return false;
}
});
});
</script>
Any idea what is being done wrong?
If your #next-step element is inside #content-wrap, it will disappear when you replace #content-wrap contents. If the new contents also have a #next-step inside it, it is a different element with the same ID, and will not have a click handler attached to it like the previous one did.
The easiest way to salvage it is to use a "live" handler - not on the #next-step, but on a parent. Try this:
$('#content-wrap').on('click', '#next-step', function (e) {
This tells #content-wrap to pay attention to a click event on a #next-step. Since you don't replace #content-wrap itself, this handler will persist, and will catch events even if #next-step is replaced.
I'm using below code. This is bootstrap 3 delete conformation message.
$(document).ready(function(){
$('a.btnDelete').on('click', function (e) {
e.preventDefault();
var id = $(this).closest('div').data('id');
$('#myModal').data('id', id).modal('show');
});
$('#btnDelteYes').click(function () {
var id = $('#myModal').data('id');
var dataString = 'id='+ id ;
$('[data-id=' + id + ']').parent().remove();
$('#myModal').modal('hide');
//ajax
$.ajax({
type: "POST",
url: "delete.php",
data: dataString,
cache: false,
success: function(html)
{
//$(".fav-count").html(html);
$("#output").html(html);
}
});
//ajax ends
});
});
This is the trigger element that I'm using
<div data-id="MYID"><a class="btnDelete" href="#">Delete</a></div>
And I'm using the same HTML element dynamically to trigger delete and it doesn't work.
Can someone point me the correct way to do it?
You have to use event delegation
$(document).on("click" , '#btnDelteYes' ,function () {
Pretty much: bind the click higher up to something that exists when the script is run, and when that something is clicked, tell it to pass the click event to the #btnDelteYes element instead
I cant understand what exactly you are doing on your code due to missing information, but the answer is: you should use event delegation on the dynamically inserted content
you can try
$('[data-id=MYID]').on('click','.btnDelteYes',function({
e.preventDefault();
var id = $(this).closest('div').data('id');
$('#myModal').data('id', id).modal('show');
});
here <div data-id="MYID"> should be a hard coded html content and The idea is to delegate the events to that wrapper, instead of binding handlers directly on the dynamic elements.
So I'm just getting started with event delegation and I'm still fairly confused by it but here goes:
I have a button which adds a rating in ajax, once clicked again I'd like it to remove the rating, here's the code with annotations (and some parts removed to make it look more clear).
$(document).on("click", '.add_rating', function() {
l.start();
var input = $(this).prev().children('.my_rating');
var score = input.val();
var what_do = input.attr('action_type');
var cur_average = $('.current_average').val();
var data = {};
data.score = score;
data.media_id = <?php echo $title_data->media_id; ?>;
data.what_do = what_do;
$.ajax({
dataType: "json",
type: 'post',
url: 'jquery/actions/add_remove_rating',
data: data,
success: function(data) {
if (data.comm === 'success') {
//do some other stuff there, irrelevant
$('.ladda-button').removeClass('btn-primary');
$('.ladda-button').removeClass('btn-sm');
$('.ladda-button').addClass('btn-danger btn-xs');
$('.ladda-label').html('Remove');
$('.ladda-button').addClass('remove_rating'); <-- add the remove rating class I want to call if the button is clicked again
input.attr('action_type', 'remove_rating');
l.stop();
}
}
});
$('.remove_rating').on('click', function() { <-- this doesn't work, why?
alert('remove was clicked');
});
});
I can't seem to trigger this:
$('.remove_rating').on('click', function() { <-- this doesn't work, why?
alert('remove was clicked');
});
Any help appreciated!
Edit: on a side note, I don't actually need this to work as php figures out if we're removing or adding a score based on the action_type attribute. I just wanted to find out why it's not triggering.
change your code to:
$(document).on("click", '.add_rating', function() {
l.start();
var input = $(this).prev().children('.my_rating');
var score = input.val();
var what_do = input.attr('action_type');
var cur_average = $('.current_average').val();
var data = {};
data.score = score;
data.media_id = <?php echo $title_data->media_id; ?>;
data.what_do = what_do;
$.ajax({
dataType: "json",
type: 'post',
url: 'jquery/actions/add_remove_rating',
data: data,
success: function(data) {
if (data.comm === 'success') {
//do some other stuff there, irrelevant
$('.ladda-button').removeClass('btn-primary');
$('.ladda-button').removeClass('btn-sm');
$('.ladda-button').addClass('btn-danger btn-xs');
$('.ladda-label').html('Remove');
$('.ladda-button').addClass('remove_rating'); <-- add the remove rating class I want to call if the button is clicked again
input.attr('action_type', 'remove_rating');
l.stop();
$('.remove_rating').on('click', function() { <-- this doesn't work, why?
alert('remove was clicked');
});
}
}
});
});
EXPLANATION:
first have a look here: Understanding Event Delegation.
event delegation is used when you need to create event handlers for elements that do not exist yet. you add a .remove_rating class to elements dynamically, however you are trying to attach a handler to elements with the above mentioned class before you even attach it.
you are attaching the class when the asynchronous ajax call returns, in the success function, however your event handler block is being processed right after you send the ajax, and not after the ajax returns (ajax is async rememeber?). therefore, you need to wait until the ajax returns and the elements are created, and only then attach the handler to them.
alternatively, using event delegation, you can attach the handler to the document, like you did in the following line:
$(document).on("click", '.add_rating', function() {
it means, that you attach the handler to the document, and whenever any element ON the document is clicked, if that element has the class '.add_rating' then execute the handler.
therefore, you may attach another handler to the document to monitor for clicks on elements with the .remove_rating class as follows:
$(document).on("click", '.remove_rating', function() {
this is called event delegation, because you delegate the event to a parent element.
Because class was added after click event initialised. You need to use live event handlers, like this:
$( document ).on('click', '.remove_rating', function() {
In this case .remove_rating click handler will work on dynamically created elements and on class name changes.