jquery, определяющий div определенного класса, был добавлен в DOM

92

Я использую .on()для привязки событий div, которые создаются после загрузки страницы. Он отлично работает для щелчка, мыши ... но мне нужно знать, когда был добавлен новый div класса MyClass. Я ищу это:

$('#MyContainer').on({

  wascreated: function () { DoSomething($(this)); }

}, '.MyClass');

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

Благодарю.

француженка
источник
Это ваш собственный код, который генерирует элементы? Если это так, вы можете проверить его при создании и действовать соответствующим образом.
Surreal Dreams
Да, это правда, но с .on () я могу связывать события в document.ready, и тогда мне не нужно беспокоиться о привязке при создании HTML. Я ищу такое же поведение с DoSomething.
frenchie
вы можете использовать $ ("div / your-selector"). hasClass ("MyClass"); чтобы проверить, имеет ли div конкретный класс или нет. Думаю, вы могли бы использовать этот api.
KBN
.on () предназначен только для событий. Вы можете настроить собственное событие, но вам все равно придется запускать это событие при создании новых элементов.
Surreal Dreams

Ответы:

105

Раньше можно было подключиться к domManipметоду jQuery, чтобы поймать все манипуляции с jQuery dom и посмотреть, какие элементы были вставлены и т. Д., Но команда jQuery отключила это в jQuery 3.0+, поскольку обычно это не лучшее решение для подключения к методам jQuery таким образом, и они ' Мы сделали так, чтобы внутренний domManipметод больше не был доступен за пределами основного кода jQuery.

События мутации также устарели, поскольку раньше можно было делать что-то вроде

$(document).on('DOMNodeInserted', function(e) {
    if ( $(e.target).hasClass('MyClass') ) {
       //element with .MyClass was inserted.
    }
});

этого следует избегать, и сегодня вместо этого следует использовать Mutation Observers, которые будут работать следующим образом

var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
        console.log(mutation)
        if (mutation.addedNodes && mutation.addedNodes.length > 0) {
            // element added to DOM
            var hasClass = [].some.call(mutation.addedNodes, function(el) {
                return el.classList.contains('MyClass')
            });
            if (hasClass) {
                // element has class `MyClass`
                console.log('element ".MyClass" added');
            }
        }
    });
});

var config = {
    attributes: true,
    childList: true,
    characterData: true
};

observer.observe(document.body, config);
аденео
источник
Firefox и webkit в значительной степени должны быть в порядке, но поддержка этого в IE несколько слабая. Он должен работать в IE9, но, вероятно, не так сильно в IE8. Я знаю, что видел обходные пути, и на самом деле я не тестировал это в IE, поэтому сначала проверьте его, если он не работает, посмотрите, есть ли обходной путь. Есть плагин, который, похоже, добавляет сюда поддержку IE , но не пробовал, просто нашел в поиске Google.
adeneo
2
Сначала я использовал ваш ответ, но тот факт, что он не работал в разных браузерах, был проблемой. Затем я быстро нашел способ избежать этой проблемы в первую очередь. А затем, несколько недель назад, эта проблема обнаружения вставки узла снова появилась в моем коде. На этот раз код, который я опубликовал в своем ответе, работает ТОЧНО, как и должен, кросс-браузерный и ничего не глючит. Вот почему я перенес свой ответ как принятый; вот как работает SO, OP решает, какой ответ он принимает, и толпа голосует за ответы. Со временем люди проголосуют и решат, какой ответ им подходит больше всего.
Френчи
2
А также хочу сказать, что вы несколько раз помогали мне качественными ответами, которые спасли мне день. Изменение принятого ответа на мой зависит исключительно от кода и от того, как он соответствует проблеме, которую я должен был решить.
frenchie
получил "Uncaught TypeError: Невозможно прочитать свойство 'contains' of undefined" с этим кодом
Горди
53

Вот мой плагин, который именно это делает - jquery.initialize

Использование такое же, как если бы вы использовали .eachфункцию, но с.initialize функцией на элементе, отличие от того, .eachчто он также инициализирует элементы, добавленные в будущем, без какого-либо дополнительного кода - независимо от того, добавляете ли вы его с помощью AJAX или чего-то еще.

Инициализация имеет точно такой же синтаксис, как и с функцией .each

$(".some-element").initialize( function(){
    $(this).css("color", "blue");
});

Но теперь, если на странице появится новый элемент, соответствующий селектору .some-element, он будет мгновенно инициализирован. Способ добавления нового элемента не важен, вам не нужно заботиться о каких-либо обратных вызовах и т. Д.

$("<div/>").addClass('some-element').appendTo("body"); //new element will have blue color!

Плагин основан на MutationObserver

Адам Пьетрасиак
источник
Огромное спасибо.
Брэндон Харрис,
как остановить инициализацию? Я хочу прекратить смотреть, как только то, что я искал, будет добавлено ...
Евгений Восток
$ (". some-element"). initialize [..] устарел. Используйте $ .initialize (". AddDialog", function () {$ ('. NoData'). Hide ();}); вместо.
Heimi
Можно ли выполнить .initializeфункцию callback только для элементов, которые добавляются динамически (например, через Ajax), и пропустить обратный вызов для элементов, уже присутствующих при первоначальной загрузке страницы? Я пытаюсь добавить параметр для optionsэтого, но пока у меня это не работает.
Nevermore
Вы можете добавить проблему на github по этому поводу. А пока технически вы могли бы$('.myClass').addClass('ignore-initialize'); $.initialize('.myClass:not(.ignore-initialize)', callback);
Адам Пьетрасиак
19

