Why does my jQuery delegated click fire multiple times? - javascript

I am copying an element and adding it to a a list of elements. First, I get some HTML using an ajax call:
var setButtonClick = function (url, btn) {
$.ajax(url, $('form').serialize(), 'Html').then(function (data) {
$(btn).parent().find(sel.addListItem).on('click', function () {
addListItem(data, this, btn);
});
addListItem(data, btn, btn);
});
}
addListItem looks like this:
var addListItem = function (data, context, btn) {
var $template = $(data);
// stuff not related removed for brevity
$(btn).before($template);
}
I then have a remove function using a delegate:
$(sel.editableList).delegate(sel.removeListItem, 'click', function () {
// fires once for every element with the sel.removeListItem selector
}
I need the click event to fire once for the clicked element only. I can get a basic version of delegate working by inserting content like this:
$( "body" ).delegate( "p", "click", function() {
$( this ).after( "<p>Another paragraph!</p>" );
});
Therefore, I'm thinking it may be because I'm inserting a copy of the element or is it the same element I'm adding over and over? I've also tried to use clone to create a new element before inserting like:
var $template = $(data).clone();
Can anyone show me where I am going wrong with this please?

The problem is that every time your ajax is called you attach a click event handler to the elements. It gets called repeatedly, because you add it to the elements that already existed and had this handler attached.
The solution for your problem is to detach previously attached handlers with off() function.
var setButtonClick = function (url, btn) {
$.ajax(url, $('form').serialize(), 'Html').then(function (data) {
$(btn).parent().find(sel.addListItem)
.off('click')
.on('click', function () {
addListItem(data, this, btn);
});
addListItem(data, btn, btn);
});
}
#
In the future you may want to attach different click event handlers or may want to turn off specific handlers, for that you could use namespaces.
$(elem).on('event.namespace', function(){});
$(elem).off('event.namespace');
That way you could have multiple click event handlers on one element. This would be the code if you have more than one click event handlers
var setButtonClick = function (url, btn) {
$.ajax(url, $('form').serialize(), 'Html').then(function (data) {
$(btn).parent().find(sel.addListItem)
.off('click.addItem')
.on('click.addItem', function () {
addListItem(data, this, btn);
});
addListItem(data, btn, btn);
});
}
#
And here's the example.
$('.btn').on('click.ns1', function(){
alert('Hey');
});
$('.btn').on('click.ns2', function(){
alert('How you doin?');
});
// Comment me out to see the difference
$('.btn').off('click.ns2');
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button class="btn">Click me</button>

Related

How to add 2 onclick event methods for a single element which class has dynamically change on the first click event method on jquery

On this click event, it changes the clicked elements class from 'fa-pencil-square-o' to 'fa-floppy-o'.
$("i.fa.fa-pencil-square-o").click(function () {
var parentid = $(this).parent().attr('id');
var parent = $('tr#' + parentid);
var rtn;
var rtd;
if (parent.find('td:nth-child(4)').text() == 'No') {
rtn = parent.find('td:nth-child(2)').text();
rtd = parent.find('td:nth-child(3)').text();
parent.find('td:nth-child(2)').empty();
parent.find('td:nth-child(3)').empty();
//changes class here
parent.find('td:nth-child(5)').empty();
parent.find('td:nth-child(5)').append('<i class="fa fa-floppy-o" aria-hidden="true"></i>');
parent.find('td:nth-child(2)').append('<input type="text" id="rtName" />');
parent.find('td:nth-child(3)').append('<input type="text" id="rtDescription" />');
$(parent).find('input#rtName').val(rtn);
$(parent).find('input#rtDescription').val(rtd);
}
});
When the class of the same clicked element changes to 'fa-floppy-o' I wanted this click event to take effect but it doesn't and still invokes the first click event above.
$('.fa-floppy-o').click(function () {
var parent = $(this).parent();
var rtn = parent.find('input#rtName');
var rtd = parent.find('input#rtDescription');
$.ajax({
url: '/editRoomType',
async: false,
type: 'POST',
data: {
roomTypeID: parent.attr('id'),
roomTypeName: rtn.val(),
roomTypeDescription: rtd.val()
},
dataType: 'json',
success: function (data) {
if (data.isSuccessful) {
$('#msgdiv').css("display", "inline");
$('#msg').empty();
$('#msgdiv').removeClass('alert-danger');
$('#msgdiv').addClass('alert-success');
$.each(data.Message, function (key, value) {
$('#msg').append(value + '<br />')
})
$('.table-details tbody').empty();
parent.find('td:nth-child(5)').empty();
parent.find('td:nth-child(5)').append('<i class="fa fa-pencil-square-o" aria-hidden="true"></i>');
getRoomType();
} else {
$('#msgdiv').css("display", "inline");
$('#msg').empty();
$('#msgdiv').removeClass('alert-success');
$('#msgdiv').addClass('alert-danger');
$.each(data.Message, function (key, value) {
$('#msg').append(value + '<br />')
})
}
},
error: function () {
alert('error accounts')
}
});
});
What I basically want to accomplish is for the elements' on click event function to change when the class of the element changes. But how?
You are adding the listener directly to the element with the provided class, instead add the listener to a parent element using jQuery's on() event delegation, and provide the class of the clicked element as the second parameter. Even document works.
This is the definition of the jQuery on() event handler:
.on( events [, selector ] [, data ], handler )
Change this line:
$("i.fa.fa-pencil-square-o").click(function () {
to this:
$(document).on("click", "i.fa.fa-pencil-square-o", function() {
And this line:
$('.fa-floppy-o').click(function () {
to this:
$(document).on("click", ".fa-floppy-o", function() {
The reason being: jQuery adds those listeners once when the page loads. Because it doesn't find anything with the class .fa-floppy-o, it doesn't add a listener. This is true of renaming (re-classing) elements, and also elements dynamically generated by jQuery/Javascript after page load.
The event will bubble up from the element to the parent element and that parent element will check to see if the element has the selector provided in the second argument.
More on event bubbling:
Direct and delegated events
The majority of browser events bubble, or propagate, from the deepest,
innermost element (the event target) in the document where they occur
all the way up to the body and the document element.
...
When a selector is provided, the event handler is referred to as
delegated. The handler is not called when the event occurs directly on
the bound element, but only for descendants (inner elements) that
match the selector. jQuery bubbles the event from the event target up
to the element where the handler is attached (i.e., innermost to
outermost element) and runs the handler for any elements along that
path matching the selector.
Here's documentation for the on() function in jQuery: http://api.jquery.com/on/

Trigger AJAX inside an AJAX loaded page [duplicate]

I have a link, myLink, that should insert AJAX-loaded content into a div (appendedContainer) of my HTML page. The problem is that the click event I have bound with jQuery is not being executed on the newly loaded content which is inserted into the appendedContainer. The click event is bound on DOM elements that are not loaded with my AJAX function.
What do I have to change, such that the event will be bound?
My HTML:
<a class="LoadFromAjax" href="someurl">Load Ajax</a>
<div class="appendedContainer"></div>
My JavaScript:
$(".LoadFromAjax").on("click", function(event) {
event.preventDefault();
var url = $(this).attr("href"),
appendedContainer = $(".appendedContainer");
$.ajax({
url: url,
type : 'get',
complete : function( qXHR, textStatus ) {
if (textStatus === 'success') {
var data = qXHR.responseText
appendedContainer.hide();
appendedContainer.append(data);
appendedContainer.fadeIn();
}
}
});
});
$(".mylink").on("click", function(event) { alert("new link clicked!");});
The content to be loaded:
<div>some content</div>
<a class="mylink" href="otherurl">Link</a>
Use event delegation for dynamically created elements:
$(document).on("click", '.mylink', function(event) {
alert("new link clicked!");
});
This does actually work, here's an example where I appended an anchor with the class .mylink instead of data - http://jsfiddle.net/EFjzG/
If the content is appended after .on() is called, you'll need to create a delegated event on a parent element of the loaded content. This is because event handlers are bound when .on() is called (i.e. usually on page load). If the element doesn't exist when .on() is called, the event will not be bound to it!
Because events propagate up through the DOM, we can solve this by creating a delegated event on a parent element (.parent-element in the example below) that we know exists when the page loads. Here's how:
$('.parent-element').on('click', '.mylink', function(){
alert ("new link clicked!");
})
Some more reading on the subject:
https://learn.jquery.com/events/event-delegation/
http://jqfundamentals.com/chapter/events
if your question is "how to bind events on ajax loaded content" you can do like this :
$("img.lazy").lazyload({
effect : "fadeIn",
event: "scrollstop",
skip_invisible : true
}).removeClass('lazy');
// lazy load to DOMNodeInserted event
$(document).bind('DOMNodeInserted', function(e) {
$("img.lazy").lazyload({
effect : "fadeIn",
event: "scrollstop",
skip_invisible : true
}).removeClass('lazy');
});
so you don't need to place your configuration to every you ajax code
As of jQuery 1.7, the .live() method is deprecated. Use .on() to attach event handlers.
Example -
$( document ).on( events, selector, data, handler );
For those who are still looking for a solution , the best way of doing it is to bind the event on the document itself and not to bind with the event "on ready"
For e.g :
$(function ajaxform_reload() {
$(document).on("submit", ".ajax_forms", function (e) {
e.preventDefault();
var url = $(this).attr('action');
$.ajax({
type: 'post',
url: url,
data: $(this).serialize(),
success: function (data) {
// DO WHAT YOU WANT WITH THE RESPONSE
}
});
});
});
If your ajax response are containing html form inputs for instance, than this would be great:
$(document).on("change", 'input[type=radio][name=fieldLoadedFromAjax]', function(event) {
if (this.value == 'Yes') {
// do something here
} else if (this.value == 'No') {
// do something else here.
} else {
console.log('The new input field from an ajax response has this value: '+ this.value);
}
});
use jQuery.live() instead . Documentation here
e.g
$("mylink").live("click", function(event) { alert("new link clicked!");});
For ASP.NET try this:
<script type="text/javascript">
Sys.Application.add_load(function() { ... });
</script>
This appears to work on page load and on update panel load
Please find the full discussion here.
Important step for Event binding on Ajax loading content...
01. First of all unbind or off the event on selector
$(".SELECTOR").off();
02. Add event listener on document level
$(document).on("EVENT", '.SELECTOR', function(event) {
console.log("Selector event occurred");
});
Here is my preferred method:
// bind button click to function after button is AJAX loaded
$('#my_button_id').bind('click', function() {
my_function(this);
});
function my_function () {
// do stuff here on click
}
I place this code right after the AJAX call is complete.
I would add one point that was NOT obvious to me as a JS newb - typically your events would be wired within document, e.g.:
$(function() {
$("#entcont_table tr td").click(function (event) {
var pk = $(this).closest("tr").children("td").first().text();
update_contracts_details(pk);
});
}
With event delegation however you'd want:
$(function() {
// other events
}
$("#entcont_table").on("click","tr td", function (event) {
var pk = $(this).closest("tr").children("td").first().text();
update_contracts_details(pk);
});
If your event delegation is done within the document ready, you'll an error of the like:
cant assign guid on th not an boject

a href click not triggering in jquery

$(function() {
$.getJSON("companies.json", function(response) {
var html = '<table id="tbl">';
response.businesses.forEach(function(row) {
html += '<tr><td>' + row.id + '</td><td>' + row.name;
});
html += '</table>';
$("#tabledata").html(html);
});
$(".move").click(function() {
var $id = $(this).attr("idname");
$.getJSON("companies.json", function(response) {
$.map(response.businesses, function(obj) {
if (obj.id == $id)
console.log(obj);
return obj; // or return obj.name, whatever.
});
});
});
});
HTML:
<div id="tabledata" class='left'></div>
<div class="right"></div>
Please help?
As your .move element is added to your page dynamically, you have to make use of jQuery's on() method to delegate the event to an ancestor of the .move element which does exist when your JavaScript first loads.
$(document).on('click', '.move', function() { ... });
Event delegation allows us to attach a single event listener, to a parent element, that will fire for all descendants matching a selector, whether those descendants exist now or are added in the future.
You can read more about jQuery's event delegation here.
If you use event delegation, your problem goes away (and your app becomes more performant and less prone to memory leaks).
// Only do this once, when your page loads...
$(document.body).on('click', '.move', function (ev) {
// This is the link that was clicked.
var $targ = $(ev.target);
});
Try This
$('#tabledata').on('click', '.move', function(e) { ... });
The reason the event isn't being triggered is because the event is only added to elements that exist on the page when you call the .click() method.
Instead, you can use event delegation:
$(document.body).on('click', '.move', function (ev) {
var $targ = $(ev.target);
});
which really says: call the function when any element that matches .move that's inside document.body is clicked.
I know others have said this already but I wanted to make event delegation clearer.

jquery: Callback not received on clicking of button

I need to send a request on click of button but callback is not received on firing of click event of the button.
Following is code snippet:
$(document).ready(function () {
var counter = 0;
$("#trail").click(function () {
$("#dialog").dialog();
if (counter < 1) {
$("#searchboxdiv").after('<input type="text" id="searchbox">');
$("#searchbox").after('<input type="button" id="searchbutton" value="search">');
counter++;
}
});
$("#searchbutton").click(function () {
var dataToSend = null;
$.ajax({
data: dataToSend,
url: "FormHandler",
success: function (result) {},
beforeSend: function () {
dataToSend = $("#searchbox").val();
}
});
});
$("#searchboxdiv").on('click', "#searchbutton", function(){
var data = null;
});
});
I added the textbox in the dialog box dynamically and on click of button in dialog box, callback is not received
Your options:
Use event delegation. Bind the click to immediate static parent like this :
$("#searchboxdiv").on('click', "#searchbutton", function(){ });
Or, bind it to the document.
$(document).on('click', "#searchbutton", function(){ });
Or, move the existing click after counter++;, ie., inside $("#trail")'s click handler.
For more info, see on()
Use event delegation (for dynamically added #searchbutton)
$('#searchboxdiv').on('click',"#searchbutton",function(){
http://learn.jquery.com/events/event-delegation/

How to bind Events on Ajax loaded Content?

I have a link, myLink, that should insert AJAX-loaded content into a div (appendedContainer) of my HTML page. The problem is that the click event I have bound with jQuery is not being executed on the newly loaded content which is inserted into the appendedContainer. The click event is bound on DOM elements that are not loaded with my AJAX function.
What do I have to change, such that the event will be bound?
My HTML:
<a class="LoadFromAjax" href="someurl">Load Ajax</a>
<div class="appendedContainer"></div>
My JavaScript:
$(".LoadFromAjax").on("click", function(event) {
event.preventDefault();
var url = $(this).attr("href"),
appendedContainer = $(".appendedContainer");
$.ajax({
url: url,
type : 'get',
complete : function( qXHR, textStatus ) {
if (textStatus === 'success') {
var data = qXHR.responseText
appendedContainer.hide();
appendedContainer.append(data);
appendedContainer.fadeIn();
}
}
});
});
$(".mylink").on("click", function(event) { alert("new link clicked!");});
The content to be loaded:
<div>some content</div>
<a class="mylink" href="otherurl">Link</a>
Use event delegation for dynamically created elements:
$(document).on("click", '.mylink', function(event) {
alert("new link clicked!");
});
This does actually work, here's an example where I appended an anchor with the class .mylink instead of data - http://jsfiddle.net/EFjzG/
If the content is appended after .on() is called, you'll need to create a delegated event on a parent element of the loaded content. This is because event handlers are bound when .on() is called (i.e. usually on page load). If the element doesn't exist when .on() is called, the event will not be bound to it!
Because events propagate up through the DOM, we can solve this by creating a delegated event on a parent element (.parent-element in the example below) that we know exists when the page loads. Here's how:
$('.parent-element').on('click', '.mylink', function(){
alert ("new link clicked!");
})
Some more reading on the subject:
https://learn.jquery.com/events/event-delegation/
http://jqfundamentals.com/chapter/events
if your question is "how to bind events on ajax loaded content" you can do like this :
$("img.lazy").lazyload({
effect : "fadeIn",
event: "scrollstop",
skip_invisible : true
}).removeClass('lazy');
// lazy load to DOMNodeInserted event
$(document).bind('DOMNodeInserted', function(e) {
$("img.lazy").lazyload({
effect : "fadeIn",
event: "scrollstop",
skip_invisible : true
}).removeClass('lazy');
});
so you don't need to place your configuration to every you ajax code
As of jQuery 1.7, the .live() method is deprecated. Use .on() to attach event handlers.
Example -
$( document ).on( events, selector, data, handler );
For those who are still looking for a solution , the best way of doing it is to bind the event on the document itself and not to bind with the event "on ready"
For e.g :
$(function ajaxform_reload() {
$(document).on("submit", ".ajax_forms", function (e) {
e.preventDefault();
var url = $(this).attr('action');
$.ajax({
type: 'post',
url: url,
data: $(this).serialize(),
success: function (data) {
// DO WHAT YOU WANT WITH THE RESPONSE
}
});
});
});
If your ajax response are containing html form inputs for instance, than this would be great:
$(document).on("change", 'input[type=radio][name=fieldLoadedFromAjax]', function(event) {
if (this.value == 'Yes') {
// do something here
} else if (this.value == 'No') {
// do something else here.
} else {
console.log('The new input field from an ajax response has this value: '+ this.value);
}
});
use jQuery.live() instead . Documentation here
e.g
$("mylink").live("click", function(event) { alert("new link clicked!");});
For ASP.NET try this:
<script type="text/javascript">
Sys.Application.add_load(function() { ... });
</script>
This appears to work on page load and on update panel load
Please find the full discussion here.
Important step for Event binding on Ajax loading content...
01. First of all unbind or off the event on selector
$(".SELECTOR").off();
02. Add event listener on document level
$(document).on("EVENT", '.SELECTOR', function(event) {
console.log("Selector event occurred");
});
Here is my preferred method:
// bind button click to function after button is AJAX loaded
$('#my_button_id').bind('click', function() {
my_function(this);
});
function my_function () {
// do stuff here on click
}
I place this code right after the AJAX call is complete.
I would add one point that was NOT obvious to me as a JS newb - typically your events would be wired within document, e.g.:
$(function() {
$("#entcont_table tr td").click(function (event) {
var pk = $(this).closest("tr").children("td").first().text();
update_contracts_details(pk);
});
}
With event delegation however you'd want:
$(function() {
// other events
}
$("#entcont_table").on("click","tr td", function (event) {
var pk = $(this).closest("tr").children("td").first().text();
update_contracts_details(pk);
});
If your event delegation is done within the document ready, you'll an error of the like:
cant assign guid on th not an boject

Categories