요소가 페이지에 추가 될 때 알림을 받으려면 어떻게해야합니까?


139

DOM 요소가 페이지에 추가 될 때 선택한 기능을 실행하고 싶습니다. 이것은 브라우저 확장과 관련이 있으므로 웹 페이지는 독립적으로 실행되며 소스를 수정할 수 없습니다. 내 옵션은 무엇입니까?

이론적 setInterval()으로는 요소의 존재를 지속적으로 검색하고 요소가있는 경우 내 작업을 수행 하는 데 사용할 수 있지만 더 나은 접근 방식이 필요하다고 생각합니다.


페이지에 넣은 다른 스크립트 나 소스에 관계없이 추가 된 요소를 확인해야합니까?
Jose Faeti

1
다른 함수의 코드에 어떤 함수가 요소를 추가하는지 알고 있다면 덮어 쓰고 사용자 지정 이벤트를 트리거하는 한 줄을 추가 할 수 있습니까?
jeffreydev

답변:


86

경고!

이 답변은 이제 구식입니다. DOM Level 4는 MutationObserver를 도입 하여 더 이상 사용되지 않는 돌연변이 이벤트를 효과적으로 대체합니다. 여기에 제시된 것보다 더 나은 해결책 은 이 답변 을 참조 하십시오. 진심으로. 100 밀리 초마다 DOM을 폴링하지 마십시오. CPU 전력을 낭비하고 사용자가 당신을 미워할 것입니다.

때문에 돌연변이 이벤트는 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 (1/10) 초마다 실행됩니다. 실시간 요소 관찰이 필요하지 않으면 충분합니다.


1
jose, 조건이 실제로 충족되면 settimeout을 어떻게 끝내나요? 즉, 마지막으로 x 초 후에 페이지에로드 된 요소를 찾았습니까?
klewis

1
@blachawk 변수에 setTimeout 반환 값을 할당해야합니다. 변수를 나중에 clearTimeout ()에 paratemer로 전달할 수 있습니다.
Jose Faeti

3
@blackhawk : 방금 그 대답을보고 +1했습니다. 그러나 setTimeout ()은 한 번만 실행되므로 clearTimeout ()을 실제로 사용할 필요는 없습니다. 하나의 'if-else'이면 충분합니다. 삽입 된 요소를 찾으면 setTimeout ()에서 실행을 허용하지 마십시오.
1111161171159459134

사용하는 유용한 실무 가이드 MutationObserver(): davidwalsh.name/mutationobserver-api
gfullam

4
더럽습니다. AS3와 "동등한"것이 실제로 Event.ADDED_TO_STAGE없습니까?
Bitterblue


19

변이 사건 의 폐기 와 출현의 출현 사이에 MutationObserver, 특정 요소가 DOM에 추가 될 때 통지되는 효율적인 방법은 CSS3 애니메이션 사건이용하는 것이었다 .

블로그 게시물을 인용하려면 :

DOM 노드 삽입 이벤트를 수신하려는 DOM 요소를 선택하여 (CSS 선택기 선택을 통해) CSS 키 프레임 시퀀스를 설정하십시오. 나는 비교적 친숙하고 거의 사용하지 않은 CSS 속성을 사용했습니다. 클립 의도 된 페이지 스타일로 엉망을 피하기 위해 개요 색상을 사용했습니다. 코드는 한 번 클립 속성을 대상으로했지만 IE에서는 버전 11부터 더 이상 애니메이션 할 수 없습니다. 애니메이션을 적용 할 수있는 모든 속성이 작동하므로 원하는 것을 선택하십시오.

다음으로 노드 삽입을 처리하기 위해 대리자로 사용하는 문서 전체의 animationstart 리스너를 추가했습니다. 애니메이션 이벤트에는 애니메이션을 시작한 키 프레임 시퀀스를 알려주는 animationName이라는 속성이 있습니다. animationName 속성이 노드 삽입을 위해 추가 한 키 프레임 시퀀스 이름과 동일해야합니다.


이것이 최고 성능의 솔루션이며, 이에 대한 라이브러리를 언급하는 중복 질문에 대한 답변 이 있습니다.
Dan Dascalescu

1
과소 평가.
Gökhan Kurt

꼼꼼한. 밀리 초 정밀도가 중요한 경우이 방법은 정확하지 않습니다. 이 jsbin은 인라인 콜백과 animationstart, jsbin.com/netuquralu/1/edit 사용 사이에 30ms 이상의 차이가 있음을 보여줍니다 .
Gajus

나는 커트에 동의합니다, 이것은 과소 평가되었습니다.
Zabbu

13

livequeryjQuery 용 플러그인을 사용할 수 있습니다 . 다음과 같은 선택기 표현식을 제공 할 수 있습니다.

$("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.


11

ETA 24 4 월 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'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) david walsh 블로그 기사 돌연변이에 대해 :

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 변이를 동기식 제어 흐름에 통합 할 수 있습니다. 얼마나 시합?



3

정확하게이 플러그인을 확인하십시오- -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

순수한 자바 스크립트 솔루션 (없이 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'));
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.