【问题标题】:Cloning a bootstrap element with an event listener使用事件侦听器克隆引导元素
【发布时间】:2016-12-04 20:47:33
【问题描述】:

我正在尝试克隆具有引导程序提供的数据切换行为的引导程序元素:

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>

克隆后,我将 divID 更改为新的唯一 id,并将按钮的 data-target 指向 new 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 是我正在克隆的对象容器的子级。

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

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

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

因此,有几个答案指出,只需从我的 clone() 调用中删除 true 即可避免复制事件侦听器。所以我现在意识到我的问题比我在这里过分简化的问题要复杂一些。我将把它作为一个单独的问题来问。 (Cloning a Bootstrap element but not all of the event listeners)

【问题讨论】:

  • 你能展示你的代码吗?
  • @jonju 我添加了用于修改克隆的 javascript。
  • 以上代码中的objectContainer是什么?
  • collapsibleObjCounter 是什么?如果您在某个循环中执行此操作,请提供克隆逻辑的完整代码块。这会很有帮助。谢谢
  • 我试图创建一个 jsfiddle,但我无法让引导程序正常工作。 objectContainer 是一个包含按钮和另一个 div 的 div。 collapsibleObjCounter 只是一个计数器,一个全局变量,用于为每个新的折叠目标创建新的 ID

标签: javascript jquery twitter-bootstrap clone


【解决方案1】:

到目前为止,您的代码还可以,只需将 trueclone() 中删除即可。

更新

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

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.

但是

您需要使用event delegation 技术处理动态对象的事件

 $(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我们不知道objectContainer 也不知道collapsibleObjCounter 那是什么?

【讨论】:

  • 您能解释一下您的代码是如何工作的吗?是不是你忽略了引导程序内置的折叠行为并直接实现它?
  • 不,我没有忽略引导程序的内置行为,引导程序崩溃事件仅在data-target 节点上具有toggle 属性功能的click 时发生,所以我没有忽略我正在用新的target 修改克隆部分的行为。
  • 您修改克隆的操作与我对header.find(".collapse").attr("id", collapseId);header.find("button[data-toggle='collapse']").attr("data-target", "#"+collapseId); 所做的操作有何不同?我通过检查生成的 HTML 确认这是有效的。
  • @PurpleVermont 到目前为止,您的代码还可以,只需从 clone() 中删除 true,关于您的问题,您尚未显示完整的 script,这就是我们无法找到 @987654352 的原因@。 LIKE 我们不知道objectContainer 也不知道collapsibleObjCounter 那是什么?这就是我为您创建单独且简单的解决方案的原因。希望你能理解。
  • 我认为你是对的,只需从 clone() 中删除 true 就可以解决我看到的问题。如果容器 div 中有其他事件处理程序需要按原样克隆怎么办?
【解决方案2】:

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

这是一个有效的 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。而不是 post incrementpre increment

【讨论】:

  • 为什么零值会有问题?它只是字符串的一部分。 (如果您担心的话,我绝对不会重复 ID。)
  • 是的,重复 ID 是我的担忧。所以我在 pluncker 中展示的不是你想要的?
【解决方案3】:

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

var header = objectContainer.clone();

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

这是fiddle

PS:不知道 OP 的问题中 this 或 objectContainer 是什么,因此创建了一个闭包并将结果粘贴到新的包装器中。

【讨论】:

  • 所以,除了...我发布了过于简化的代码,容器 div 中还有其他按钮,我确实需要复制其事件处理程序。有什么方法可以使用true 标志进行克隆,然后删除相关的侦听器,从而导致引导程序重新添加所需的侦听器?
  • 你可以尝试使用.on()在DOM树中的某个点委托这些事件处理程序,这样你可能根本不需要复制它们,它可以正常工作动态节点也是如此(通过冒泡)。例如。 $('body').on('click', '.someBtn', someFunc) 这将在点击 someBtn 时触发事件处理程序 someFunc,即使它是稍后动态添加的。
【解决方案4】:

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

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

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

我有来自aManHasNoNameforked a fiddle 的小提琴说明了这一点。唯一的修改是:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-01
    • 2019-12-23
    • 2017-09-14
    • 2018-05-16
    • 1970-01-01
    • 2011-12-20
    • 2016-11-14
    相关资源
    最近更新 更多