克隆并恢复“工具”元素

时间:2015-04-07 12:31:32

标签: javascript jquery html twitter-bootstrap

我在恢复已将元素传递给Bootstrap的.fn.tooltip()方法的DOM结构方面遇到了麻烦。

具体来说:$('footer p')传递给文档就绪事件的工具提示,如下所示:

$(function(){

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

})

我检查出来,工具提示有效,出现点击控制台消息。现在我通过调用函数来备份我将要删除的内容并从控制台中删除它:

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

如预期的那样,页脚p消失了。

现在我使用以下命令恢复window.backup变量中克隆和缓存的内容:

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

也从控制台调用,这里发生了什么:

  • footer p元素应该回来了
  • footer p点击后会生成控制台消息'只是为了测试事件' 消息,因此此事件已恢复以及元素
  • 没有恢复工具提示

即使我在函数experiment_restore中重新调用tooltip方法,我也什么也得不到。有没有人有一些想法?

更新 我又做了一个变种。尝试使用不同的 - 完全最小的DOM环境,只有p tooltip和父容器元素。 结果相同。在我的复杂DOM结构中肯定没有某些东西,这些东西正在搞乱。

这是very simple Fiddle

3 个答案:

答案 0 :(得分:7)

您需要再次调用tooltip()方法。您可以选择在克隆/删除项目之前销毁工具提示以清理数据。

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();
});

答案 1 :(得分:3)

对于您在问题中显示的方案,我会使用$().detach()将其从DOM中删除,同时保持事件处理程序和添加到$().data()的数据完好无损。关于你提出问题的小提琴:

$('#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);
});

这是更新后的fiddle

幕后发生的事情是Bootstrap使用$().data()将类Tooltip的JavaScript对象添加到DOM元素中,并添加了一堆事件处理程序。你需要保留这些。

如果由于某种原因,您无法使用$().detach(),则必须通过调用$().tooltip()重新创建工具提示。

为什么$().clone(true, true)无效?

您使用参数调用$().clone()以深层克隆DOM层次结构并使用$().data()保留事件处理程序和数据集,那么为什么它不起作用?是不是克隆应该引用Bootstrap创建的Tooltip对象?

是的,事件处理程序保留,而 克隆具有对Tooltip对象的引用。但是,这个对象本身克隆。更重要的是,它不适用于引用由$().clone()创建的新DOM节点。 (所以,即使jQuery 克隆它,它仍然无效。)它确实收到触发工具提示但Tooltip.prototype.show执行此检查的事件:

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

如果inDom在DOM中,this.$element变量将为true。但是,这是指为其创建工具提示的原始元素,而不是克隆。由于该元素不再在DOM中,因此inDom为false并且下一行返回,因此工具提示永远不会显示。

对于咯咯笑,请抓取您在其上创建Bootstrap工具提示的DOM元素的克隆,不要删除原始元素,而是将克隆添加到页面上的其他位置。然后触发克隆上的工具提示。工具提示将显示在原始元素上。 :)

我上面描述的是Bootstrap的jQuery插件工作的一般方式:他们使用$().data()将JavaScript对象添加到它们运行的​​元素。下拉列表还有一个Dropdown类,模态的Modal类等。

答案 2 :(得分:0)

作为补充答案,我使用了JQuery的clone方法,但我复制了所有事件监听器,例如.clone(true, true)。我的问题是工具提示与旧元素和克隆元素完全相同,但是它们的位置不同(因此,将鼠标悬停在新元素上会在我的浏览器的左上角显示一个工具提示)。

我想到的最简单的修复应该永远适用于所有Javascript,Bootstrap和JQuery:

const target = document.getElementById("random-div-you-want-to-clone-to")
$("selecting").clone(true, true).appendTo(target);

// if you only want .innerHTML of $("selecting")
// you can do $("selecting").children()

const _tooltips = target.querySelectorAll("[data-toggle='tooltip']");
for (const x of _tooltips) {
    const build = x.cloneNode(true);
    $(build).tooltip();
    x.parentNode.replaceNode(build, x);
}

因此.clone(true, true)将获取所有事件侦听器,包括“ mousedown”(它是工具提示的侦听器)。使用本机ECMAScript的cloneNode方法时,您没有获取事件侦听器,因此需要重置工具提示。

这不是最有效的方法,但是我已经花了一个小时试图思考一些问题……请我的客人找到一种更有效的方法,因为这种方法不是,但是它可以工作。 (例如,使用forEach,直接使用JQuery等)。

编辑:克隆时,您也可以使用.children()来获取$("selecting")的内脏(即其子项),而不是使其与子项并存。