【问题标题】:JavaScript loading order causing issuesJavaScript 加载顺序导致问题
【发布时间】:2012-09-05 22:53:15
【问题描述】:

我在标题中有一个 JS 文件(这是用于 Google DFP),另一个在 </body> 之前

我发现如果头 JS 文件在底部文件之前没有加载,我在 Chrome 控制台中收到此错误:

 Uncaught TypeError: Object #<Object> has no method 'defineSlot' 

defineSlot 在第一个脚本中定义。此问题仅在大约每 10 次页面刷新时发生,因此大部分时间都可以。

我想就如何处理这个问题提出建议。以下是 2 个脚本:

标题脚本:

<script type='text/javascript'>
var googletag = googletag || {};
googletag.cmd = googletag.cmd || [];
(function() {
    var gads = document.createElement('script');
    gads.async = true;
    gads.type = 'text/javascript';
    var useSSL = 'https:' == document.location.protocol;
    gads.src = (useSSL ? 'https:' : 'http:') + 
    '//www.googletagservices.com/tag/js/gpt.js';
    var node = document.getElementsByTagName('script')[0];
    node.parentNode.insertBefore(gads, node);
})();
</script>

<script type='text/javascript'>
googletag.cmd.push(function() {
    googletag.pubads().enableAsyncRendering();
    slot1 = googletag.defineSlot('/21848415/GoneGlobal_Square', [250, 250], 'doubleClickSquareIndex-250-250').addService(googletag.pubads());
    slot2 = googletag.defineSlot('/21848415/GoneGlobal_Skyscraper', [160, 600], 'doubleClickSkyscraperIndex-160-600').addService(googletag.pubads());
    slot3 = googletag.defineSlot('/21848415/GoneGlobal_Leaderboard', [728, 90], 'doubleClickLeaderboardIndex-728-90').addService(googletag.pubads());
    slot4 = googletag.defineSlot('/21848415/GoneGlobal_Leaderboard', [728, 90], 'doubleClickLeaderboardHeaderInternal-728-90').addService(googletag.pubads());
    slot5 = googletag.defineSlot('/21848415/GoneGlobal_SmallSquare', [200, 200], 'doubleClickSmallSquareHeaderExternal-200-200').addService(googletag.pubads());
    googletag.enableServices();
});
</script>

下面是第二个脚本 - 这只是它的一部分(它很长):

$(function() {
    ///////////////////////// Double Click AD Variables
    var slot1 = googletag.defineSlot('/21848415/GoneGlobal_Square', [250, 250], 'doubleClickSquareIndex-250-250');
    var slot2 = googletag.defineSlot('/21848415/GoneGlobal_Skyscraper', [160, 600], 'doubleClickSkyscraperIndex-160-600');
    var slot3 = googletag.defineSlot('/21848415/GoneGlobal_Leaderboard', [728, 90], 'doubleClickLeaderboardIndex-728-90');
    var slot4 = googletag.defineSlot('/21848415/GoneGlobal_Leaderboard', [728, 90], 'doubleClickLeaderboardHeaderInternal-728-90');
    var slot5 = googletag.defineSlot('/21848415/GoneGlobal_SmallSquare', [200, 200], 'doubleClickSmallSquareHeaderExternal-200-200');
)};

有没有办法阻止脚本运行另一个加载的方法/函数?

我可以创建某种依赖关系吗?

如何保证top JS先于bottom加载?

【问题讨论】:

  • 标题是指&lt;head&gt;?如果是这样,它应该始终在内容加载之前加载 (&lt;body&gt;)。
  • @PeeHaa 第一个脚本异步注入第二个。
  • 你不能禁用async吗? @OP

标签: javascript jquery


【解决方案1】:

此处的其他解决方案可能有效,但真正的问题在于您使用 DFP 的方式。

DFP 是异步加载的,因此在执行导致错误的脚本时,您的页面上将不存在方法 defineSlot。

