使用事件侦听器克隆引导程序元素

Cloning a bootstrap element with an event listener

本文关键字:引导程序 元素 侦听器 事件      更新时间:2023-09-26

我正试图克隆一个具有bootstrap提供的数据切换行为的bootstrap元素:

HTML

<div class="container">
<button aria-expanded="false" data-target="#collapsible_obj_0" data-toggle="collapse" class="btn btn-link collapsed">click here</button>
<div style="height: 0px;" aria-expanded="false" id="collapsible_obj_0" class="collapse">
  <span>foo</span>
</div>
</div>

克隆后,我将div的ID更改为新的唯一id,并将按钮的data-target更改为指向新div。

JS

  var header = objectContainer.clone(true);
  var counter = this.collapsibleObjCounter++;
  var collapseId = "collapsible_obj_" + counter;
  header.find(".collapse").attr("id", collapseId);
  header.find("button[data-toggle='collapse']").attr("data-target", "#"+collapseId);

按钮和div是对象容器I正在克隆的子级。

有时这是可行的,但有时我会得到一个仍然可以扩展和收缩原始div的按钮,即使当我检查HTML时,ID看起来是正确的。

我怀疑复制的事件处理程序可能对要扩展和收缩的div的id的引用进行了硬编码,这就是为什么仅仅修复DOM元素中的id是不起作用的。然而,这并不能解释为什么有些克隆有效,而另一些则无效。

克隆带有引导行为的东西的正确方法是什么?

因此,有几个答案指出,仅从clone()调用中删除true就可以避免复制事件侦听器。所以我现在意识到我的问题比我在这里过于简单化的问题要复杂一些。我将单独提问。(克隆一个Bootstrap元素,但不是所有的事件侦听器)

到目前为止,您的代码还可以,只需从clone()中删除true即可,这是不需要的。

更新

布尔值指示是否应将事件处理程序与元素一起复制。默认值为false。因此,当我们在不传递任何布尔值的情况下调用.clone()方法时,它只是复制元素,而不是附加到它的事件处理程序。但是,当我们传递值true时,它会复制元素和附加到它上的任何event处理程序。

但是Bootstrap正在处理动态对象的事件处理程序,因此您不需要在克隆中使用true

喜欢

如果您使用这种方式处理动态对象的events

$(".btn").click(.....); 
// This button was dynamically created and you want a click event for it, 
// but it wont work because at the time of event binding this button wasn't exist at all.

但是

您需要使用事件委派技术为动态对象处理事件

 $(document).on("click",".btn",function(){ .... });

这将起作用,因为事件处理程序绑定到DOM树(在本例中为文档)的更高的元素,并且将在事件到达该元素时执行,该元素源自与选择器匹配的元素,这就是Bootstrap为动态对象所做的,如果需要,也可以为动态对象做同样的操作。她是JSFiddle

此外,您还需要将整个collapsible部分封装在div中进行克隆。

注意:使用.clone()会产生具有重复id属性的元素,这些属性本应是唯一的。在可能的情况下,建议避免克隆具有此属性的元素或使用类属性作为标识符。

因此,您需要在克隆后更新data-targetdivid属性,以便新创建的按钮targets新创建的折叠面板

我正在使用jQuery

这是代码代码段

$(function(){
  var target = "collapsible_obj_";
  var i = 1;
  
  $("#button").click(function(){
    $(".parent_colapse:last").clone().insertAfter(".parent_colapse:last");        
    $(".parent_colapse:last > button").attr("data-target", "#"+target+i);
    $(".parent_colapse:last .collapse").attr("id", target+i);
    i++;
  });
  
  $(document).on("click",".button",function(){
     alert();
  });
});
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<div class="parent_colapse">
<button aria-expanded="false" data-target="#collapsible_obj_0" data-toggle="collapse" class="btn btn-link collapsed">click here</button>
<div style="height: 0px;" aria-expanded="false" id="collapsible_obj_0" class="collapse">
  <span>foo</span>
  <button type="button" class="button">click</button>
</div>
  </div>
<button type="button" id="button">Clone</button>

关于您的问题您没有显示完整的script,这就是我们无法找到错误的原因LIKE我们不知道objectContainercollapsibleObjCounter那是什么?

克隆时不要复制事件,请删除true标志。

var header = objectContainer.clone();

我的猜测是Bootstrap正在处理动态对象的事件绑定,也用于数据切换,它只需要有不同的id;目标

这是一把小提琴。

PS:在OP的问题中,不知道这个或objectContainer是什么,所以在新的包装器中创建了一个闭包并粘贴结果。

问题不是由事件侦听器本身引起的。问题是Bootstrap是如何工作的。对于大多数Bootstrap组件,Bootstrap创建一个与DOM元素相关联的JavaScript对象在克隆引导程序组件时,需要注意这个对象对于collapse元素,Bootstrap会创建一个Collapse对象。

JavaScript对象通过jQuery的$.data与DOM元素相关联这就是问题所在。如果您使用jQuery的clone并请求将事件处理程序复制到克隆,那么您还可以使用$.data获得数据集的副本。但是,如果数据是对JavaScript对象的引用,则复制到克隆的是引用。因此,原始和克隆引用了同一个JavaScript对象,这就是一切出错的地方。顺便说一句,这对Bootstrap来说并不特殊:任何引用都会受到这个问题的影响。

您可以对作为引导组件的克隆元素执行$.removeData。这将强制Bootstrap重新创建JavaScript对象。对于自动注册到数据API中的组件,这应该是所有需要的。(collapse自动注册。)对于不自动注册的组件(例如工具提示),您需要调用$.[component]手动重新创建组件。

我从一个ManHasNoName的小提琴上拨出了一把小提琴来说明这一点。唯一的修改是:

  1. 将参数true, true添加到var header = objectContainer.clone();

  2. 修改header.find(".collapse").attr("id", collapseId),在末尾添加.removeData()

我已经为您的代码创建了一个JSFiddle。在您看来,最可能的错误是使用0this.collapsibleObjCounter++值可能会产生问题。

这是一个正在工作的JSFiddle。如果你想这么做,请告诉我。谢谢。

https://jsfiddle.net/2fgoywzy/1/


JS

$( document ).ready(function() {
for (i = 1; i < 5; i++) { 
    var objectContainer =  $("#main");
    var header = objectContainer.clone(true);
    var counter = i; // replace this with this.collapsibleObjCounter++
    var collapseId = "collapsible_obj_" + counter;
    header.find(".collapse").attr("id", collapseId);
    header.find("button[data-toggle='collapse']").attr("data-target", "#"+collapseId);
    $(header).appendTo('body');
}
});

HTML

<div id="main">
    <button aria-expanded="false" data-target="#collapsible_obj_0" data-toggle="collapse" class="btn btn-link collapsed">click here</button>
    <div style="height: 0px;" aria-expanded="false" id="collapsible_obj_0" class="collapse">
        <span>foo</span>
    </div>
</div>

如果你在this.collapsibleObjCounter++中有一个0的值,那么我建议你做++this.collapsibleObjCounter。不执行增量后执行

增量前