sourcecode

요소가 존재할 때까지 기다리는 방법

copyscript 2022. 9. 6. 22:25
반응형

요소가 존재할 때까지 기다리는 방법

Chrome에서 Extension을 작업하고 있는데, 어떤 요소가 존재하는지 알아내는 가장 좋은 방법이 있을까요?요소가 존재할 때까지 체크하는 간격을 두고 플레인 Javascript를 사용하는가, 아니면 jQuery가 이를 위한 쉬운 방법을 가지고 있는가?

다음은 MutationObserver api를 사용한 간단한 솔루션입니다.

  1. .jQuery
  2. .Timer
  3. 서드파티 라이브러리 없음
  4. Promise가 되어 , 잘 .async/await

나는 그것을 여러 프로젝트에 사용해 왔다.

function waitForElm(selector) {
    return new Promise(resolve => {
        if (document.querySelector(selector)) {
            return resolve(document.querySelector(selector));
        }

        const observer = new MutationObserver(mutations => {
            if (document.querySelector(selector)) {
                resolve(document.querySelector(selector));
                observer.disconnect();
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    });
}

사용방법:

waitForElm('.some-class').then((elm) => {
    console.log('Element is ready');
    console.log(elm.textContent);
});

또는 비동기/대기:

const elm = await waitForElm('.some-class');

DOMNodeInserted는 퍼포먼스 문제로 인해 다른 DOM 변환 이벤트와 함께 권장되지 않습니다.MutationObserver를 사용하여 DOM을 감시하는 것이 좋습니다.다만, 새로운 브라우저에서만 지원되고 있기 때문에, 에 폴백 해 주세요.DOMNodeInsertedMutationObserver사용할 수 없습니다.

let observer = new MutationObserver((mutations) => {
  mutations.forEach((mutation) => {
    if (!mutation.addedNodes) return

    for (let i = 0; i < mutation.addedNodes.length; i++) {
      // do things to your newly added nodes here
      let node = mutation.addedNodes[i]
    }
  })
})

observer.observe(document.body, {
    childList: true
  , subtree: true
  , attributes: false
  , characterData: false
})

// stop watching using:
observer.disconnect()

요소의 표시를 기다리는 핵심 JavaScript 함수는 다음과 같습니다(더 정확하게는 DOM에 삽입).

// Call the below function
waitForElementToDisplay("#div1",function(){alert("Hi");},1000,9000);

function waitForElementToDisplay(selector, callback, checkFrequencyInMs, timeoutInMs) {
  var startTimeInMs = Date.now();
  (function loopSearch() {
    if (document.querySelector(selector) != null) {
      callback();
      return;
    }
    else {
      setTimeout(function () {
        if (timeoutInMs && Date.now() - startTimeInMs > timeoutInMs)
          return;
        loopSearch();
      }, checkFrequencyInMs);
    }
  })();
}

가 "", ""로 되어 있는 태그를 .id="div1"1000 밀리초마다.요소가 발견되면 Hi 경보메시지가 표시됩니다.9000밀리초 후에도 요소가 발견되지 않으면 이 함수는 실행을 중지합니다.

파라미터:

  1. selector : 이 . 문자열 : ${selector} }oror 。
  2. callback : 되는 함수 기능 : 요 function function function function function 。
  3. checkFrequencyInMs : 이마다 이 번호 : ${checkFrequencyInMs}입니다.
  4. timeoutInMs: 번 : 옵 : 。${timeoutInMs}입니다.

NB : 실렉터에 대해서는http://https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector 를 참조해 주세요.

저도 같은 문제가 있어서 플러그인을 작성했습니다.

$(selector).waitUntilExists(function);

코드:

;(function ($, window) {

var intervals = {};
var removeListener = function(selector) {

    if (intervals[selector]) {

        window.clearInterval(intervals[selector]);
        intervals[selector] = null;
    }
};
var found = 'waitUntilExists.found';

/**
 * @function
 * @property {object} jQuery plugin which runs handler function once specified
 *           element is inserted into the DOM
 * @param {function|string} handler 
 *            A function to execute at the time when the element is inserted or 
 *            string "remove" to remove the listener from the given selector
 * @param {bool} shouldRunHandlerOnce 
 *            Optional: if true, handler is unbound after its first invocation
 * @example jQuery(selector).waitUntilExists(function);
 */

$.fn.waitUntilExists = function(handler, shouldRunHandlerOnce, isChild) {

    var selector = this.selector;
    var $this = $(selector);
    var $elements = $this.not(function() { return $(this).data(found); });

    if (handler === 'remove') {

        // Hijack and remove interval immediately if the code requests
        removeListener(selector);
    }
    else {

        // Run the handler on all found elements and mark as found
        $elements.each(handler).data(found, true);

        if (shouldRunHandlerOnce && $this.length) {

            // Element was found, implying the handler already ran for all 
            // matched elements
            removeListener(selector);
        }
        else if (!isChild) {

            // If this is a recurring search or if the target has not yet been 
            // found, create an interval to continue searching for the target
            intervals[selector] = window.setInterval(function () {

                $this.waitUntilExists(handler, shouldRunHandlerOnce, true);
            }, 500);
        }
    }

    return $this;
};

}(jQuery, window));

다른 기능을 실행할 수 있도록 요소가 나타날 때까지 대기하기 위해 이 방법을 사용했습니다.

'우리'라고 합시다.doTheRestOfTheStuff(parameters)는 ID를 가진 .the_Element_ID되었거나 로딩이 를 할 수 .사용할 수 있습니다.

var existCondition = setInterval(function() {
 if ($('#the_Element_ID').length) {
    console.log("Exists!");
    clearInterval(existCondition);
    doTheRestOfTheStuff(parameters);
 }
}, 100); // check every 100ms

쉽고 읽기 쉬운 작업 예시로 아직 답이 없다고 생각합니다.Mutation Observer 사용 interfaceDOM을 사용하다

var observer = new MutationObserver(function(mutations) {
    if ($("p").length) {
        console.log("Exist, lets do something");
        observer.disconnect(); 
        //We can disconnect observer once the element exist if we dont want observe more changes in the DOM
    }
});

// Start observing
observer.observe(document.body, { //document.body is node target to observe
    childList: true, //This is a must have for the observer with subtree
    subtree: true //Set to true if changes must also be observed in descendants.
});
            
$(document).ready(function() {
    $("button").on("click", function() {
        $("p").remove();
        setTimeout(function() {
            $("#newContent").append("<p>New element</p>");
        }, 2000);
    });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<button>New content</button>
<div id="newContent"></div>

주의: 스페인어 Mozilla 문서 정보MutationObserver세한것 、 을을해해요요 。

노래는 들을 수 요.DOMNodeInserted ★★★★★★★★★★★★★★★★★」DOMSubtreeModified새로운 요소가 DOM에 추가될 때마다 발생하는 이벤트입니다.

LiveQuery jQuery 플러그인도 있습니다.이 플러그인은 새로운 요소가 작성되었을 때 검출됩니다.

$("#future_element").livequery(function(){
    //element created
});

갱신하다

아래는 약속과 함께 작동하는 업데이트 버전입니다.또한 특정 시행 횟수에 도달한 경우에도 "정지"됩니다.

function _waitForElement(selector, delay = 50, tries = 100) {
    const element = document.querySelector(selector);

    if (!window[`__${selector}`]) {
      window[`__${selector}`] = 0;
      window[`__${selector}__delay`] = delay;
      window[`__${selector}__tries`] = tries;
    }

    function _search() {
      return new Promise((resolve) => {
        window[`__${selector}`]++;
        setTimeout(resolve, window[`__${selector}__delay`]);
      });
    }

    if (element === null) {
      if (window[`__${selector}`] >= window[`__${selector}__tries`]) {
        window[`__${selector}`] = 0;
        return Promise.resolve(null);
      }

      return _search().then(() => _waitForElement(selector));
    } else {
      return Promise.resolve(element);
    }
  }

은 아주 await 해.async★★★★

const start = (async () => {
  const $el = await _waitForElement(`.my-selector`);
  console.log($el);
})();

오래된 버전

원하는 셀렉터를 추가하기만 하면 됩니다.요소가 발견되면 콜백 함수로 에 액세스할 수 있습니다.

const waitUntilElementExists = (selector, callback) => {
const el = document.querySelector(selector);

if (el){
    return callback(el);
}

setTimeout(() => waitUntilElementExists(selector, callback), 500);
}

waitUntilElementExists('.wait-for-me', (el) => console.log(el));

할수있습니다

$('#yourelement').ready(function() {

});

이 기능은 서버에서 요청 시 요소가 DOM에 있는 경우에만 작동합니다.요소가 JavaScript를 통해 동적으로 추가될 경우 해당 요소가 작동하지 않으므로 다른 답변을 확인해야 할 수 있습니다.

jQuery를 사용한 간단한 접근법으로는 다음과 같은 것이 잘 작동한다는 것을 알 수 있습니다.

  // Wait for element to exist.
  function elementLoaded(el, cb) {
    if ($(el).length) {
      // Element is now loaded.
      cb($(el));
    } else {
      // Repeat every 500ms.
      setTimeout(function() {
        elementLoaded(el, cb)
      }, 500);
    }
  };

  elementLoaded('.element-selector', function(el) {
    // Element is ready to use.
    el.click(function() {
      alert("You just clicked a dynamically inserted element");
    });
  });

여기서는 단순히 500ms마다 요소가 로딩되어 있는지 확인하고, 로딩되어 있으면 사용할 수 있습니다.

이것은 문서에 동적으로 추가된 요소에 클릭 핸들러를 추가할 때 특히 유용합니다.

삽입은 어떻습니까?라이브러리 쿼리?

삽입쿼리는 지정된 셀렉터에 연결된 CSS Animation 콜백을 사용하여 요소 작성 시 콜백을 실행합니다.이 메서드를 사용하면 처음뿐만 아니라 요소가 생성될 때마다 콜백을 실행할 수 있습니다.

github에서:

표시되는 노드를 포착하는 비돔 이벤트 방식.그리고 셀렉터를 사용합니다.

이것은 단순히 더 넓은 브라우저 지원을 위한 것이 아니라, 특정 부분에서는 DOMMutationObserver보다 더 나을 수 있습니다.

왜요?

  • DOM 이벤트가 발생하면 브라우저와 삽입 속도가 느려지기 때문입니다.쿼리가 실행되지 않음
  • DOM Mutation Observer는 삽입보다 브라우저 지원이 적기 때문에쿼리
  • 왜냐하면 삽입하면쿼리 성능 오버헤드 없이 선택기를 사용하여 DOM 변경 사항을 필터링할 수 있습니다.

폭넓은 지원!

IE10+ 및 기타 대부분 (모바일 포함)

다음은 MutationObserver에 대한 얇은 래퍼 역할을 하는 함수입니다.유일한 요건은 브라우저가 MutationObserver를 지원하는 것입니다.JQuery에는 의존하지 않습니다.다음 스니펫을 실행하여 작업 예를 확인하십시오.

function waitForMutation(parentNode, isMatchFunc, handlerFunc, observeSubtree, disconnectAfterMatch) {
  var defaultIfUndefined = function(val, defaultVal) {
    return (typeof val === "undefined") ? defaultVal : val;
  };

  observeSubtree = defaultIfUndefined(observeSubtree, false);
  disconnectAfterMatch = defaultIfUndefined(disconnectAfterMatch, false);

  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      if (mutation.addedNodes) {
        for (var i = 0; i < mutation.addedNodes.length; i++) {
          var node = mutation.addedNodes[i];
          if (isMatchFunc(node)) {
            handlerFunc(node);
            if (disconnectAfterMatch) observer.disconnect();
          };
        }
      }
    });
  });

  observer.observe(parentNode, {
    childList: true,
    attributes: false,
    characterData: false,
    subtree: observeSubtree
  });
}

