【问题标题】:YepNope/Modernizr callbacks with global JavaScript variables and Internet Explorer带有全局 JavaScript 变量和 Internet Explorer 的 YepNope/Modernizr 回调
【发布时间】:2013-12-12 00:26:15
【问题描述】:

谁能解释为什么在 Internet Explorer 中,代码示例 1 不起作用而代码示例 2 起作用?

代码 1(非功能性)

Modernizr.load([
    {
        load: [
            '../includes/css/foo.css',
            '../includes/js/foo.js',
            '../includes/js/bar.js'
        ],
        complete: function() {
            initBar();
        }
    }
]);

代码 2(功能)

Modernizr.load([
    {
        load: [
            '../includes/css/foo.css',
            '../includes/js/foo.js',
            '../includes/js/bar.js'
        ],
        complete: function() {
            window.initBar();
        }
    }
]);

bar.js

var initBar = function() {
    // code here
};

它在其他浏览器中运行良好。我已经尝试将块移动到头部以及页面下方。我还尝试将回调的内容包装在 $(document).ready() 中,但没有一个与代码 1 一起使用。

我得到的具体错误是:

SCRIPT5009:« initBar » est indéfini

这几乎就像是在资源加载完成之前执行了回调,但如果是这种情况,那么代码示例 2 为什么会起作用?

我还会注意到刷新页面时加载正常(很可能是由于资源被缓存的事实),但在清除缓存后它也可以正常加载。我必须在清除缓存后重新启动浏览器会话才能重现问题。

更新: 这个问题不仅限于功能。在加载的 JS 文件中定义的任何全局变量似乎都无法直接访问。如果我在页面顶部加载 CSS 而不是异步加载其他资源,也会发生这种情况。事实上,我也注意到一些以这种方式加载的 jQuery 插件存在这个问题。

更新 2: 这是根据下面的调试说明输出的console.log()。为了说明这一点,我将 bar 更改为对象而不是函数。

Internet Explorer:

   HTML1300: Une navigation s’est produite.
   Fichier : test18.php

   before .load() called
   before bar accessed
   typeof bar = undefined
   typeof window.bar = undefined

   SCRIPT5009: « bar » est indéfini
   Fichier : test18.js, ligne : 14, colonne : 13

   before bar defined

看来complete 函数在bar 定义之前执行。我觉得奇怪的是 window.bar 也未定义但仍然有效......

火狐

[02:10:46,448] "before .load() called"
[02:10:47,184] "before bar defined"
[02:10:47,184] "before bar accessed"
[02:10:47,184] "typeof bar = object"
[02:10:47,184] "typeof window.bar = object"

before .load() called
before bar defined
before bar accessed
typeof bar = object
typeof window.bar = object

Firefox 和 Chrome 似乎都以正确的顺序加载和执行资源。

