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

时间:2013-11-26 02:18:01

标签: javascript html asynchronous

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

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

不起作用的显而易见的工具包括:

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

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

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

  • 任何涉及document.write的内容都是正确的。 (除了该方法本质上是邪恶的,我的代码是异步加载的,因此document.write可能会对页面造成不良影响。)

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

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

我读了“Deep dive into the murky waters of script loading”,试图弄清楚我可以依赖浏览器的订购保证。

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

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

尽管看了async support table,我还是无法判断我是否可以在足够的浏览器中依赖脚本排序来实现这一点。

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

2 个答案:

答案 0 :(得分: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);
}

答案 1 :(得分:0)

您是否考虑过使用窗口onerror处理程序?这将让您检测大多数错误发生的时间,然后您可以采取适当的措施。作为任何未被捕获的问题的后备,您还可以使用try / catch保护自己的代码。

您还应检查实际加载的第三方脚本:

<script type="text/javascript" onload="loaded=1" src="thirdparty.js"></script>

然后检查它是否已加载:

window.onload = function myLoadHandler() {
  if (loaded == null) {
    // The script doesn't exist or couldn't be loaded!
  }
}

您可以使用url参数检查导致错误的脚本。

window.onerror = function myErrorHandler(errorMsg, url, lineNumber) {
  if (url == third_party_script_url) {
    // Do alternate code
  } else {
    return false; // Do default error handling
  }
}