Это ранее обсуждалось здесь: Как выполнить действие, когда элемент добавлен на страницу с помощью 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),
Таким образом, фактический ответ таков: "Используйте наблюдателей за мутациями... в конце концов. Но пока оставьте с ответом Хосе Фаэти" :)
Ответ 3
Вы также можете использовать события анимации CSS3, чтобы получать уведомления о добавлении определенного элемента в DOM: http://www.backalleycoder.com/2012/04/25/i-want-a-damnodeinserted/
Ответ 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'));