【问题讨论】:

    标签: javascript internet-explorer global-variables modernizr yepnope


    【解决方案1】:

    首先,您应该知道modernizr 中的.load() 来自yepnope 库,因此您可以在其中找到它的详细文档。

    以下是我能想到的在不同浏览器中可能会有所不同的事情:

    1. 加载脚本的确切时间以及调用 complete() 函数的时间。

    2. 浏览器中的缓存(会影响加载时间)。

    3. 因为您是通过将 initBar 分配给变量而不是常规的 function initBar() 定义来定义它,所以在该行代码执行之前,该函数将不存在,而 function initBar() 将在脚本解析时存在。

    4. 确保 yepnope 加载库的版本为 1.5 或更高版本(我不知道对应的modernizr 版本。.load() 的 yepnope 文档说:“在 1.5 之前的 yepnope 版本中[调用完整函数的时间]可能会不时变化”。

    5. this page 上有注释,除非您有加载项,否则 yepnope 库可能不会在调用完整回调之前等待 .css 文件加载。我不知道这是否会影响整个时间,或者我注意到您的加载列表中确实有 .css 文件。

    所以,我建议调试一下:

    1) 将您的 initBar 定义更改为:

    function initBar() {
        // code here
    }
    

    2) 确保您的 initBar 定义在正确的范围内并且可以从您的其他代码访问。留意诸如在另一个函数(onload、document.ready 等)中可能使其无法访问的情况。

    3) 像这样插入一些console.log() 语句来做一些时序调试:

    console.log("before .load() called");
    Modernizr.load([
        {
            load: [
                '../includes/css/foo.css',
                '../includes/js/foo.js',
                '../includes/js/bar.js'
            ],
            complete: function() {
                console.log("before initBar() called");
                console.log("typeof initBar = " + typeof initBar);
                console.log("typeof window.initBar = " + typeof window.initBar);
                initBar();
                console.log("after initBar() called");
            }
        }
    ]);
    
    
    console.log("before initBar() defined");
    function initBar() {
        // code here
    }
    

    然后,看看事情的顺序和 typeof 语句的内容。这里的想法是尝试找出事情是否以错误的顺序执行或范围是否错误。

    4) 尝试单独加载 .css 文件,以免影响 .js 加载。


    这是一个替换脚本,可以动态加载多个脚本来替换modernizr buggy .load() 代码。这一个并行加载它们。这仅适用于脚本文件(尽管相同的概念可用于 .css 文件。

    function loadScriptsInParallel(scripts, completeCallback) {
        var head = document.getElementsByTagName('head')[0];
        var remaining = scripts.length, i, scriptTag;
    
        function complete() {
            // make sure it's not called again for this script
            this.onreadystatechange = this.onload = function() {};
            // decrement remaining count and check if all are done
            --remaining;
            if (remaining === 0) {
                // all are done call the callback
                completeCallback();
            }
        }
    
        for (var i = 0; i < scripts.length; i++) {
            scriptTag = document.createElement('script');
            scriptTag.type = 'text/javascript';
            scriptTag.src = scripts[i];
            // most browsers
            scriptTag.onload = complete;
            // IE 6 & 7
            scriptTag.onreadystatechange = function() {
                if (this.readyState == 'complete') {
                    complete.apply(this, arguments);
                }
            }
            head.appendChild(scriptTag);
        }
    }
    

    示例用法:

    loadScriptsInParallel([
        '../includes/js/foo.js',
        '../includes/js/bar.js'
    ], function() {
        // put code here for when all scripts are loaded
        initBar();
    });
    

    工作演示:http://jsfiddle.net/jfriend00/qs44R/

    如果您需要按顺序加载它们(由于它们之间的依赖关系,一个接一个地加载),那么您可以使用:

    function loadScriptsInSequence(scripts, completeCallback) {
        var head = document.getElementsByTagName('head')[0];
        var remaining = scripts.length, i = 0;
    
        function loadNext() {
            var scriptTag = document.createElement('script');
            scriptTag.type = 'text/javascript';
            scriptTag.src = scripts[i++];
            // most browsers
            scriptTag.onload = complete;
            // IE 6 & 7
            scriptTag.onreadystatechange = function() {
                if (this.readyState == 'complete') {
                    complete.apply(this, arguments);
                }
            }
            head.appendChild(scriptTag);
        }
    
        function complete() {
            // make sure it's not called again for this script
            this.onreadystatechange = this.onload = function() {};
            // decrement remaining count and check if all are done
            --remaining;
            if (remaining === 0) {
                // all are done call the callback
                completeCallback();
            } else {
                loadNext();
            }
        }
    
        loadNext();
    }
    

    工作演示:http://jsfiddle.net/jfriend00/9aVLW/

    【讨论】:

    • 在答案中添加了调试建议。
    • 添加了关于确保您拥有足够新版本的 yepnope 库的说明。
    • 添加了关于 complete 不一定要等待加载 .css 文件的注释。
    • 感谢您的回复。我尝试使用函数声明而不是函数表达式但没有成功(另外,这不适用于不是函数的变量)。我还从资源数组中删除了 CSS,这并没有改变结果。我测试的函数在全局范围内(没有$(document).readywindow.onload),console.log 给了我undefined。我已经更新了我原来的问题。
    • @rink.attendant.6 - 看起来像那个库中的错误。还有其他方法可以通过加载通知动态加载脚本文件。这是this answer 中的一个简单代码sn-p,jQuery 可以用$.getScript() 完成它,并且周围还有许多其他库可以做到这一点。稍等片刻,我将扩展该代码 sn-p 以支持多个脚本文件。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-17
    相关资源
    最近更新 更多