// Example
waitForMutation(
  // parentNode: Root node to observe. If the mutation you're looking for
  // might not occur directly below parentNode, pass 'true' to the
  // observeSubtree parameter.
  document.getElementById("outerContent"),
  // isMatchFunc: Function to identify a match. If it returns true,
  // handlerFunc will run.
  // MutationObserver only fires once per mutation, not once for every node
  // inside the mutation. If the element we're looking for is a child of
  // the newly-added element, we need to use something like
  // node.querySelector() to find it.
  function(node) {
    return node.querySelector(".foo") !== null;
  },
  // handlerFunc: Handler.
  function(node) {
    var elem = document.createElement("div");
    elem.appendChild(document.createTextNode("Added node (" + node.innerText + ")"));
    document.getElementById("log").appendChild(elem);
  },
  // observeSubtree
  true,
  // disconnectAfterMatch: If this is true the hanlerFunc will only run on
  // the first time that isMatchFunc returns true. If it's false, the handler
  // will continue to fire on matches.
  false);

// Set up UI. Using JQuery here for convenience.

$outerContent = $("#outerContent");
$innerContent = $("#innerContent");

$("#addOuter").on("click", function() {
  var newNode = $("<div><span class='foo'>Outer</span></div>");
  $outerContent.append(newNode);
});
$("#addInner").on("click", function() {
  var newNode = $("<div><span class='foo'>Inner</span></div>");
  $innerContent.append(newNode);
});
.content {
  padding: 1em;
  border: solid 1px black;
  overflow-y: auto;
}
#innerContent {
  height: 100px;
}
#outerContent {
  height: 200px;
}
#log {
  font-family: Courier;
  font-size: 10pt;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h2>Create some mutations</h2>
