jQuery “on create” подія для динамічно створених елементів


81

Мені потрібно вміти динамічно створювати <select>елемент і перетворювати його в jQuery .combobox(). Це має бути подія створення елемента, на відміну від якоїсь події "клацання", і в цьому випадку я міг би просто використовувати jQuery .on().

То чи існує щось подібне?

$(document).on("create", "select", function() {
    $(this).combobox();
}

Я неохоче використовую livequery, оскільки він дуже застарів.

ОНОВЛЕННЯ Згаданий select / combobox завантажується через ajax в кольорову скриньку jQuery (модальне вікно), отже проблема - я можу ініціювати combobox лише за допомогою colorbox onComplete, однак при зміні одного combobox інший select / combobox повинен бути динамічно створений, для цього мені потрібен більш загальний спосіб виявлення створення елемента ( selectв даному випадку).

UPDATE2 Щоб спробувати пояснити проблему далі - у мене є select/comboboxелементи, створені рекурсивно, всередині також є багато ініціюючого коду .combobox(), тому якби я використав класичний підхід, як у відповіді @ bipen , мій код роздувся б до шалених рівнів. Сподіваюся, це краще пояснює проблему.

UPDATE3 Дякую всім, тепер я розумію, що після припинення дії DOMNodeInsertedмутації DOM залишилася порожнеча, і рішення цієї проблеми не існує. Мені просто доведеться переглянути свою заявку.


1
Може бути з готовим заходом? $(select).ready(function() { });
Пабло Бандерас

@PabloBanderas readyвикористовується для: "Вкажіть функцію, яка буде виконуватися, коли DOM буде повністю завантажений." (з jquery.com)
Кабальєро

1
Чому б не застосувати combobox()метод в момент створення / вставки, а не потім?
Девід каже, що відновити Моніку

@DavidThomas Спробував пояснити причину в моєму другому оновленні
Кабальєро

Так що $(document).on('onComplete', 'select', function(){ // bind here? });? (Очевидно, використовуйте ближчого батька, ніж той document.)
Девід каже, що відновити Моніку

Відповіді:


59

Ви можете подія , щоб отримати подія для того, коли він додається в документ коду.onDOMNodeInserted

$('body').on('DOMNodeInserted', 'select', function () {
      //$(this).combobox();
});

$('<select>').appendTo('body');
$('<select>').appendTo('body');

Скрипка тут: http://jsfiddle.net/Codesleuth/qLAB2/3/

РЕДАГУВАТИ: після прочитання мені просто потрібно ще раз перевірити DOMNodeInsertedце не призведе до проблем у браузерах. Це запитання 2010 року свідчить про те, що IE не підтримує подію, тому протестуйте його, якщо зможете.

Дивіться тут: [посилання] Увага! тип події DOMNodeInserted визначений у цій специфікації для посилання та повноти, але ця специфікація припиняє використання цього типу події.


не .delegateприпинено з jQuery версії 1.7 і не замінено .on?
Кабальєро

1
Так, ти маєш рацію. Я зміню цю відповідь, але я, чесно кажучи, не впевнений, чи варто вам використовувати це, оскільки DOMNodeInsertedвоно вже не використовується.
Кодслеут

Я пробував, on('DOMNodeInserted'перш ніж опублікувати це запитання. Очевидно, це не спрацювало.
Кабальєро

20
Оскільки не у вашому питанні ви пробували, я б сказав, що це несправедливий голос. У будь-якому випадку, я не можу вам тут допомогти.
Кодслеут


20

Як згадувалось у кількох інших відповідях, події мутації застаріли, тому вам слід використовувати MutationObserver замість цього . Оскільки про це ще ніхто не повідомляв, тут справа йде ...

Основний API JavaScript

API для MutationObserver досить простий. Це не так просто, як події мутації, але все одно це нормально.

function callback(records) {
  records.forEach(function (record) {
    var list = record.addedNodes;
    var i = list.length - 1;
    
	for ( ; i > -1; i-- ) {
	  if (list[i].nodeName === 'SELECT') {
	    // Insert code here...
	    console.log(list[i]);
	  }
	}
  });
}

var observer = new MutationObserver(callback);

var targetNode = document.body;

observer.observe(targetNode, { childList: true, subtree: true });
<script>
  // For testing
  setTimeout(function() {
    var $el = document.createElement('select');
    document.body.appendChild($el);
  }, 500);
</script>

Давайте розберемо це.

var observer = new MutationObserver(callback);

Це створює спостерігача. Спостерігач ще нічого не спостерігає; саме тут прикріплюється слухач події.

observer.observe(targetNode, { childList: true, subtree: true });

Це змушує спостерігача запуститися. Перший аргумент - це вузол, на якому спостерігач спостерігатиме за змінами. Другий аргумент - варіанти, на що слід слідкувати .

  • childList означає, що я хочу стежити за тим, як дочірні елементи додаються або видаляються.
  • subtree- це модифікатор, який розширюється childList для спостереження за змінами в будь-якому місці піддерева цього елемента (в іншому випадку він буде просто розглядати зміни безпосередньо всередині targetNode).

Два інших основні варіанти, крім цього childList, attributesі characterData, що означають, як вони звучать. Ви повинні використовувати один із цих трьох.

function callback(records) {
  records.forEach(function (record) {

Усередині зворотного дзвінка стає трохи хитро. Зворотний дзвінок отримує масив MutationRecord s. Кожен MutationRecord може описувати кілька змін одного типу ( childList, attributesабо characterData). Оскільки я лише наказав спостерігачеві стежити childList, я не буду турбуватися перевіряючи тип.

var list = record.addedNodes;

Тут я захоплюю NodeList усіх дочірніх вузлів, які були додані. Це буде порожнім для всіх записів, де вузли не додаються (а таких записів може бути багато).

З цього моменту я прокручую додані вузли і знаходжу будь-які <select>елементи.

Тут нічого насправді складного.

jQuery

... але ви просили jQuery. Прекрасно.

(function($) {

  var observers = [];

  $.event.special.domNodeInserted = {

    setup: function setup(data, namespaces) {
      var observer = new MutationObserver(checkObservers);

      observers.push([this, observer, []]);
    },

    teardown: function teardown(namespaces) {
      var obs = getObserverData(this);

      obs[1].disconnect();

      observers = $.grep(observers, function(item) {
        return item !== obs;
      });
    },

    remove: function remove(handleObj) {
      var obs = getObserverData(this);

      obs[2] = obs[2].filter(function(event) {
        return event[0] !== handleObj.selector && event[1] !== handleObj.handler;
      });
    },

    add: function add(handleObj) {
      var obs = getObserverData(this);

      var opts = $.extend({}, {
        childList: true,
        subtree: true
      }, handleObj.data);

      obs[1].observe(this, opts);

      obs[2].push([handleObj.selector, handleObj.handler]);
    }
  };

  function getObserverData(element) {
    var $el = $(element);

    return $.grep(observers, function(item) {
      return $el.is(item[0]);
    })[0];
  }

  function checkObservers(records, observer) {
    var obs = $.grep(observers, function(item) {
      return item[1] === observer;
    })[0];

    var triggers = obs[2];

    var changes = [];

    records.forEach(function(record) {
      if (record.type === 'attributes') {
        if (changes.indexOf(record.target) === -1) {
          changes.push(record.target);
        }

        return;
      }

      $(record.addedNodes).toArray().forEach(function(el) {
        if (changes.indexOf(el) === -1) {
          changes.push(el);
        }
      })
    });

    triggers.forEach(function checkTrigger(item) {
      changes.forEach(function(el) {
        var $el = $(el);

        if ($el.is(item[0])) {
          $el.trigger('domNodeInserted');
        }
      });
    });
  }

})(jQuery);

Це створює нову подію з ім'ям domNodeInserted, з допомогою JQuery спеціальних подій API . Ви можете використовувати його так:

$(document).on("domNodeInserted", "select", function () {
  $(this).combobox();
});

Я особисто пропоную шукати клас, оскільки деякі бібліотеки створюватимуть selectелементи для тестування.

Звичайно, ви також можете використовувати .off("domNodeInserted", ...)або тонко налаштувати перегляд, передавши такі дані:

$(document.body).on("domNodeInserted", "select.test", {
  attributes: true,
  subtree: false
}, function () {
  $(this).combobox();
});

Це спричинить перевірку зовнішнього вигляду select.testелемента, коли атрибути змінюються для елементів безпосередньо всередині тіла.

Ви можете побачити його в прямому ефірі нижче або на jsFiddle .


Примітка

Цей код jQuery є досить базовою реалізацією. Це не спрацьовує у випадках, коли модифікації в інших місцях роблять ваш селектор дійсним.

Наприклад, припустимо, що ваш селектор є, .test selectа документ уже має <select>. Додавання класу testдо <body>зробить селектор дійсним, але оскільки я лише перевіряю record.targetі record.addedNodes, подія не запускається. Зміна має відбутися з елементом, який ви хочете вибрати сам.

Цього можна уникнути, запитуючи селектор, коли трапляються мутації. Я вирішив цього не робити, щоб уникнути дублювання подій для елементів, які вже оброблялись. Правильна робота з сусідніми або загальними комбінаторами братів чи сестер зробить справи ще складнішими.

Для більш повного вирішення см https://github.com/pie6k/jquery.initialize , як згадано в Damien Ó Ceallaigh «s відповідь . Однак автор цієї бібліотеки оголосив, що бібліотека стара, і пропонує не використовувати для цього jQuery.


9

Просто придумав це рішення, яке, здається, вирішує всі мої проблеми з ajax.

Для готових подій я зараз використовую це:

function loaded(selector, callback){
    //trigger after page load.
    $(function () {
        callback($(selector));
    });
    //trigger after page update eg ajax event or jquery insert.
    $(document).on('DOMNodeInserted', selector, function () {
        callback($(this));
    });
}

loaded('.foo', function(el){
    //some action
    el.css('background', 'black');
});

І для звичайних тригерних подій я зараз використовую це:

$(document).on('click', '.foo', function () {
    //some action
    $(this).css('background', 'pink');
});


4

Це можна зробити за допомогою, DOM4 MutationObserversале це буде працювати лише у Firefox 14+ / Chrome 18+ (поки що).

Однак є " епічний хак " (авторські слова не мої!), Який працює у всіх браузерах, що підтримують анімацію CSS3, а саме: IE10, Firefox 5+, Chrome 3+, Opera 12, Android 2.0+, Safari 4+. Дивіться демонстрацію з блогу. Злом полягає у використанні події анімації CSS3 з заданим ім’ям, яка спостерігається та реагує на JavaScript.


4

Одним із способів, який здається надійним (хоча протестований лише у Firefox та Chrome), є використання JavaScript для прослуховування події animationend(або її camelCased та префіксу, брат або сестра animationEnd) та застосування короткочасної (у демонстрації 0,01 секунди) анімації до тип елемента, який ви плануєте додати. Це, звичайно, не onCreateподія, але наближує (у сумісних браузерах) onInsertionтип події; наступне - доказ концепції:

$(document).on('webkitAnimationEnd animationend MSAnimationEnd oanimationend', function(e){
    var eTarget = e.target;
    console.log(eTarget.tagName.toLowerCase() + ' added to ' + eTarget.parentNode.tagName.toLowerCase());
    $(eTarget).draggable(); // or whatever other method you'd prefer
});

З таким HTML:

<div class="wrapper">
    <button class="add">add a div element</button>
</div>

І (скорочено, попередньо випущені версії видалено, хоча вони присутні у Скрипці нижче)

/* vendor-prefixed alternatives removed for brevity */
@keyframes added {
    0% {
        color: #fff;
    }
}

div {
    color: #000;
    /* vendor-prefixed properties removed for brevity */
    animation: added 0.01s linear;
    animation-iteration-count: 1;
}

Демонстрація JS Fiddle .

Очевидно, CSS можна налаштувати відповідно до розміщення відповідних елементів, а також селектора, що використовується в jQuery (він дійсно повинен бути якомога ближче до точки вставки).

Документація назв подій:

Mozilla   |  animationend
Microsoft |  MSAnimationEnd
Opera     |  oanimationend
Webkit    |  webkitAnimationEnd
W3C       |  animationend

Список літератури:


4

Для мене прив'язка до тіла не працює. Прив’язка до документа за допомогою jQuery.bind () робить.

$(document).bind('DOMNodeInserted',function(e){
             var target = e.target;
         });

1

Думаю, варто зазначити, що в деяких випадках це може спрацювати:

$( document ).ajaxComplete(function() {
// Do Stuff
});

1

замість...

$(".class").click( function() {
    // do something
});

Ви можете написати ...

$('body').on('click', '.class', function() {
    // do something
});

0

створити <select>з ідентифікатором, додати його до документа .. і зателефонувати.combobox

  var dynamicScript='<select id="selectid"><option value="1">...</option>.....</select>'
  $('body').append(dynamicScript); //append this to the place your wanted.
  $('#selectid').combobox();  //get the id and add .combobox();

це повинно зробити трюк .. ви можете приховати виділення, якщо хочете, і після .comboboxпоказати це .. або в іншому випадку використовуйте find ..

 $(document).find('select').combobox() //though this is not good performancewise

-1

якщо ви використовуєте angularjs, ви можете написати власну директиву. У мене була та ж проблема з bootstrapSwitch. Мені потрібно зателефонувати $("[name='my-checkbox']").bootstrapSwitch(); у javascript, але мій об’єкт введення html на той час не був створений. Тому я пишу власну директиву і створюю вхідний елемент за допомогою <input type="checkbox" checkbox-switch>

У директиві я компілюю елемент, щоб отримати доступ через javascript для виконання команди jquery (подібно до вашої .combobox()команди). Дуже важливо видалити атрибут. В іншому випадку ця директива викличе себе, і ви побудуєте цикл

app.directive("checkboxSwitch", function($compile) {
return {
    link: function($scope, element) {
        var input = element[0];
        input.removeAttribute("checkbox-switch");
        var inputCompiled = $compile(input)($scope.$parent);
        inputCompiled.bootstrapSwitch();
    }
}
});

Питання стосувалось лише jQuery, а не AngularJS. Рішення має бути обмежене плагінами jQuery та jQuery.
James Dunne
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.