This question already has answers here:
Events triggered by dynamically generated element are not captured by event handler
(5 answers)
Closed 8 years ago.
I have a simple front-end in jQuery/HTML5 (+ backend-generated code which does not bring the issue, so I will omit it). The currently-in-use jQuery version is 1.8.3 and no version collision exists (i.e. no other jQuery version is loaded - it happened many times in other systems here).
The front-end invokes the following routines:
detailAjaxCall("\/client\/orders\/detailsLoad\/id\/3");
$(".upLink").click(function(){
console.log("subiendo");
var id = $(this).closest("tr").data('detail-id');
var url = "\/client\/orders\/detailMoveUp" + "/id/" + id;
detailAjaxCall(url);
return false;
});
$(".downLink").click(function(){
console.log("bajando");
var id = $(this).closest("tr").data('detail-id');
var url = "\/client\/orders\/detailMoveDown" + "/id/" + id;
detailAjaxCall(url);
return false;
});
$(".delLink").click(function(){
console.log("borrando");
var id = $(this).closest("tr").data('detail-id');
var url = "\/client\/orders\/detailDelete" + "/id/" + id;
detailAjaxCall(url);
return false;
});
Note: the url string are not malformed. they are generated by a json exporter (this chunk of code was extracted from the view source option in Google Chrome browser). Evaluating any of them will return a string with no backslashes.
The detailAjaxCall("/client/orders/detailsLoad/id/<number>") actually works: it returns the expected json code when I hit the url, and renders the appropiate table items:
function detailAjaxCall(url)
{
$.get(
url,
{},
function(data, status, xhr) {
//todo refrescar(data);
var table = $("#detail-list");
table.empty();
if (data.length == 0) {
$("<tr></tr>").addClass("empty").append($("<td></td>").addClass("empty").text("No hay detalles para este pedido")).appendTo(table);
} else {
$.each(data, function(index, element) {
$("<tr></tr>")
.data('detail-id', element['id'])
.append(
$("<td></td>")
.append(
$("<span></span>").addClass("product-name").text(element['producto_nombre'])
)
.append("<br />")
.append(
$("<span></span>").addClass("product-dims").text(
"Ancho: " + element['ancho'] +
", Largo: " + element['largo'] +
", Calibre: " + element['calibre']
)
)
)
.append($("<td></td>").addClass("quantity").text(element['cantidad']))
.append($("<td></td>").addClass("price").text(element['precio']))
.append(
$("<td></td>")
.append(
$("<a></a>").addClass("upLink").text("subir").attr("href", "javascript: void 0")
).append(" ")
.append(
$("<a></a>").addClass("downLink").text("bajar").attr("href", "javascript: void 0")
).append(" ")
.append(
$("<a></a>").addClass("delLink").text("eliminar").attr("href", "javascript: void 0")
).append(" ")
)
.appendTo(table);
});
}
},
'json'
).fail(function(){
$("#ajaxDetailErrorDialog").dialog("open");
});
}
Pay attention to the generation of the "<a></a>" since my problem is with them. They all have classes like delLink, upLink and downLink.
My issue starts here: calling $(".delLink").click(callback), $(".upLink").click(callback), $(".downLink").click(callback) does not seem to bind the events to the newly created items (althought they are created inside the ajax call). Seeing the source code for the click method, passing parameters, is like a call to on.
So: what am I doing wrong to bind the event dynamically, so newly created elements trigger my events as well?
You need to dynamically delegate the click handlers because you assign your click handlers before the new elements are created.
For example, delegate to the document:
$(document).on('click', '.upLink', function(){
console.log("subiendo");
var id = $(this).closest("tr").data('detail-id');
var url = "\/client\/orders\/detailMoveUp" + "/id/" + id;
detailAjaxCall(url);
return false;
});
This works because all clicks on the document will be checked by this handler, to see if they match .upLink. Even if you create new elements after this is assigned, the clicks still pass through this event.
Related
I am trying to make an MVC for training purposes and I am following a tutorial for that which is rather old. The implementation in the tutorial was made using live() but I decided to use jQuery 2.1.1 and have to implement on() method. I made a small use case for clarification.
I can insert new elements on the page while adding them in the DB
I can delete preloaded elements which existed in the DB at page load both from the DB and DOM
I can not remove elements which are added live neither from the db nor the DOM.
this is my entire code regarding that.
$(function(){
$.get('dashboard/xhrGetListings', function(o){
for (var i = 0; i < o.length; i++ )
{
$('#listInserts').append('<div>' + o[i].text + '<a class="del" rel="' + o[i].id + '" href="#">x</a></div>');
}
$('.del').on("click", function() {
delItem = $(this);
var id = $(this).attr('rel');
$.post('dashboard/xhrDeleteListing', {'id': id}, function(o) {
delItem.parent().remove(); // THIS IS NOT EXECUTED AT ALL
}, 'json');
return false;
});
}, 'json');
//Not necesarly relevant, it just helps for code clarity
$('#randomInsert').on("submit", function() {
var url = $(this).attr('action');
var data = $(this).serialize();
console.log(data);
$.post(url, data, function(o) {
$('#listInserts').append('<div>' + o.text + ' <a class="del" rel="' + o.id + '" href="#">X</a></div>');
}, 'json');
return false;
});
});
Another issue that I'm not focussing on at this point is that if I delete the parent inside the $.post method (as shown in the code above) it's not deleted, only if I move that line outside of the post method. Any clarification on that would be also very appreciated.
Use event delegation and event.preventDefault() it stops the default action
$('#listInserts').on("click", '.del' , function(event) {
event.preventDefault();
// your code come here
});
I'm totally newbie in jQuery and i wonder if it is possible to combine these two functions.
As you can see, the first function is used to load json data to trigger a click.
The second function is used to toggle view for the list items.
Could you help me, and show me the good way to combine these functions!?
When the json file is loaded, it will be create the list elements (li), and the toggle will be able to toggle these list elements (li).
IMPORTANT: actually, my code don't work (the toggle function not work fine).
Here is the code of 1st functions :
$(document).ready(function() {
// ----------------------
// JSON INFOS
// ----------------------
$(".color-list.one li:first-child").on('click', function() {
$.getJSON("result.json", function(data) {
//Handle my response
$('ul.elements-list').html(
'<li class="elements-item"><span class="tog">' + data.name + '</span><div class="togcont hidden">' + data.info + data.size + '</div></li>');
//alert(data);
});
});
});
The code of 2nd function :
$(document).ready(function() {
// ----------------------
// TOGGLE BULLZ
// ----------------------
$(".tog").click(function(){
var obj = $(this).next();
if($(obj).hasClass("hidden")){
$(obj).removeClass("hidden").slideDown();
$(this).addClass("bounce");
} else {
$(obj).addClass("hidden").slideUp();
$(this).removeClass("bounce");
}
});
});
When you use $(".tog").click() it only binds to whatever elements match the ".tog" selector at that moment so won't work on elements that you add dynamically later. You can instead use the delegated syntax of .on() like this:
$('ul.elements-list').on("click", ".tog", function(){ ...
...which will bind the click handler to your list, but only execute your function if the click occurred on an element in that list that matches the ".tog" selector in the second parameter at the time of the click. And within the handler this will be set to the ".tog" element that was clicked.
Also you can put all your code in a single document ready handler assuming all the code is in the same file.
Also your obj variable is a jQuery object, so you can call jQuery methods on it directly like obj.hasClass() rather than wrapping it in $() again as $(obj).hasClass().
So try this instead:
$(document).ready(function() {
$(".color-list.one li:first-child").on('click', function() {
$.getJSON("result.json", function(data) {
//Handle my response
$('ul.elements-list').html(
'<li class="elements-item"><span class="tog">' + data.name + '</span><div class="togcont hidden">' + data.info + data.size + '</div></li>');
});
});
$('ul.elements-list').on("click", ".tog", function(){
var obj = $(this).next();
if(obj.hasClass("hidden")){
obj.removeClass("hidden").slideDown();
$(this).addClass("bounce");
} else {
obj.addClass("hidden").slideUp();
$(this).removeClass("bounce");
}
});
});
I have a problem with displaying the id of the clicked div on the screen in an alert window. I am pretty confident this is because of the order of the controls and event handlers being added to the page, however after trying different ways I am unable to get this to work. Unfortunately I can't post reproducible code due to the div's being created from an ajax get request.
$(document).ready(function () {
$.getJSON('ClientPortal/GetSkills', function (data) {
var test = 'poo';
$.each(data, function (data) {
$('#flipContainer').append("<div class=flip id='" + this.Value + "' value='" + this.Value + "'>" + this.Text + "<//div>");
})
})
})
$(document).ready(function () {
$(".flip").on('click', function () {
alert($(this).attr("id"));
})
})
Try this :
$(document).ready(function () {
$("#flipContainer").on("click", ".flip", function () {
alert($(this).attr("id"));
})
})
You effectively have to base you click-binding on an element existing when the DOM ready event fires... But with this syntax, you delegate the binding on an existing element but it applies to another element contain in the first one...
See the jQuery documentation for on (for line "selector") : http://api.jquery.com/on/#on-events-selector-data-handlereventObject .
I am inserting elements into the DOM populated with some data I retrieved from a web service. I attached an inline click event to call a function when invoked. The problem is I am not getting a reference to the element that invoked that function.
Code that appends the new elements:
$.getJSON("/search?" + $(this).serialize(), function (data) {
if (data != null) {
$.each(data, function (index, video) {
resultItem.append("<li ><a onclick='loadNewVideo(e)' href='play?video=" + video.video_id + "'>" + "<img width='185' src='" + video.defaultImg + "'/>" + "<span class='video_left_title'>" + video.song.song_name + "<h6 class='artist_name'>" + video.artist.artist_name + "</h6></span></a>");
});
}
});
Function:
function loadNewVideo (e) {
e.preventDefault();
alert($(this).attr("href"));
}
Instead of using inline event handlers, you could delegate the clicks on all a to resultItem:
// Call this only once, when resultItem is already in the DOM
// (for example, on a document.ready callback)
resultItem.on('click', 'a', loadNewVideo);
// Proceed with your current code (slightly modified):
function loadNewVideo (e) {
e.preventDefault();
alert($(this).attr("href"));
}
$.getJSON ("/search?" + $(this).serialize(),function (data) {
if (data != null) {
$.each (data,function (index,video) {
resultItem.append("<li ><a href='play?video=" + video.video_id +"'>"
+ "<img width='185' src='"+video.defaultImg +"'/>"
+ "<span class='video_left_title'>"+ video.song.song_name
+ "<h6 class='artist_name'>"+video.artist.artist_name
+ "</h6></span></a>");
});
}
});
Inline onclick handlers don't go through jQuery, which is why you don't have access.
You can either leave those there and change the handler:
function loadNewVideo(e) {
e.preventDefault();
alert($(e.target).attr("href"));
}
Or, and more preferably, don't use the inline handlers. Just give the a elements a class of video (or whatever) and install handlers with jQuery:
...
resultItem.append("<li><a class='video' href=...'")
...
// and elsewhere
$(resultItem).on('click', 'a.video', loadNewVideo);
jQuery's event object allowed me to grab what the documentation calls the 'current DOM element within the event bubbling phase'.
var originatingElement = event.currentTarget;
I'm quite new with javascript and I don't understand this problem:
$(function() {
var $tab_title_input = $( "#tab_title"),
$tab_content_input = $( "#tab_content" );
var tab_counter = 0;
var editors = {};
var tab_current = 0;
// tabs init with a custom tab template and an "add" callback filling in the content
var $tabs = $( "#tabs").tabs({
tabTemplate: "<li><a href='#{href}'>#{label}</a> <span class='ui-icon ui-icon-close'>Remove Tab</span></li>",
add: function( event, ui ) {
var tab_content = $tab_content_input.val() || "Tab " + tab_counter + " content.";
$( ui.panel ).append("<div id=\"editor" + tab_counter + "\" class=\"editor\">" + tab_content + "</div>");
adjust_size();
tab_current = ui.index;
editors[tab_current] = ace.edit("editor" + tab_counter);
},
show: function( event, ui ) {
adjust_size();
tab_current = ui.index; // zero-based index
editors[tab_current].resize();
},
select: function( event, ui ) {
adjust_size();
tab_current = ui.index; // zero-based index
},
});
The problem is that this line of code:
editors[tab_current].resize();
breaks everything telling Uncaught TypeError: Cannot call method 'resize' of undefined.
But editors editors[tab_current].resize() is well defined in the add event and alert(tab_current) gives me the correct result.
I'd bet money that editors[tab_current] returns undefined.
Your alert(tab_current) may well return a correct value, but that doesn't mean that there's an element of editors that corresponds to it. Test it with alert(editors[tab_current]), and if it shows undefined then go check if the element is being set properly.
I can see two avenues of investigation straight away:
What does ace.edit("editor" + tab_counter) return? Does it always return an object with a resize method or does it sometimes return undefined?
Is add always called prior to show for any value of tab_current?