<div id="main">
  <button id="addOuter">Add outer node</button>
  <button id="addInner">Add inner node</button>
  <div class="content" id="outerContent">
    <div class="content" id="innerContent"></div>
  </div>
</div>
<h2>Log</h2>
<div id="log"></div>

여기 바닐라 Javascript로 된 Promise-Return 솔루션이 있습니다(잡동사니 콜백 없음).기본적으로는 200ms마다 체크됩니다.

function waitFor(selector) {
    return new Promise(function (res, rej) {
        waitForElementToDisplay(selector, 200);
        function waitForElementToDisplay(selector, time) {
            if (document.querySelector(selector) != null) {
                res(document.querySelector(selector));
            }
            else {
                setTimeout(function () {
                    waitForElementToDisplay(selector, time);
                }, time);
            }
        }
    });
}

여기 어떤 것도 기다릴 수 있는 순수 자바스크립트 기능이 있습니다.CPU 리소스를 적게 사용하도록 간격을 더 길게 설정합니다.

/**
 * @brief Wait for something to be ready before triggering a timeout
 * @param {callback} isready Function which returns true when the thing we're waiting for has happened
 * @param {callback} success Function to call when the thing is ready
 * @param {callback} error Function to call if we time out before the event becomes ready
 * @param {int} count Number of times to retry the timeout (default 300 or 6s)
 * @param {int} interval Number of milliseconds to wait between attempts (default 20ms)
 */
