Обратные вызовы YepNope/Modernizr с глобальными переменными JavaScript и Internet Explorer
Кто-нибудь может объяснить, почему в 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" есть индифини
Это почти так, как если бы обратный вызов выполнялся до того, как ресурсы закончили загружаться, но если это имело место, то почему пример кода 2 работает?
Также отмечу, что при обновлении страница загружается нормально (скорее всего из-за того, что ресурсы кэшируются), но она также прекрасно загружается и после очистки кеша. Я должен перезапустить сеанс браузера после очистки кеша, чтобы воспроизвести проблему.
ОБНОВЛЕНИЕ: Эта проблема распространяется не только на функции. Кажется, что любая глобальная переменная, определенная в загружаемом файле JS, не доступна напрямую. Это также происходит, если я загружаю CSS вверху страницы, а не асинхронно с другими ресурсами. На самом деле я также замечаю эту проблему с некоторыми плагинами jQuery, которые загружаются таким образом.
ОБНОВЛЕНИЕ 2: Вот console.log()
вывод в соответствии с инструкциями по отладке ниже. Я изменил бар, чтобы быть объектом вместо функции, чтобы проиллюстрировать это.
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
также не определено, но работает...
Fire Fox
[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 загружают и выполняют ресурсы в правильном порядке.
1 ответ
Прежде всего, вы должны знать, что .load()
В Modernizr из библиотеки Yepnope, где вы найдете подробную документацию для него.
Вот возможные вещи, которые могут отличаться в разных браузерах:
Точное время загрузки скриптов и, следовательно, время, когда
complete()
функция вызывается.Кеширование в браузере (что может повлиять на время загрузки).
Потому что вы определяете
initBar
назначая его переменной вместо обычнойfunction initBar()
определение, функция не будет существовать, пока эта строка кода не будет выполнена, тогда какfunction initBar()
будет существовать во время синтаксического анализа.Убедитесь, что у вас версия 1.5 или выше из библиотеки загрузки yepnope (я не знаю, какой версии modernizr соответствует. Документ yepnope для
.load()
говорит это: "В версиях yepnope до 1.5 это [когда вызывается полная функция] может время от времени меняться".На этой странице есть примечания о том, что библиотека 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 .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/