programing

동적으로 로드된 스크립트를 기다리는 중

padding 2023. 10. 25. 23:07
반응형

동적으로 로드된 스크립트를 기다리는 중

내 페이지 본문에 AJAX 호출의 결과로 다음 코드를 삽입해야 합니다.

    <p>Loading jQuery</p>
    <script type='text/javascript' src='scripts/jquery/core/jquery-1.4.4.js'></script>
    <p>Using jQuery</p>
    <script type='text/javascript'>
        $.ajax({
            ...
        });
    </script>

사용할 수 없습니다.$.load()문서가 이미 로드되었기 때문에 이벤트가 발생하지 않습니다.

안전한가요?만약 그렇지 않다면, 내 커스텀, 생성된 코드가 실행되기 전에 jquery 스크립트가 로드되었는지 확인하려면 어떻게 해야 합니까?

쿼리할 수 있도록 스크립트 파일에 ID를 추가합니다.

<script id="hljs" async src="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.0.0/highlight.min.js"></script>

그런 다음 자바스크립트로 load listener를 추가합니다.

<script>
  var script = document.querySelector('#hljs');
  script.addEventListener('load', function() {
    hljs.initHighlightingOnLoad(); 
  });
</script>

꽤 안전합니다.역사적으로.<script>태그는 완전 차단이므로 두번째.<script>전자가 구문 분석/실행을 완료하기 전에는 태그를 찾을 수 없습니다.유일한 문제는 "현대의" 브라우저가 스크립트를 비동기적으로 로드하고 지연시키는 경향이 있다는 것입니다.따라서 순서가 올바른지 확인하려면 다음과 같이 사용합니다.

<p>Loading jQuery</p>
<script type='text/javascript' async=false defer=false src='scripts/jquery/core/jquery-1.4.4.js'></script>
<p>Using jQuery</p>
<script type='text/javascript'>
    $.ajax({
        ...
    });
</script>

그러나 이것을 HTML 문자열로 DOM에 밀어 넣는 대신 동적 스크립트 태그 삽입을 사용하는 것이 더 나을 것입니다.같은 이야기일 것입니다.

var scr  = document.createElement('script'),
    head = document.head || document.getElementsByTagName('head')[0];
    scr.src = 'scripts/jquery/core/jquery-1.4.4.js';
    scr.async = false; // optionally

head.insertBefore(scr, head.firstChild);
const jsScript = document.createElement('script')
jsScript.src =
  'https://coolJavascript.js'

jsScript.addEventListener('load', () => {
  doSomethingNow()
})

document.body.appendChild(jsScript)

스크립트가 동적으로 추가된 후 로드됩니다.

여러 스크립트가 로드될 때까지 대기

다음 도우미는 여러 스크립트를 한 번만 로드하고 약속을 반환합니다.

async function cirosantilli_load_scripts(script_urls) {
    function load(script_url) {
        return new Promise(function(resolve, reject) {
            if (cirosantilli_load_scripts.loaded.has(script_url)) {
                resolve();
            } else {
                var script = document.createElement('script');
                script.onload = resolve;
                script.src = script_url
                document.head.appendChild(script);
            }
        });
    }
    var promises = [];
    for (const script_url of script_urls) {
        promises.push(load(script_url));
    }
    await Promise.all(promises);
    for (const script_url of script_urls) {
        cirosantilli_load_scripts.loaded.add(script_url);
    }
}
cirosantilli_load_scripts.loaded = new Set();

(async () => {
    await cirosantilli_load_scripts([
        'https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/1.3.8/FileSaver.min.js',
        'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.min.js',
    ]);

    // Now do stuff with those scripts.

})();

GitHub 업스트림: 정의사용.

크롬 75에서 테스트했습니다.

또한 에 새로운 기능이 있습니다.jQuery 1.6. 라고 합니다.jQuery.holdReady(). 그것은 사실 자기 설명입니다; 당신이 전화할 때.jQuery.holdReady(true),ready호출하기 전에는 이벤트가 실행되지 않습니다.jQuery.holdReady(false)이 상태만 이 값을 false로 설정하면 준비된 이벤트가 자동으로 실행되지 않으며, 홀드만 제거됩니다.

문서에서 가져온 스크립트를 로드하는 비차단 예제는 다음과 같습니다.

$.holdReady(true);
$.getScript("myplugin.js", function() {
     $.holdReady(false);
});

자세한 내용은 http://api.jquery.com/jQuery.holdReady/ 참조

이것은 나에게 알맞습니다.

function loadScript(sources, callBack) {
    var script      = document.createElement('script');
    script.src      = sources;
    script.async    = false; 
    document.body.appendChild(script); 
    
    script.addEventListener('load', () => {
        if(typeof callBack == "function") callBack(sources);
    });
}

제 경우에는 해결책이 효과가 없었습니다.스크립트가 링크에 대한 오른쪽 클릭 이벤트를 로드한 후 프로그램적으로 링크를 클릭하고 싶었습니다.따라서 타임아웃 및 루프 작업을 수행해야 했습니다.

