Событие, когда элемент добавлен на страницу

Это ранее обсуждалось здесь: Как выполнить действие, когда элемент добавлен на страницу с помощью JQuery?

Ответчик предложил запустить настраиваемое событие всякий раз, когда на страницу был добавлен div. Однако я пишу расширение Chrome и не имею доступа к источнику страницы. Какие у меня варианты? Я предполагаю, что в теории я мог бы просто использовать setTimeout для постоянного поиска присутствия элемента и добавления моего действия, если элемент существует.

Ответ 1

Поскольку DOM Mutation Events теперь устарели (см. Примечание внизу) с последними спецификациями, и у вас нет контроля над вставленными элементами, потому что они добавлены кодом другого пользователя, ваш единственный вариант - постоянно проверять их.

function checkDOMChange()
{
    // check for any new element being inserted here,
    // or a particular node being modified

    // call the function again after 100 milliseconds
    setTimeout( checkDOMChange, 100 );
}

Как только эта функция вызывается, она будет запускаться каждые xxx миллисекунды. Я выбрал 100, что составляет 1/10 (одна десятая) секунды. Если вам не нужны элементы времени в режиме реального времени, этого должно быть достаточно.

редактировать

Как прокомментировал Бен Дэвис, спецификация DOM уровня 4 представляет наблюдателей Mutation (также в документах Mozilla), которые заменяют устаревшие события мутации.

Ответ 2

Фактический ответ - "использовать наблюдателей мутаций" (как указано в этом вопросе: определение, был ли HTML-элемент добавлен в DOM динамически), однако поддержка (в частности, в IE) ограничена (http://caniuse.com/mutationobserver),

Таким образом, фактический ответ таков: "Используйте наблюдателей за мутациями... в конце концов. Но пока оставьте с ответом Хосе Фаэти" :)

Ответ 4

Вы можете использовать плагин livequery для jQuery. Вы можете предоставить выражение селектора, например:

$("input[type=button].removeItemButton").livequery(function () {
    $("#statusBar").text('You may now remove items.');
});

Каждый раз, когда removeItemButton класса removeItemButton в строке состояния появляется сообщение.

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

Пересмотренный ответ

Ответ выше был предназначен только для обнаружения того, что элемент был добавлен в DOM через плагин.

Однако, скорее всего, jQuery.on() будет более уместным, например:

$("#myParentContainer").on('click', '.removeItemButton', function(){
          alert($(this).text() + ' has been removed');
});

Если у вас есть динамическое содержимое, которое должно реагировать, например, на клики, лучше связать события с родительским контейнером, используя jQuery.on.

Ответ 5

ETA 24 апреля 17 Я хотел немного упростить это с помощью некоторой магии async/await, так как это делает ее намного более лаконичной:

Использование того же обещанного-наблюдаемого:

const startObservable = (domNode) => {
  var targetNode = domNode;

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

  return new Promise((resolve) => {
      var observer = new MutationObserver(function (mutations) {
         // For the sake of...observation...let output the mutation to console to see how this all works
         mutations.forEach(function (mutation) {
             console.log(mutation.type);
         });
         resolve(mutations)
     });
     observer.observe(targetNode, observerConfig);
   })
} 

Ваша вызывающая функция может быть такой простой, как:

const waitForMutation = async () => {
    const button = document.querySelector('.some-button')
    if (button !== null) button.click()
    try {
      const results = await startObservable(someDomNode)
      return results
    } catch (err) { 
      console.error(err)
    }
}

Если вы хотите добавить тайм-аут, вы можете использовать простой шаблон Promise.race, как показано здесь:

const waitForMutation = async (timeout = 5000 /*in ms*/) => {
    const button = document.querySelector('.some-button')
    if (button !== null) button.click()
    try {

      const results = await Promise.race([
          startObservable(someDomNode),
          // this will throw after the timeout, skipping 
          // the return & going to the catch block
          new Promise((resolve, reject) => setTimeout(
             reject, 
             timeout, 
             new Error('timed out waiting for mutation')
          )
       ])
      return results
    } catch (err) { 
      console.error(err)
    }
}

оригинал

Вы можете сделать это без библиотек, но вам придется использовать некоторые вещи ES6, так что будьте в курсе проблем совместимости (то есть, если ваша аудитория в основном является амишей, луддитами или, что еще хуже, пользователями IE8)

Во-первых, мы будем использовать API MutationObserver для создания объекта наблюдателя. Мы обернем этот объект в обещание и resolve() при вызове обратного вызова (h/t davidwalshblog) Статья блога Дэвида Уолша о мутациях:

const startObservable = (domNode) => {
    var targetNode = domNode;

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

    return new Promise((resolve) => {
        var observer = new MutationObserver(function (mutations) {
            // For the sake of...observation...let output the mutation to console to see how this all works
            mutations.forEach(function (mutation) {
                console.log(mutation.type);
            });
            resolve(mutations)
        });
        observer.observe(targetNode, observerConfig);
    })
} 

Затем мы создадим функцию generator function. Если вы еще не использовали их, то вы упускаете возможность - но краткий обзор таков: он работает как функция синхронизации, и когда он находит выражение yield <Promise>, он ожидает неблокирующим образом обещание будет выполнено (Генераторы делают больше, чем это, но это то, что нас интересует здесь).

// we'll declare our DOM node here, too
let targ = document.querySelector('#domNodeToWatch')

function* getMutation() {
    console.log("Starting")
    var mutations = yield startObservable(targ)
    console.log("done")
}

Хитрость в генераторах заключается в том, что они "не возвращаются", как обычная функция. Итак, мы будем использовать вспомогательную функцию, чтобы иметь возможность использовать генератор как обычную функцию. (опять-таки, ч/т до dwb)

function runGenerator(g) {
    var it = g(), ret;

    // asynchronously iterate over generator
    (function iterate(val){
        ret = it.next( val );

        if (!ret.done) {
            // poor man "is it a promise?" test
            if ("then" in ret.value) {
                // wait on the promise
                ret.value.then( iterate );
            }
            // immediate value: just send right back in
            else {
                // avoid synchronous recursion
                setTimeout( function(){
                    iterate( ret.value );
                }, 0 );
            }
        }
    })();
}

Затем, в любой момент, прежде чем может произойти ожидаемая мутация DOM, просто запустите runGenerator(getMutation).

Теперь вы можете интегрировать DOM-мутации в поток управления синхронного стиля. Как насчет этого.

Ответ 6

Там есть многообещающая библиотека javascript под названием Arrive, которая выглядит как отличный способ начать использование наблюдателей за мутацией, когда поддержка браузера становится обычным явлением.

https://github.com/uzairfarooq/arrive/

Ответ 7

Проверьте этот плагин, который делает это, что - jquery.initialize

Он работает отлично, как функция .each, разница заключается в том, что он принимает введенный вами селектор и следит за добавлением новых элементов в будущем, соответствующих этому селектору, и инициализации их

Инициализация выглядит следующим образом

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

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

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

Итак, если вы добавите новый элемент, например:

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

он будет немедленно инициализирован.

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

Ответ 8

Чистое решение javascript (без jQuery):

const SEARCH_DELAY = 100; // in ms

// it may run indefinitely. TODO: make it cancellable, using Promise 'reject'
function waitForElementToBeAdded(cssSelector) {
  return new Promise((resolve) => {
    const interval = setInterval(() => {
      if (element = document.querySelector(cssSelector)) {
        clearInterval(interval);
        resolve(element);
      }
    }, SEARCH_DELAY);
  });
}

console.log(await waitForElementToBeAdded('#main'));