function waitUntil(isready, success, error, count, interval){
    if (count === undefined) {
        count = 300;
    }
    if (interval === undefined) {
        interval = 20;
    }
    if (isready()) {
        success();
        return;
    }
    // The call back isn't ready. We need to wait for it
    setTimeout(function(){
        if (!count) {
            // We have run out of retries
            if (error !== undefined) {
                error();
            }
        } else {
            // Try again
            waitUntil(isready, success, error, count -1, interval);
        }
    }, interval);
}

예를 들어 jQuery에서 호출하려면 다음과 같이 사용합니다.

waitUntil(function(){
    return $('#myelement').length > 0;
}, function(){
    alert("myelement now exists");
}, function(){
    alert("I'm bored. I give up.");
});

다음과 같이 시험해 보십시오.

const wait_until_element_appear = setInterval(() => {
    if ($(element).length !== 0) {
        // some code
        clearInterval(wait_until_element_appear);
    }
}, 0);

이 솔루션은 나에게 매우 효과적이다.

Tag Manager에는 보통 다음 스니펫을 사용합니다.

<script>
(function exists() {
  if (!document.querySelector('<selector>')) {
    return setTimeout(exists);
  }
  // code when element exists
})();  
</script>

observe아래 함수를 사용하면 셀렉터를 통해 요소를 청취할 수 있습니다.

