只要第三方脚本无法加载,就运行一段JavaScript

Jam*_*arp 6 html javascript asynchronous

我为几个网站提供了一个JavaScript小部件,它们是异步加载的.我的小部件又需要加载由我控制之外的另一方提供的脚本.

有几种方法可以检查脚本是否已成功加载.但是,如果脚本加载失败,我还需要运行不同的代码.

显而易见的工具包括:

  • 我不愿意使用JavaScript库,比如jQuery.我需要一个非常小的脚本来最小化我对使用我的小部件的网站的影响.

  • 我想尽快检测到故障,因此使用计时器进行轮询是不可取的.不过,我不介意使用计时器作为旧浏览器的最后手段.

  • 我发现<script>标签的onerror事件在某些主流浏览器中是不可靠的.(这似乎取决于安装了哪些附加组件.)

  • 任何涉及的事情document.write都是正确的.(除了那种本质上是邪恶的方法之外,我的代码是异步加载的,因此document.write可能会对页面产生不良影响.)

我有一个以前的解决方案,涉及加载<script>一个新的<iframe>.在那个iframe中,我设置了一个<body onload=...>事件处理程序,用于检查<script onload=...>事件是否已经被触发.因为它<script>是初始文档的一部分,以后不是异步注入的,所以onload只有在使用<script>标记完成网络层之后才会触发.

但是,现在我需要在父文档中加载脚本; 它不能再在iframe中了.因此,一旦网络层放弃尝试获取脚本,我就需要一种不同的方式来触发代码.

我读到了" 深入挖掘脚本加载的阴暗水域 ",试图弄清楚我可以依赖浏览器的订购保证.

如果我理解那里记录的技术:

  • 我需要将我的故障处理代码放在一个单独的.js文件中.
  • 然后,在某些浏览器上,我可以确保我的代码仅在第三方脚本运行或失败后运行.这要求浏览器支持:
    • <script async>通过DOM 将属性设置为false,
    • <script onreadystatechange=...>在IE 6+上使用.

尽管查看了异步支持表,但我无法确定是否可以依赖足够的浏览器中的脚本排序来实现这一点.

那么如何在加载我无法控制的脚本时可靠地处理故障呢?

Jam*_*arp 2

我相信我已经解决了我提出的问题,尽管事实证明这并没有解决我实际遇到的问题。那好吧。这是我的解决方案:

我们希望在浏览器尝试加载第三方脚本后运行一些代码,以便检查它是否加载成功。我们通过限制后备脚本的加载仅在第三方脚本运行或失败后才发生来实现这一点。然后,后备脚本可以检查第三方脚本是否创建了它应该创建的全局变量。

受http://www.html5rocks.com/en/tutorials/speed/script-loading/启发的跨浏览器按顺序脚本加载。

var fallbackLoader = doc.createElement(script),
    thirdPartyLoader = doc.createElement(script),
    thirdPartySrc = '<URL to third party script>',
    firstScript = doc.getElementsByTagName(script)[0];

// Doesn't matter when we fetch the fallback script, as long as
// it doesn't run early, so just set src once.
fallbackLoader.src = '<URL to fallback script>';

// IE starts fetching the fallback script here.

if('async' in firstScript) {
    // Browser support for script.async:
    // http://caniuse.com/#search=async
    //
    // By declaring both script tags non-async, we assert
    // that they need to run in the order that they're added
    // to the DOM.
    fallbackLoader.async = thirdPartyLoader.async = false;
    thirdPartyLoader.src = thirdPartySrc;
    doc.head.appendChild(thirdPartyLoader);
    doc.head.appendChild(fallbackLoader);
} else if(firstScript.readyState) {
    // Use readyState for IE 6-9. (IE 10+ supports async.)
    // This lets us fetch both scripts but refrain from
    // running them until we know that the fetch attempt has
    // finished for the first one.
    thirdPartyLoader.onreadystatechange = function() {
        if(thirdPartyLoader.readyState == 'loaded') {
            thirdPartyLoader.onreadystatechange = null;
            // The script-loading tutorial comments:
            // "can't just appendChild, old IE bug
            // if element isn't closed"
            firstScript.parentNode.insertBefore(thirdPartyLoader, firstScript);
            firstScript.parentNode.insertBefore(fallbackLoader, firstScript);
        }
    };
    // Don't set src until we've attached the
    // readystatechange handler, or we could miss the event.
    thirdPartyLoader.src = thirdPartySrc;
} else {
    // If the browser doesn't support async or readyState, we
    // just won't worry about the case where script loading
    // fails. This is <14% of browsers worldwide according to
    // caniuse.com, and hopefully script loading will succeed
    // often enough for them that this isn't a problem.
    //
    // If that isn't good enough, you might try setting an
    // onerror listener in this case. That still may not work,
    // but might get another small percentage of old browsers.
    // See
    // http://blog.lexspoon.org/2009/12/detecting-download-failures-with-script.html
    thirdPartyLoader.src = thirdPartySrc;
    firstScript.parentNode.insertBefore(thirdPartyLoader, firstScript);
}
Run Code Online (Sandbox Code Playgroud)