Jquery Clone() Not Cloning Event Bindings, Even with On()

jQuery clone() not cloning event bindings, even with on()

I think you should use this overload of the .clone() method:

$element.clone(true, true);

clone( [withDataAndEvents] [, deepWithDataAndEvents] )

withDataAndEvents: A Boolean indicating whether event handlers and data should be copied along with the elements. The default value is false.

deepWithDataAndEvents: A Boolean indicating whether event handlers and data for all children of the cloned element should be copied. By default its value matches the first argument's value (which defaults to false).

Beware that .on() does not actually bind the events to the targets but to the element you are delegating to. So if you have:

$('#container').on('click', '.button', ...);

The events are actually binded to #container. When a click on a .button element occurs, it bubbles up to the #container element The element which triggered the event is evaluated upon the selector parameter of .on() and if it matches, the event handler is executed. This is how event delegation works.

If you clone the element #container, you have to deep clone with events and data for the bindings made with .on() to be preserved.

This would not be necessary if you were using .on() on a parent of #container.

jQuery's .clone(true, true) not cloning event bindings on children

There are two problems with your code:

  1. The original clone ($cloned) does not have an event listener bound to the button
  2. Your second invocation of clone does not preserve events

You need to clone the container after you have attached the event listener to the button. Additionally, you need to pass the true arguments to the second invocation of clone:

var $addButton = $(".btn.add");
$addButton.on("click", function(e) {
var $cloner = $(this).closest(".clone");
$cloned.clone(true, true).insertAfter($cloner);
var $cloned = $('.clone').clone(true, true);

Here is a demonstration: http://jsfiddle.net/3hjfj/

JQuery: Elements made with clone() not taking event properties of original

Use .on():

$(document).on('change', '.fourthboxes', function() {

This makes your event handler work for current elements, but also future added elements, that match the .fourthboxes selector. This uses the principle of delegated events.

From the documentation:

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.

Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time.

Events not working while cloning by using .clone(true)

If you invert .replaceWith() with .clone() it'll work:

$('button').on('click', function(){
$(this).replaceWith('<p>'+ $(this).text() +'</p>');

The .replaceWith() will clear all the events bound to the element (as discussed here). When you call .clone() after that, there's no events to retain.

My guess is that .clone() retains the base html even before the call to it (like in a internal variable) and .clone(true) retrieves that value and then bind the events.

Jquery clone div breaking events

Easiest answer; the clone isn't breaking the event. The event isn't firing because it's never assigned to the new element. What you're doing when you clone the element is creating a dynamic element. This means it's added to the DOM after the page has loaded, at which point, your event was already delegated.

The way you're delegating the event is not wrong. It's simply not the best practice when you will have dynamic elements. There are a few ways you can handle this.

Using .clone()'s withDataAndEvents parameter.

The withDataAndEvents parameter of .clone() is intended to make this process easier by copying data and events to the new element, as such

var copy = $firstTRCopy.clone(true)

However, this can have a few nasty side-effects. For instance, it's been known to clone id's and if your event is assigned to an id, you'll still only get fire on the first element.

Event Delegation - See Understanding Event Delegation

This is a method by which we use a parent element, or even the document root Object to attach events that will always be fired on all children. Such as:

$(document).on('click', '.addRow', function(e) {...})

Due to convenience more than anything, it's often not recommended you attach to the document root. I had first suggested it because I was in public and it was a quick and easy answer. It's not unsafe to attach to the document root, but can become an issue if you have a lot going on in your page. See, each element an event is assigned to will bubble up when the event is fired. Simply put, when you attach 'click' to an element with an event delegated via the document, you're essentially clicking the entire DOM. More often than not, it's not really an issue and I make use of it all day long for convenience of readability.

However, the more proper way would be to look for the closest parent element that exist on load. That is to say, it is not created dynamically. Simply put, like so:

    $('#parentAlreadyLoaded').on('click', '.addRow', function(e) {...})

Note: Assigning an event to .addRow using $(document) or $('parent element selector') does not change the event parameter in function(e) {. The e in $('.addRow').click is the same as the e in $(document OR 'parent').on('click', '.addRow'

Short Example

function addRow(e) {    if (console && console['log']) console.log(e); var par = $('#addRows'),  tr = $('<tr />').appendTo(par),  tdBlue = $('<td />').appendTo(tr),  tdRed = $('<td />').appendTo(tr),  btnBlue = $('<button />', { 'class': 'make-blue', 'text': 'Make Blue' }).appendTo(tdBlue),  btnRed = $('<button />', { 'class': 'make-red', 'text': 'Make Red' }).appendTo(tdRed)}
$(function() { $('thead button').on('click', addRow); $('#addRows').on('click', '.make-blue', function(e) { if (console && console['log']) console.log(e); $(this).closest('tr').removeClass('red').addClass('blue'); }); $(document).on('click', '.make-red', function(e) { if (console && console['log']) console.log(e); $(this).closest('tr').removeClass('blue').addClass('red'); });})
table { border-collapse: collapse; margin: 0 auto; }tr { padding: .25em .5em; text-align: center; }th, td { border: 3px groove; }th button { margin: .25em auto; }td button { margin: .5em; }.blue { background-color: #00F; }.red { background-color: #F00; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script><table>    <thead>        <th colspan="2">            <button>Click Me</button>        </th>    </thead>    <tbody id="addRows"></tbody></table>

Events on cloned elements?

Since you're not cloning the button itself, but a parent, you need to do a deep clone:

this.fileTemplate = $('.file:first').clone(true,true);


However, if you're removing the element anyway, you don't need to clone it at all -- just store the div with all of its events by using .detach() instead of .remove():

this.fileTemplate = $('.file:first').detach();


To add copies of that element, deep-clone it after it's detached:

clone_copy = this.fileTemplate.clone(true,true);

Related Topics

Leave a reply