Я хочу, чтобы функция по моему выбору запускалась, когда на страницу добавляется элемент DOM. Это в контексте расширения браузера, поэтому веб-страница работает независимо от меня, и я не могу изменить ее источник. Какие у меня здесь варианты?
Я предполагаю, что теоретически я мог бы просто использовать setInterval()
для постоянного поиска присутствия элемента и выполнения своих действий, если элемент есть, но мне нужен лучший подход.
Ответы:
Поскольку события мутации устарели в 2012 году, и вы не можете контролировать вставленные элементы, потому что они добавлены чужим кодом, единственный вариант - постоянно проверять их.
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 (одну десятую) секунды. Если вам не нужно наблюдение за элементами в реальном времени, этого должно быть достаточно.
источник
MutationObserver()
: davidwalsh.name/mutationobserver-apiEvent.ADDED_TO_STAGE
?Фактический ответ - «использовать наблюдателей за мутациями» (как указано в этом вопросе: определение того, был ли элемент HTML добавлен в DOM динамически ), однако поддержка (особенно в IE) ограничена ( http://caniuse.com/mutationobserver ) .
Итак, НАСТОЯЩИЙ ответ таков: «Используйте наблюдателей за мутациями .... в конце концов. Но пока воспользуйтесь ответом Хосе Фаэти» :)
источник
Между прекращением поддержки событий мутации и появлением
MutationObserver
эффективного способа получать уведомления о добавлении определенного элемента в DOM было использование событий анимации CSS3. .Чтобы процитировать сообщение в блоге:
источник
animationstart
, jsbin.com/netuquralu/1/edit .Вы можете использовать
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
.источник
ETA 24 Apr 17 Я хотел немного упростить это с помощью некоторых
async
/await
magic, так как это делает его более лаконичным:Используя тот же самый обещанный-наблюдаемый:
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)
Во-первых, мы будем использовать 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'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") }
Сложность генераторов заключается в том, что они не «возвращаются», как обычные функции. Итак, мы будем использовать вспомогательную функцию, чтобы иметь возможность использовать генератор как обычную функцию. (опять же, h / t в 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 в поток управления синхронного стиля. Как насчет этого.
источник
Есть многообещающая библиотека javascript под названием Arrive, которая выглядит как отличный способ начать пользоваться преимуществами наблюдателей за мутациями, когда поддержка браузеров станет обычным явлением.
https://github.com/uzairfarooq/arrive/
источник
Посмотрите этот плагин, который именно это делает - 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
источник
Решение на чистом 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'));
источник