4

我在标题中有一个 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');
)};

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

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

如何确保在底部之前加载顶部 JS?

4

3 回答 3

6

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

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

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

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

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

});
于 2012-09-06T14:13:33.323 回答
4

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

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数组包含要加载的脚本数组。顺序由脚本在数组中的位置暗示。因此, at 的脚本scripts[0]将在 at 的脚本之前加载scripts[1]

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

如果i等于scripts.length,则表示我们已经加载了所有脚本,因此我们将进入else块,这是您放置所有脚本加载后需要运行的代码的地方。

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

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

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

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

于 2012-09-05T23:17:49.193 回答
0

您可以创建自己的事件,比如说first_script_loaded,当第一个脚本被加载时,您可以在最后添加$(document).trigger("first_script_loaded"),您可能还想为此设置一个全局boolean,它false在开始时设置为,当第一个脚本被加载时, 你把它设置为true

在第二个脚本中,检查是否booleantrue,这意味着在开始加载第二个脚本之前已经加载了第一个脚本,但如果它是 false,则监听事件并在方法$(document).on("first_script_loaded")中添加您的代码。callbackon

于 2012-09-05T23:08:47.743 回答