Клонирование элемента начальной загрузки с помощью прослушивателя событий

Я пытаюсь клонировать элемент начальной загрузки, который имеет поведение переключения данных, предоставляемое 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>

После клонирования я меняю ID div на новый уникальный 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 являются дочерними элементами контейнера объектов. Я клонирование.

Иногда это работает, но иногда я получаю кнопку, которая все еще расширяет и сжимает оригинальный div, даже если при проверке HTML идентификаторы выглядят правильно.

Я подозреваю, что обработчик события, который скопирован, может быть жестким кодированием ссылки на идентификатор div, который нужно развернуть и сжать, поэтому просто исправление идентификаторов в элементах DOM не работает. Однако это не объясняет, почему некоторые из клонов работают, а другие нет.

Каков правильный способ клонирования того, к чему привязаны загрузочные поведения?

Итак, в нескольких ответах указано, что просто удаление true из моего вызова clone() позволит избежать копирования прослушивателя событий. Поэтому я теперь понимаю, что моя проблема немного сложнее, чем тот, который я упростил здесь. Я задам его как отдельный вопрос. (Клонирование элемента Bootstrap, но не всех прослушивателей событий)

Ответ 1

Пока ваш код в порядке, просто удалите true из clone() ему не нужно.

ОБНОВЛЕНО

Это Boolean указывает, следует ли копировать обработчики событий вместе с элементами. Значение по умолчанию - false. Итак, когда мы вызывали метод .clone(), не передавая никакого значения Boolean, он просто копировал элементы, но не привязывал к ним обработчики событий. Но когда мы передаем значение true, он копирует как элементы, так и любые обработчики событий, прикрепленные к нему.

Но Bootstrap обрабатывает обработчики событий для динамических объектов, поэтому вам не нужно true в клоне.

LIKE

Если вы обрабатываете 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-target и div id, чтобы вновь созданная кнопка 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>

Ответ 2

Не копируйте события во время клонирования, удалите флаг true.

var header = objectContainer.clone();

Мое предположение: Bootstrap обрабатывает привязку событий к динамическим объектам также для переключения данных, для этого требуется только идентификатор и цель.

Здесь fiddle.

PS: Не знаю, что это было или objectContainer в вопросе OP, поэтому создал закрытие и вставив результат в новую оболочку.

Ответ 3

Я создал 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>

Если у вас есть значение 0 в this.collapsibleObjCounter++, я бы предложил сделать ++this.collapsibleObjCounter. Вместо post increment выполните pre increment

Ответ 4

Проблема не вызвана самими прослушивателями событий. Проблема заключается в том, как работает Bootstrap. Для большинства компонентов Bootstrap Bootstrap создает объект JavaScript, связанный с элементом DOM. Этот объект - это то, что вам нужно позаботиться, когда вы клонируете компоненты Bootstrap. Для элемента collapse Bootstrap создает collapse объект.

Объект JavaScript связан с элементом DOM через jQuery $.data. Это проблема. Если вы используете jQuery clone и запрашиваете, чтобы обработчики событий были скопированы в клон, вы также получите копию набора данных с помощью $.data. Однако, когда данные являются ссылкой на объект JavaScript, то это ссылка, которая копируется в клон. Таким образом, оригинал и клон относятся к одному и тому же объекту JavaScript, и здесь все сбивается с пути. Это, кстати, не относится к Bootstrap: любая ссылка подвержена этой проблеме.

Что вы можете сделать, это выполнить $.removeData для клонированных элементов, которые являются компонентами Bootstrap. Это заставит Bootstrap воссоздать объект JavaScript. Для компонентов, которые автоматически регистрируются в API данных, это должно быть все, что необходимо. (collapse автоматически регистрируется.) Для компонентов, которые не регистрируются автоматически (например, всплывающих подсказок), вам необходимо вызвать $.[component] для воссоздания компонента вручную.

Я разворачивал скрипку из aManHasNoName, что иллюстрирует это. Единственными модификациями были:

  • Добавьте параметры true, true в var header = objectContainer.clone();

  • Измените header.find(".collapse").attr("id", collapseId), чтобы добавить .removeData() в конец.