您的所有 DFP 代码都应包含在 googletag.cmd.push 包装器中,该包装器将存储命令,直到加载 DFP,然后以正确的顺序执行它们。

像这样简单地包装任何 DFP 代码,应该可以解决您的错误:

googletag.cmd.push(function() {
    // Double Click AD Variables

});

【讨论】:

    【解决方案2】:

    将您的脚本存储在单独的文件中。然后像下面这样的东西应该可以工作:

    function loadScript(pathToScript, callback) {
        var head = document.getElementsByTagName("head")[0];
        var script = document.createElement("script");
    
        script.type = "text/javascript";
        script.src = pathToScript + "?t=" + new Date().getTime(); //prevent caching
    
        if (callback) {
            script.onload = callback;
        }
    
        head.appendChild(script);
    }
    

    loadScript 函数加载驻留在“pathToScript”处的脚本,一旦完成加载脚本,它就会调用提供的回调。您需要回调,因为加载外部资源是异步操作。这意味着当它完成加载外部资源时,您基本上需要一个“通知”。这基本上就是回调的作用。如果您依赖来自异步操作的数据,则不能触发操作然后继续尝试使用数据或资源,因为数据尚未完成加载。因此,任何依赖于资源的代码都放在回调中。我们为什么要这样做,稍后会更清楚。

    var scripts = ["/path/to/first/script.js", "/path/to/second/script.js"];    
    function load(i) {
        if (i < scripts.length) {
            loadScript(scripts[i], function () {
                load(++i);
            });
        } else {
            //all your scripts have loaded, so go ahead and do what you need to do
        }
    }
    
    load(0);
    

    scripts 数组包含要加载的脚本数组。该顺序由脚本在数组中的位置暗示。因此,scripts[0] 的脚本将在 scripts[1] 的脚本之前加载。

    load 函数可确保您以所需的顺序加载脚本。我们知道加载操作是异步的。我们还知道,一旦操作完成,就会调用回调。使用该信息,我们可以在第一个脚本完成加载后加载第二个脚本。 load 有一个参数,表示scripts 数组的当前索引。我们最初将索引i 设置为0,这意味着我们将首先在索引0 处加载脚本。然后我们查看i 的值。如果它小于数组的长度,则意味着我们还没有完成所有脚本(将其视为一个循环)。所以如果i小于长度,我们调用loadScript函数,其值为scripts[i]。然后在我们的回调中,我们再次调用我们的load 函数,但这一次我们增加了i 的当前值。因此,当脚本完成加载后,我们最终会使用递增的索引调用 load,因此我们现在可以加载我们的下一个脚本。

    如果i 等于scripts.length,这意味着我们已经加载了所有脚本,所以我们将进入else 块,这就是你放置需要运行的代码的地方脚本已加载。

    最后一行是对load的实际调用,带有参数0,它从第一个脚本的路径开始整个过程​​,该脚本位于scripts数组的索引0处.

    如果您愿意,您也可以将 load 函数重写为 IIFE:

    (function load(i) {
        if (i < resources.length) {
            loadResource(resources[i], function () {
                load(++i);
            });
        }
    })(0);
    

    这里有一个“自调用”函数,它在回调中调用自身。它在功能上等同于上述不那么特别的版本。

    【讨论】:

      【解决方案3】:

      您可以创建自己的事件,比如说first_script_loaded,当第一个脚本加载时,您可以在末尾添加$(document).trigger("first_script_loaded"),您可能还想为此设置一个全局boolean,它的设置位置开头为false,当加载第一个脚本时,将其设置为true

      在第二个脚本中,检查boolean是否为true,表示第一个脚本在开始加载第二个脚本之前已经加载完毕,如果为false,则监听$(document).on("first_script_loaded")事件并将您的代码添加到on 方法的callback 中。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-12-24
        • 2010-09-06
        • 1970-01-01
        • 1970-01-01
        • 2018-03-15
        • 2017-03-01
        • 1970-01-01
        相关资源
        最近更新 更多