なるべくスマートに要素の出現を待つ【javascript】

2023/03/26に公開

はじめに

Chromeの拡張機能など書いているときに、querySelector等で要素を取得したいときがあります。
静的なHTMLページでは、素直にquerySelectorすればいいわけですが、動的なページでやるとnullが帰ってくる場合があります。実行時点ではその要素がDOMに存在しないからです。

document.querySelector(".dynamicElement"); //null

普通に書く

出現するまで定期的に要素があるかチェックする必要があるわけですが、setTimeout()で普通に書くとこうなります(めんどくさかったのでChatGPTに書かせた)

function waitForElementToDisplay(selector, time, callback) {
    if(document.querySelector(selector)!=null) {
        callback();
        return;
    }
    else {
        setTimeout(function() {
            waitForElementToDisplay(selector, time, callback);
        }, time);
    }
}

自分のリポジトリからも、
https://github.com/doma-itachi/Youtube-shorts-block/blob/master/src/main.js#L54-L71
こんな感じで書けるわけですがスマートじゃない!コールバック嫌い!

awaitが使えたらなぁ

awaitが使えれば、関数に待つ処理をまとめた上で、コールバックも使わず1行で書けるかも

let element=await waitQuerySelector(".dynamicElement");
element.click();

async function waitQuerySelector(){...}

実装

async function waitQuerySelector(selector, node=document){
    let obj=null;
    while(!obj){
        obj=await new Promise(resolve=>setTimeout(()=>resolve(node.querySelector(selector), 100)));
    }
    return obj;
}

セレクタに要素が引っかかるまでawaitするquerySelectorです。
間隔はとりあえず100にしてあります。
第一引数にセレクタを指定します。
第二引数には、Elementが指定されればElement.querySelectorを呼び出します。未指定の場合はdocument.querySelectorが呼び出されます。

whileループの中では

null
null
null
null
<div class="dynamicElement">...</div>

みたいになってます。

上限を決めるときも

上のコードは、絶対に要素が出現することを前提として書いたので、上限を設定していませんが、whileのところをforに変えるだけで上限を設定できます。

+for(let i=0;i<10&&!obj;i++){
-while(!obj){

思いつきで書いたので間違ってたら訂正ください

Discussion