user3690595
user3690595

Reputation: 41

Cloning a jQuery object with events bound to the new object

I'm an experienced programmer but new to JQuery. I'm trying to clone a data-role="collapsible" object and wish to do so with the event bindings that the previous object had. My code is below:

$(document).ready(function() {
    var appointment = $("#appointment").clone(true).attr("id", "appointment1");
    $("#appointment-list").append(appointment);
});

The problem is that this copies the event bindings exactly, which means that when you click on the cloned object, the original is what is expanded. How do you clone it so that the events trigger actions on the clone?

EDIT:

That's the only code I've written as I'm doing it as a test before I complete my app. The rest taken from the JQuery mobile library. The element being cloned is a collapsible content block that I'm using as a template for dynamically added blocks. A stripped down version of it is below:

<div data-role="collapsible" id="appointment">
  <h1>Click me - I'm collapsible!</h1>
  <p>I'm the expanded content.</p>
</div>

EDIT:

AlexKM, NEVER take it upon yourself to correct the spelling and grammar of a native English speaker, which you are clearly not. Where do you get off completely butchering someone else's post into barely intelligible drivel? I have reverted your changes.

Upvotes: 2

Views: 486

Answers (2)

Andreas Louv
Andreas Louv

Reputation: 47099

I'm not sure you understand how jQuery.fn.clone works, consider this:

var $d1 = $('<div/>').prop('id', 'd1').on('click', function() { 
    alert('d1 clicked? The id is: ' + this.id); 
});
var $d2 = $d1.clone(true).prop('id', 'd2').click(); // alerts d1 clicked? The is is: d2

What im trying to show it that jQuery triggers the event on the cloned object, but the event handler is shared between the two elements.

Btw as of jQuery 1.7 - you should use jQuery.fn.prop instead of jQuery.fn.attr

Upvotes: 0

iCollect.it Ltd
iCollect.it Ltd

Reputation: 93571

The question has changed completely based on new information and comments, so will add an appropriate answer here instead (my other answer is now deleted).

The specific problem is that you wish to clone jQuery UI collapsible objects already on the page.

Unfortunately jQuery UI attaches to existing collapsible objects (decorated with data-role="collapsible) at document load time, in doing this it also creates a dynamic structure on each item to manage the open and closed states.

It also stores state data referencing the new pieces of the collapsible element. This means that when it is clicked it will reference the original child elements of the cloned item (not the clone).

A clone has more elements than you expect, so you cannot just reapply collapsible() to them.

  1. If you deep clone a collapsible element, it incorrectly references the original element's components and toggles that one instead of itself.
  2. If you shallow clone a collapsible element you get additional structure that needs to be undone/removed before you can apply collapsible(). otherwise you get this result:

enter image description here

So the options are:

  • Find out how to attach a new code instance without changing the structure (hard)
  • Undo the structure changes and reapply collapsible (easier)

To do the second of these, first we look at what collapsible does to elements:

Element before collapsible:

<div class="cloneme" data-role="collapsible">
      <h1>Click me - I'm collapsible!</h1>
      <p>I'm the expanded content.</p>
</div>

Element after collapsible:

<div class="cloneme ui-collapsible ui-collapsible-inset ui-corner-all ui-collapsible-themed-content ui-collapsible-collapsed" data-role="collapsible">
    <h1 class="ui-collapsible-heading ui-collapsible-heading-collapsed">
         <a href="#" class="ui-collapsible-heading-toggle ui-btn ui-icon-plus ui-btn-icon-left ui-btn-inherit">Click me - I'm collapsible!<span class="ui-collapsible-heading-status"> click to expand contents</span>
          </a>
    </h1>
    <div class="ui-collapsible-content ui-body-inherit ui-collapsible-content-collapsed" aria-hidden="true">
        <p>I'm the expanded content.</p>
    </div>
</div>

Answer "undo and reapply":

The code to do all this is:

JSFiddle: http://jsfiddle.net/TrueBlueAussie/yMcqB/2/

$(function () {
    var clone = $(".cloneme").clone();
    $('.ui-content').append(clone);
    clone.addClass('ImAClone');        // Just for testing - REMOVE THIS
    clone.find('.ui-collapsible-heading-toggle').children().unwrap();
    clone.find('.ui-collapsible-heading-status').remove();
    clone.find('.ui-collapsible-content').children().unwrap();
    clone.collapsible();
});

You can probably combine some of the operations into a single one (e.g. the unwraps), but I leave that for you to do :)

Thoughts: Ideally these plugins would allow for applying to an existing element that already has collapsible decorations.

The end result is two independent collapsible elements:

enter image description here

Upvotes: 1

Related Questions