다음 예제에서는 2초 경과 후.greeting에 삽입됩니다..container이 요소의 삽입을 듣고 있기 때문에 삽입 시 트리거되는 콜백을 할 수 있습니다.

const observe = (selector, callback, targetNode = document.body) =>
  new MutationObserver(mutations => [...mutations]
    .flatMap((mutation) => [...mutation.addedNodes])
    .filter((node) => node.matches && node.matches(selector))
    .forEach(callback))
  .observe(targetNode, { childList: true, subtree: true });

const createGreeting = () => {
  const el = document.createElement('DIV');
  el.textContent = 'Hello World';
  el.classList.add('greeting');
  return el;
};

const container = document.querySelector('.container');

observe('.greeting', el => console.log('I have arrived!', el), container);

new Promise(res => setTimeout(() => res(createGreeting()), 2000))
  .then(el => container.appendChild(el));
html, body { width: 100%; height: 100%; margin: 0; padding: 0; }
body { display: flex; }
.container { display: flex; flex: 1; align-items: center; justify-content: center; }
.greeting { font-weight: bold; font-size: 2em; }
<div class="container"></div>

를 반환하는 솔루션Promise타임아웃(호환 IE 11+)을 사용할 수 있습니다.

단일 요소(유형 요소):

"use strict";

function waitUntilElementLoaded(selector) {
    var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;

    var start = performance.now();
    var now = 0;

    return new Promise(function (resolve, reject) {
        var interval = setInterval(function () {
            var element = document.querySelector(selector);

            if (element instanceof Element) {
                clearInterval(interval);

                resolve();
            }

            now = performance.now();

            if (now - start >= timeout) {
                reject("Could not find the element " + selector + " within " + timeout + " ms");
            }
        }, 100);
    });
}

여러 요소(NodeList 유형):

"use strict";

function waitUntilElementsLoaded(selector) {
    var timeout = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;

    var start = performance.now();
    var now = 0;

    return new Promise(function (resolve, reject) {
        var interval = setInterval(function () {
            var elements = document.querySelectorAll(selector);

            if (elements instanceof NodeList) {
                clearInterval(interval);

                resolve(elements);
            }

            now = performance.now();

            if (now - start >= timeout) {
                reject("Could not find elements " + selector + " within " + timeout + " ms");
            }
        }, 100);
    });
}

예:

waitUntilElementLoaded('#message', 800).then(function(element) {
    // element found and available

    element.innerHTML = '...';
}).catch(function() {
    // element not found within 800 milliseconds
});

waitUntilElementsLoaded('.message', 10000).then(function(elements) {
    for(const element of elements) {
        // ....
    }
}).catch(function(error) {
    // elements not found withing 10 seconds
});

요소 목록과 단일 요소 모두에 대해 작동합니다.

Jamie Hutber's에서 영감을 얻어 답을 개발했습니다.

약속 기반 기능으로 다음을 설정할 수 있습니다.

  • 최대 시도 횟수 - 기본값10;
  • 지연(밀리초) - 기본값100 ms.

