我在恢复已将元素传递给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结构中肯定没有某些东西,这些东西正在搞乱。
答案 0 :(得分:7)
您需要再次调用tooltip()
方法。您可以选择在克隆/删除项目之前销毁工具提示以清理数据。
$('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")
的内脏(即其子项),而不是使其与子项并存。