Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clone and restore "tooltiped" elements

I'm in trouble with restoring DOM structure that has elements passed to Bootstrap's .fn.tooltip() method.

To be specific: $('footer p') is passed to tooltip on document ready event, like this:

$(function(){

$('footer p').tooltip();
$('footer p').on('click', function(){
console.log('Just to test events')
});

})

I check it out, tooltip works, on click console message appears. Now I take backup of what am I about to delete and delete it, from console, by calling function:

function experiment_destroy() {
window.backup = $('footer').clone(true, true);
$('footer p').remove();
}

as expected, footer's p disappears.

Now I restore what is cloned and cached in window.backup variable with:

function experiment_restore(){
    $('footer').empty();
    $('footer').replaceWith(window.backup);
}

also called from console and here's what happens:

  • footer p element is back as it should be
  • footer p on click produces console message 'Just to test events' message, so this event is restored along with element
  • no tooltip is restored.

Even if I re-call tooltip method in function experiment_restore I get nothing. Does anyone have some idea?

UPDATE: I've made one more variation. Tried with different - totally minimal DOM environment with just p for tooltip and parent container element. Results are the same. Definitely there isn't just something in my complex DOM structure that was messing things up.

Here is very simple Fiddle.

like image 923
Miloš Đakonović Avatar asked Apr 07 '15 12:04

Miloš Đakonović


2 Answers

You need to call the tooltip() method again. Optionally you should destroy the tooltip before cloning / removing the item for cleaning up the data.

Working Fiddle

$('footer p').tooltip();

$('#destroy').click(function(){
    // optionally remove bindings
    $('footer p').tooltip('destroy');

    window.backup = $('footer').clone();
    $('footer p').remove();
})

$('#restore').click(function(){
    $('footer').replaceWith(window.backup);

    // apply tooltip again
    //window.backup.find("p").tooltip();
    $('footer p').tooltip();
});
like image 125
Catalin Avatar answered Nov 03 '22 03:11

Catalin


For the scenario you've shown in your question, I would use $().detach() to remove it from the DOM while at the same time keeping the event handlers and the data added to it with $().data() intact. In terms of the fiddle you've put in the question:

$('#destroy').click(function(){
    var $footer_p = $('footer p');
    window.backup = $footer_p;
    $footer_p.detach();
})

$('#restore').click(function(){
    var $footer = $('footer');
    $footer.append(window.backup);
});

Here's an updated fiddle

What happens behind the scenes is that Bootstrap uses $().data() to add a JavaScript object of class Tooltip to your DOM element, and adds a bunch of event handlers. You need to preserve these.

If for some reason, you cannot use $().detach(), then you would have to recreate the tooltip by calling $().tooltip().

Why is $().clone(true, true) not working?

You call $().clone() with parameters to deep clone the DOM hierarchy and preserve the event handlers and the data set with $().data() so why is it not working? Is it not the case that the clone should have a reference to the Tooltip object created by Bootstrap?

Yes, the event handlers are preserved, and the clone does have a reference to the Tooltip object. However, this object it itself not cloned. More importantly, it is not adapted to refer to the new DOM node created by $().clone(). (So even if jQuery would clone it, it would still not work.) It does receive the event that would trigger the tooltip but Tooltip.prototype.show performs this check:

  var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0])
  if (e.isDefaultPrevented() || !inDom) return

The inDom variable will be true if this.$element is in the DOM. However, this refers to the original element for which the tooltip was created, not the clone. Since that element is no longer in the DOM, then inDom is false and the next line returns, so the tooltip is never shown.

For giggles, take a clone of a DOM element on which you created a Bootstrap tooltip, do not remove the original element but add the clone somewhere else on the page. Then trigger the tooltip on the clone. The tooltip will appear on the original element. :)

What I described above is the general way Bootstrap's jQuery plugins work: they use $().data() to add JavaScript objects to the elements on which they operate. There's also a Dropdown class for dropdowns, a Modal class for modals, etc.

like image 37
Louis Avatar answered Nov 03 '22 02:11

Louis