После просмотра этого и нескольких других сообщений я попытался свести то, что я считал лучшим из каждого, к чему-то простому, что позволило мне определить, когда класс элементов вставлен, и затем действовать с этими элементами .

function onElementInserted(containerSelector, elementSelector, callback) {

    var onMutationsObserved = function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.addedNodes.length) {
                var elements = $(mutation.addedNodes).find(elementSelector);
                for (var i = 0, len = elements.length; i < len; i++) {
                    callback(elements[i]);
                }
            }
        });
    };

    var target = $(containerSelector)[0];
    var config = { childList: true, subtree: true };
    var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
    var observer = new MutationObserver(onMutationsObserved);    
    observer.observe(target, config);

}

onElementInserted('body', '.myTargetElement', function(element) {
    console.log(element);
});

Для меня важно то, что это позволяет а) целевому элементу существовать на любой глубине в «добавленных узлах» и б) иметь дело с элементом только при его первоначальной вставке (нет необходимости искать во всем параметре документа или игнорировать «уже обработанный» флаги).

Джон Клегг
источник
1
Ваше состояние должно быть , if(mutation.addedNodes.length)потому что addedNodesи removedNodesмассивы всегда в mutationтипа childList(проверено на IE11, Chrome53, FF49). Тем не менее +1, потому что ваш единственный ответ, addedNodesкоторый рассматривается ( callbackвызывается только при добавлении узла); не только в этой теме, но и во многих других. Все остальные просто вызывают callbackдля всех мутаций, даже если узел удаляется.
Vivek Athalye
17

Спустя 3 года опыта я слушаю «элемент определенного класса, добавленный в DOM» : вы просто добавляете крючок в html()функцию jQuery , например:

function Start() {

   var OldHtml = window.jQuery.fn.html;

   window.jQuery.fn.html = function () {

     var EnhancedHtml = OldHtml.apply(this, arguments);

     if (arguments.length && EnhancedHtml.find('.MyClass').length) {

         var TheElementAdded = EnhancedHtml.find('.MyClass'); //there it is
     }

     return EnhancedHtml;
   }
}

$(Start);

Это работает, если вы используете jQuery, что я и делаю. И он не полагается на событие DOMNodeInserted, зависящее от браузера , которое не является кроссбраузерным. Я также добавил ту же реализацию для.prepend()

В целом, это работает как амулет для меня, и, надеюсь, для вас тоже.

француженка
источник
6
Хм - я бы пояснил, что это работает, если вы используете исключительно htmlпомощник jQuery для изменения DOM, а не просто «с помощью jQuery» в целом. Элементы, добавленные с использованием, скажем, jQuery.appendне вызовут это событие.
iameli
1
Да, и поэтому я пояснил, что добавил ту же реализацию с .preprend (), и если вы используете .append (), сделайте то же самое с .append ()
frenchie
Есть гораздо более быстрый подход с использованием анимации CSS3 . Библиотека с открытым исходным кодом также предоставляет гораздо более простой код:insertionQ('#intercom-container').every(function(/element){ element.style.display = 'none'; });
Дэн Даскалеску
3
Почему отрицательный? Это действительно работает: нет плагинов и нет хакерской функции setTimeout.
frenchie
2
Проголосовали - это допустимый вариант - почему в мире (!!!) это было отклонено ?????? Дэн, CSS3-анимации - это не XBC: caniuse.com/#search=keyframes ; davidwalsh.name/detect-node-insertion .
Коди
6

вы можете использовать события мутации

http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-eventgroupings-mutationevents

РЕДАКТИРОВАТЬ

из MDN: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events

Устарело. Эта функция удалена из Интернета. Хотя некоторые браузеры могут по-прежнему поддерживать его, в настоящее время он удаляется. Не используйте его в старых или новых проектах. Страницы или веб-приложения, использующие его, могут сломаться в любой момент.

Наблюдатели за мутациями - это предлагаемая замена событий мутации в DOM4. Они должны быть включены в Firefox 14 и Chrome 18.

https://developer.mozilla.org/en/docs/Web/API/MutationObserver

MutationObserverпредоставляет разработчикам способ реагировать на изменения в DOM. Он разработан как замена для событий мутации, определенных в спецификации событий DOM3.

Пример использования

Следующий пример был взят из http://hacks.mozilla.org/2012/05/dom-mutationobserver-reacting-to-dom-changes-without-killing-browser-performance/ .

// select the target node
var target = document.querySelector('#some-id');

// create an observer instance
var observer = new MutationObserver(function(mutations) {
  mutations.forEach(function(mutation) {
    console.log(mutation.type);
  });    
});

// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };

// pass in the target node, as well as the observer options
observer.observe(target, config);

// later, you can stop observing
observer.disconnect();
RGB
источник
2
События мутации в значительной степени устарели, согласно mdn , в пользу MutationObservers
Ольга
Теперь, когда IE10 является EOL, можно с уверенностью сказать, что MutationObserver широко поддерживается.
rymo
0

Два дополнения к ответу adeneo:

(1) мы можем изменить строку

return el.classList.contains('MyClass');

Чтобы

if( el.classList ) {
    return el.classList.contains('MyClass');
} else {
    return false;
}

Так что "Uncaught TypeError: Cannot read property 'contains' of undefined"ошибки мы не увидим .

(2) добавить subtree: trueв configнайти добавленные узлы рекурсивно во всех добавленных элементов.

Agrul
источник