따라서 기본적으로는 요소가 DOM에 표시될 때까지1초 기다립니다

이 메시지가 표시되지 않으면 A가 반환됩니다.promise.reject와 함께null원하는 대로 오류를 처리할 수 있습니다.

코드

export function _waitForElement(selector, delay = 10, tries = 100) {
  const element = document.querySelector(selector);


  if (!window[`__${selector}`]) {
    window[`__${selector}`] = 0;
    window[`__${selector}__delay`] = delay;
    window[`__${selector}__tries`] = tries;
  }

  function _search() {
    return new Promise((resolve) => {
      window[`__${selector}`]++;
      setTimeout(resolve, window[`__${selector}__delay`]);
    });
  }

  if (element === null) {
    if (window[`__${selector}`] >= window[`__${selector}__tries`]) {
      window[`__${selector}`] = 0;
      return Promise.resolve(null);
    }

    return _search().then(() => _waitForElement(selector));
  } else {
    return Promise.resolve(element);
  }
}

사용방법:

async function wait(){
    try{
        const $el = await waitForElement(".llama");
        console.log($el);
    } catch(err){
        console.error("Timeout - couldn't find element.")
    }
} 

wait();

위의 예에서는 셀렉터를 기다립니다..llama. StackoverFlow 콘솔에서 더 많은 지연을 추가하여 테스트할 수 있습니다.

클래스만 추가합니다.llamaDOM 상의 임의의 요소에 접속할 수 있습니다.

MutationObserver를 사용한 보다 명확한 예:

new MutationObserver( mutation => {
    if (!mutation.addedNodes) return
    mutation.addedNodes.forEach( node => {
        // do stuff with node
    })
})

잠시 후(타임아웃) 검색을 중지하려면 다음 jQuery가 작동합니다.10초 후에 타임아웃 됩니다.이름을 통한 입력을 선택해야 하고 다른 솔루션 구현에 어려움을 겪었기 때문에 순수 JS가 아닌 이 코드를 사용해야 했습니다.

 // Wait for element to exist.

    function imageLoaded(el, cb,time) {

        if ($(el).length) {
            // Element is now loaded.

            cb($(el));

            var imageInput =  $('input[name=product\\[image_location\\]]');
            console.log(imageInput);

        } else if(time < 10000) {
            // Repeat every 500ms.
            setTimeout(function() {
               time = time+500;

                imageLoaded(el, cb, time)
            }, 500);
        }
    };

    var time = 500;

    imageLoaded('input[name=product\\[image_location\\]]', function(el) {

     //do stuff here 

     },time);

가능한 한 돌연변이 관찰자를 피하려고 해요 그래서 이렇게 된 거예요위의 다른 답변과 비슷합니다.이 함수는 소정의 DOM 콜 내에 존재하는 첫 번째 요소(className은 예상되는 사용법이지만 tagName 또는 Id를 받아들일 수도 있습니다)를 검색합니다.특정 클래스 이름 또는 tagname을 가진 요소가 로드된 경우 정확한 인덱스에 대한 인수를 추가할 수도 있습니다.

    async function waitUntilElementExits(domkey,domquery,maxtime){
        const delay = (ms) => new Promise(res => setTimeout(res, ms));
        for(let i=0; i<maxtime; i=i+200){
            await delay(200);
            let elm = document[domkey](domquery);
            if( (domkey == 'getElementById' && elm) || elm?.[0] ) break;
        }
    }
    // usage
    await waitUntilElementExits('getElementByClassName','some_class_name',10000)

심플한 Javascript.

cont elementExist = setInterval(() => {
    var elm = document.getElementById("elementId")
    if (elm!=null)
         // call your function here to do something
        clearInterval(elementExist);
    }
}, 100);

주의: 이렇게 하면 다른 실행이 차단됩니다.

언급URL : https://stackoverflow.com/questions/5525071/how-to-wait-until-an-element-exists

반응형