<script>
  var ifrbutton = document.getElementById("id-of-link");
  if(typeof(ifrbutton) != 'undefined' && ifrbutton != null && window.location.hash == "#special-hash") {
    var i = 0;
    // store the interval id to clear in future
    var intr = setInterval(function() {
      if (ifrbutton.onclick!=null) {
        ifrbutton.click();
        clearInterval(intr);
        i = 200;
      }
      if (++i >= 200) clearInterval(intr);
    }, 300)
  }
</script>

따라서 스크립트가 제공하는 특정 기능을 주기적으로 확인하는 것도 해결책이 될 수 있습니다.스크립트가 로드되지 않을 경우를 대비하여 안전한 끝을 설정합니다.나를 위해 안전하게 일함 ;)

    new MutationObserver((mutationsList, observer) => {
        for (var mutation of mutationsList) 
            if (mutation.type === 'childList') 
                Array.from (mutation.addedNodes)
                    .filter (node => node.tagName === 'SCRIPT')
                    .forEach (script => {
                        //Script started loading
                        script.addEventListener ('load', () => { 
                             //Script finished loading
                        })
                    })
    }).observe(document, { attributes: false, childList: true, subtree: true });

Ciro Santilli의 답변은 훌륭합니다.몇 가지 문제가 있어서 조금 개선했습니다.

저는 코드미러 에디터를 기반으로 특별한 양식 필드를 만들었습니다.CodeMirror의 스크립트는 크기가 크기 때문에 필드 초기화를 통해 필요에 따라 로드합니다.

양식에 이러한 필드가 2개인 경우, 스크립트의 로드는 단지 동시에 두 번 시작됩니다. 처음 시작한 스크립트가 아직 로드되지 않았기 때문에 실제로는 두 번 로드되는데, 나쁜 점이 있습니다.

MyNamespace.loadScript(script_url) 및 MyNamespace.loadScripts(script_urls) 함수는 항상 약속을 반환하므로 대기 여부를 결정할 수 있습니다.

  1. 스크립트가 이미 로드된 경우 즉시 해결되는 약속을 반환합니다.
  2. 스크립트가 로드되는 경우 스크립트가 로드되면 해결되는 간격 관찰 약속을 반환합니다.
  3. 스크립트가 로드되지 않은 경우 로드된 이벤트별로 해결되는 로드 스크립트와 함께 약속을 반환합니다.

코드:

MyNamespace.loadScript = function (script_url) {
    console.log("loading: " + script_url);

    if (!MyNamespace.loadedScripts) MyNamespace.loadedScripts = new Set();
    if (!MyNamespace.loadingScripts) MyNamespace.loadingScripts = new Set();

    if (MyNamespace.loadedScripts.has(script_url)) {
        return new Promise(function (resolve, reject) {
            console.log("already loaded");
            resolve();
        });
    }

    if (MyNamespace.loadingScripts.has(script_url)) {
        return new Promise(function (resolve, reject) {
            console.log("loading in progress");

            let interval = setInterval(function () {
                if (MyNamespace.loadedScripts.has(script_url)) {
                    clearInterval(interval);
                    console.log("waited until loaded");
                    resolve();
                }
            }, 200);
        });
    }

    MyNamespace.loadingScripts.add(script_url);

    return new Promise(function (resolve, reject) {
        let script = document.createElement('script');
        script.src = script_url;

        script.onload = function () {
            console.log("actually loaded");
            MyNamespace.loadedScripts.add(script_url);
            resolve();
        };

        script.onerror = function () {
            console.log("load error");
            reject(new Error("Error by loading the script " + script_url));
        };

        document.head.append(script);
    });
};

MyNamespace.loadScripts = function (script_urls) {
    let promises = [];
    for (let script_url of script_urls) {
        promises.push(MyNamespace.loadScript(script_url));
    }

    return Promise.all(promises);
};

$.ajax과 $.load는 동일합니다.둘 중 하나를 사용할 수 있습니다.$.load를 페이지의 스크립트 태그에 입력하면 $.ajax()와 마찬가지로 로드 시 실행됩니다.

토말락이 말한 것이 사실입니다. 당신이 무언가를 하기 전에 대본이 발사되기를 기다리는 것에 관해서라면 말입니다.비동기 호출은 예외입니다.자바스크립트는 AJAX 호출을 한 후 스크립트를 계속 실행하고 AJAX 호출이 응답하면 사용자가 지시하는 작업에 따라 성공/실패를 실행합니다.

이제 JS가 한 번의 콜로 성공 콜백에서 모든 것을 마무리하는 것이 매우 간단합니다. 그렇지 않으면 $.delered를 사용할 수 있습니다.이를 통해 수많은 아약스 콜 또는 하나의 콜을 수행한 다음 종료 후에만 코드를 실행할 수 있습니다.

$.when &$.이 경우에는 아마도 이 상황에 가장 적합할 것입니다.

당신이 하고 있는 일이 무엇이든 안전합니다.

언급URL : https://stackoverflow.com/questions/7308908/waiting-for-dynamically-loaded-script

반응형