Событие, когда элемент добавлен на страницу - PullRequest
124 голосов
/ 15 сентября 2011

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

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

Ответы [ 8 ]

81 голосов
/ 15 сентября 2011

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

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 );
}

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

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

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

29 голосов
/ 15 августа 2013

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

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

17 голосов
/ 07 июня 2014

Вы также можете использовать события CSS3 Animation, чтобы получать уведомления при добавлении определенного элемента в DOM: http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/

13 голосов
/ 20 октября 2011

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

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

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

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

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

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

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

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

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

9 голосов
/ 19 сентября 2016

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

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

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's 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)

Во-первых, мы будем использовать MutationObserver API для создания объекта наблюдателя.Мы обернем этот объект в обещание, и 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's 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's "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 в поток управления синхронного стиля.Как насчет этого.

8 голосов
/ 06 августа 2014

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

3 голосов
/ 04 февраля 2015

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

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

Инициализация выглядит так

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

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

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

Так что, если вы добавите новый элемент, такой как:

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

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

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

0 голосов
/ 15 декабря 2018

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

const SEARCH_DELAY = 100; // in ms

// it may run indefinitely. TODO: make it cancellable, using Promise's `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'));
...