jquery .on() doesn't work for cloned element - javascript

It seems .on() failed to bind newly created element from clone()
<div class="container">
<div class="add">add</div>
</div>
$(".remove").on("click",function() {
$(this).remove();
});
$(".add").on("click", function() {
$(this).clone().toggleClass("add remove").text("remove").appendTo($(".container"));
});
jsfiddle
Why this doesn't work?
Update:
please note that, I know .clone() takes two parameters, but I don't want to let the cloned element continue registered to the old events, but a new one, I could use .off() and .on() to register again, but my question is why the previously declared .on(".remvove") didn't capture the change in the cloned element.

Because you need to pass a boolean parameter to clone so that all data and event handlers will be attached to cloned element.
https://api.jquery.com/clone/
$(".remove").on("click",function() {
$(this).remove();
});
$(".add").on("click", function() {
$(this).clone(true).toggleClass("add remove").text("remove").appendTo($(".container"));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="container">
<div class="add">add</div>
</div>
Edited
$(".container").on("click", '.remove', function() {
$(this).remove();
});
$(".add").on("click", function() {
$(this).clone().toggleClass("add remove").text("remove").appendTo($(".container"));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<div class="container">
<div class="add">add</div>
</div>

"my question is why the previously declared .on(".remvove") didn't capture the change in the cloned element."
Because the event listener was attached to all the elements with class names .remove which existed in the DOM at that point in time.
But with clone you created a new Element. It has just been created. A fresh node. How can your earlier listener work on this.
Solutions:
1.) Either use event delegation and attach your listener to a static ancestor node
2.) Pass the boolean flag while calling the clone function so that your listeners also get copied.
3.) Register the Event Listener Again on that node.

Unbind and bind the function.
bindEvent();
function bindEvent(){
$(".remove").unbind("click");
$(".remove").bind("click",function() {
$(this).remove();
bindEvent();
});
$(".add").unbind("click");
$(".add").bind("click", function() {
$(this).clone().toggleClass("add remove").text("remove").appendTo($("body"));
bindEvent();
});
}
jsfiddle

Related

Sortover and sortout events not triggered after element is replaced

The middle column should change width when dragging element over it. It does work until I click on replace button which replaces the html of the middle column. Then the events stop being triggered. As far as I know .on() should be able to handle such cases, or am I wrong?
This is a simple demo which demonstrates the issue: https://jsfiddle.net/shuetvyj/3/
HTML:
<div class="members-column">
<div class="sort"> 1 </div>
<div class="sort"> 1 </div>
</div>
<div id="groupB" class="members-column">
</div>
<div class="members-column">
<div class="sort"> 2 </div>
</div>
replace groupB
Javascript:
$(".members-column").sortable({
items: ".sort",
connectWith: ".members-column"
});
$("#groupB").on("sortover", function() {
console.log('overB');
$('#groupB').css('min-width','80px');
});
$("#groupB").on("sortout", function() {
console.log('outB');
$('#groupB').css('min-width','');
});
$("#replace").on("click", function(e) {
e.preventDefault();
$("#groupB").replaceWith('<div id="groupB" class="members-column"></div>');
});
Instead using replaceWith use .html as below
$("#replace").on("click", function(e) {
e.preventDefault();
$("#groupB").html("");
});
https://jsfiddle.net/shuetvyj/5/
Using replaceWith
$('body').on('sortover', '.members-column', function() {
$('#groupB').css('min-width','80px');
});
$('body').on('sortout', '.members-column', function() {
$('#groupB').css('min-width','');
});
$("#replace").on("click", function(e) {
e.preventDefault();
$("#groupB").replaceWith('<div id="groupB" class="members-column"></div>');
$(".members-column").sortable({ items: ".sort",connectWith: ".members-column" });;
});
https://jsfiddle.net/shuetvyj/8/
when you replace the html, the listeners you had attached to the old html are not reattached to the new html.
As a result, your handlers will no longer get called.
To solve this, simply attach the listeners to the new html when you have replaced it.
(jQuery solved this for a while with live instead of on, which would automatically attach listeners after dom changes) http://api.jquery.com/live/
But live() has been deprecated since jQuery 1.7 and was removed in 1.9
additionally, removing the column probably also breaks sortable's behavior (since it does not store the selector but the result of the selector when it was first called, and thus retains a reference to the div you removed from the DOM.
In short ; do not remove the column div once you have sortable listening to it, just clear or replace its contents

Event delegation after removing class

I have a button
<button data-button='next' class="disabled">next</button>
I remove the disabled class to activate but the event does not work
$("[data-button='next']:not(.disabled)").on('click', document, function(){
How can I get this working
Which is not the right syntax for event delegation, update it as follows.
// provide any selector which is parent of the dynamic element
//--\/-- and present when handler is attaching
$(document).on('click', "[data-button='next']:not(.disabled)", function(){
// --------------------^^^^^---- provide the selector of corresponding dynamic element
// code here
});
$(document).on('click', "[data-button='next']:not(.disabled)", function() {
console.log('clicked');
});
$("[data-button='next']").removeClass('disabled')
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button data-button='next' class="disabled">next</button>

Jquery one() function fires twice on click

My Jquery one() function dies after second click instead of first click. Here is my HTML
<div class="box">
<div class="call" data-tai="5">CLICK</div>
</div>
and heres my Jquery
$('body div').one('click', '.call', function() {
var mother = $(this).parent();
if(mother.css('position') === 'static')
mother.css('position', 'relative');
var tai = $(this).data('tai');
$.ajax({
method: 'GET',
url: '/bootstrap/call.php',
data: 'tai='+tai,
dataType: 'html',
success: function(ret) {
mother.append(ret);
},
});
return false;
});
Interesting thing is, if i don't use return false;, it dies after first click. However bubbling occurs and it appends 2 html tags instead of 1, inside box element. Thanks for help
$('body div')
would select both the divs and attach click handlers to both of them. When you click on the nested div then, both clicks will be fired. Use a specific selector to avoid this.
$('.call')
could perhaps achieve this.
That's because event handlers bound by using .one will be fired once for each element in the jQuery collection. Since the return false stops the propagation of the event, if you click on the .call element, click handler of the parent element is not executed but the parent element still has an active click handler. You should use a more specific selector for selecting the target element. If the click handler should be bound to the div.call elements:
$('.box div.call').one(...);
Now, if .box elements have 9 div.call descendants then you have 9 click handlers! After clicking on each element jQuery unbinds the handler for that specific element.
It's not once for all elements, it's once for each element.
If the handler should be called once for all the matching elements you can use the delegation version of the .one method:
$(document).one('click', '.box div.call', function() {
// ...
});
And if you want to delegate the event and have the handler working once for dynamically generated elements you can use the .on method and :not selector:
$(document).on('click', '.box .call:not(.clicked)', function() {
$(this).addClass('clicked');
// ...
});
Now the handler is called once for each .call element. Since :not excludes the elements that have .clicked class the selector doesn't match the already-clicked elements.
Events bubble in JavaScript. Your code
$('body div').one('click', '.call', function() {
}
wires up on both
<div class="box"> <!-- This -->
<div class="call" data-tai="5">CLICK</div> <!-- And this -->
</div>
You need a more specific selector. If this div is a parent element in the body like this:
<body>
<div class="box">
<div class="call" data-tai="5">CLICK</div>
</div>
<div class="box">
<div class="call" data-tai="5">CLICK</div>
</div>
</body>
then you can use a selector like this:
$('body > div').one('click', '.call', function() {
}
The question is - where do you expect your click event to be placed? Perhaps the div with the box class?
$('div.box').one('click', '.call', function() {
}
This assumes that the .call divs are being added dynamically to the .box div.
P.S. - if you want to stop the bubbling, I suggest you pass in the event object to your click event and call stopPropagation()
$('div.box').one('click', '.call', function(evt) {
evt.stopPropagation(); // no bubbling
}

How to access value attributes of dynamically created <select> tag dropdown, using jQuery?

Here's the jsfiddle link to a small mockup of what I am trying to do http://jsfiddle.net/dscLc/8/
Javascipt code
newc=function(){
$('#div').html('<center><select id="resClass"><option value="" selected>FIRST</option></select><center>');
for(var i=0;i<10;i+=1)
{
$('#resClass').append('<option value="'+i+'">'+i+'</option>');
}
}
$(document).ready(function(){
$("#resClass").change(function(){
alert($(this).val());
});
});
HTML code goes here
<div id="div">
<center>
<button type="button" onclick="newc()">Compare</button>
</center>
</div>
Also this seems that you have a static parent with an id #div so you can delegate to this element. Although $(document) && $(document.body) are always available to event delegation, but this is very slow in terms of performance.
Better to delegate the event to the closest static parent, where dom lookup time is low and performance is fast.
change this:
$("#resClass").change(function(){
to this:
$("#div").on('change', "#resClass", function(){
This is a case of event delegation.
What this means if any element is dynamically generated then you can't bind an event to that element the way you are currently trying to bind, because when dom was ready this element was not available at that point of time.
As you are adding the select dynamically, You need event delegation:
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.
$("#div").on('change','#resClass',function(){
alert($(this).val());
});
Working Demo
Working demo: http://jsfiddle.net/Lcr5t/
API: .on - http://api.jquery.com/on/
Rest should fit the need :)
Code
newc=function()
{
$('#div').html('<center><select id="resClass"><option value="" selected>FIRST</option></select><center>');
for(var i=0;i<10;i+=1)
{
$('#resClass').append('<option value="'+i+'">'+i+'</option>');
}
}
$(document).ready(function(){
$(document).on('change',"#resClass",function(){
alert($(this).val());
});
});
You event hookup needs to use 'on' to apply it to any controls that are subsequently created after it has been registered:
$(document).ready(function(){
$(document).on("change", "#resClass", function(){
alert($(this).val());
});
});
Your must to use delegated events like as .on, .bind, .live
There is a small problem in your code.
First, you have not called the function newc().
Secondly, since the select element is dynamically added, change() is not triggered.
Please check the following code:
newc=function(){
$('#div').html('<center><select id="resClass"><option value="" selected>FIRST</option></select><center>');
for(var i=0;i<10;i+=1) {
$('#resClass').append('<option value="'+i+'">'+i+'</option>');
}
}
newc();
$(document).on('change','#resClass',function(){
alert($(this).val());
});
FIDDLE

How to select all the elements in the DOM except one element?

I have an jQuery events) that are triggering on every DOM element on the page.
That's fine for now, but I need a way to exclude a div (with child divs as well) from DOM selection in order to prevent triggering event on them.
I've tried jQuery's unbind(), not() and off() directly on $('#myDiv'), but the events are still triggering.
The code that triggers the event(s):
$("*").each(function () {
// here are events bound to every DOM in the page
$(this).mouseover(mouseover).mouseout(mouseout).click(click);
});
I've also tried with
$("*:not(#myDiv)").each(function () {
// events
$(this).mouseover(mouseover).mouseout(mouseout).click(click);
});
and still wasn't able to remove events from #myDiv.
The HTML:
<div id="myDiv">
<div class="data">
<!-- -->
</div>
<div class="debug">
<!-- -->
</div>
<div class="form">
<!-- -->
</div>
</div>
What is the best way to make full DOM selection but excluding #myDiv, so I would be able to use bind() or on() for binding events?
NOTE: I don't want to remove #myDiv from the DOM tree, but rather exclude it from selection.
You may try this
$(function(){
$('body > *').not('#myDiv')
.on('mouseover', function(){ //... })
.on('click', function(){ //... });
});
An Example.
Try .not()
$("*").not("#myDiv").each(function () {
$(this).mouseover(mouseover).mouseout(mouseout).click(click);
});
Try the following:
$("*:not(#myDiv):not(#myDiv *)")
.mouseover(mouseover)
.mouseout(mouseout)
.click(click)
This will exclude not only #myDiv, but any child elements of #myDiv (which you seem to be going for).
If you want to exclude the children as well, you'll need to do so explicitly:
$("*:not(#myDiv):not(#myDiv *)")

Categories

Resources