Можно ли доверять script.readyState для обнаружения конца динамической загрузки script?
Я использую динамическую загрузку script для уменьшения продолжительности начальной загрузки страницы. Чтобы гарантировать доступность функций и объектов, определенных script, мне нужно убедиться, что script был полностью загружен.
Я разработал свою собственную Javascript-библиотеку с этой целью и, таким образом, провел довольно много исследований по этому вопросу, изучив, как это делается в разные библиотеки.
Во время обсуждения, связанного с этой проблемой, Кайл Симпсон, автор LABjs, заявил, что:
LABjs (и многие другие загрузчики) установлены как "onload", так и "onreadystatechange" на всех элементах script, зная, что некоторые браузеры будут запускать один, а некоторые будет стрелять другой...
Вы можете найти пример этого в текущей версии jQuery на момент написания этой статьи, v1.3.2:
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function(){
if ( !done && (!this.readyState ||
this.readyState == "loaded" || this.readyState == "complete") ) {
done = true;
success();
complete();
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
head.removeChild( script );
}
};
Что касается уровня техники, но при анализе странного поведения в Opera 9.64, я пришел к выводу, что, используя эту технику, обратный вызов onload был запущен слишком рано.
Я опубликую свои собственные выводы в ответ на этот вопрос и хотел бы собрать дополнительные доказательства и отзывы сообщества.
Ответы
Ответ 1
В Opera нельзя использовать свойство script.readyState. Например, readyState "загружен" может быть запущен до того, как script работает в Opera 9.64.
Я выполнил тот же тест в Opera 9.64 и Opera 10 с разными результатами.
В Opera 9.64 обработчик onreadystatechange запускается дважды, один раз до и один раз после запуска script. Свойство readyState "загружается" в обоих случаях, что означает, что этому значению нельзя доверять, чтобы обнаружить конец загрузки script:
# Fri Dec 18 2009 17:54:43 GMT+0100
# Opera/9.64 (Windows NT 5.1; U; en) Presto/2.1.1
Test for script.readyState behavior started
Added script with onreadystatechange handler
readystatechange: loaded
test1.js: Start
test1.js: Start of closure
test1.js: End of closure
readystatechange: loaded
В Opera 10 обработчик onreadystatechange по-прежнему запускается дважды со значением "загружен", но оба раза после запуска script:
# Fri Dec 18 2009 18:09:58 GMT+0100
# Opera/9.80 (Windows NT 5.1; U; en) Presto/2.2.15 Version/10.10
Test for script.readyState behavior started
Added script with onreadystatechange handler
test1.js: Start
test1.js: Start of closure
test1.js: End of closure
readystatechange: loaded
readystatechange: loaded
Эти разные поведения показывают, что onreadystatechange не является надежным способом обнаружения конца загрузки script в Opera. Поскольку Opera также поддерживает прослушиватель onload, этот другой механизм следует использовать вместо этого.
Исходя из результатов этих тестов, onreadystatechange следует использовать только для обнаружения конца загрузки script в Internet Explorer, и его нельзя устанавливать в других браузерах.
Ответ 2
В Firefox, Safari и Chrome вызывается обработчик onreadystatechange nevers.
Я создал короткий тестовый пример, создав динамический script только с обработчиком onreadystatechange:
<script type="text/javascript" language="javascript">
bezen.log.info(new Date(),true);
bezen.log.info(navigator.userAgent,true);
// Activate logs
bezen.log.on();
bezen.log.info('Test for script.readyState behavior started');
var script = document.createElement('script');
script.src = 'test1.js';
script.onreadystatechange = function(){
bezen.log.info('readystatechange: '+script.readyState);
};
document.body.appendChild(script);
bezen.log.info('Added script with onreadystatechange handler');
</script>
Я выполнил тест в локальном файле в Firefox 2, Firefox 3, Firefox 3.5, Safari 3, Safari 4 и Chrome 3 и получил аналогичные результаты (здесь журналы, записанные в FF 3.5):
Fri Dec 18 2009 17:53:58 GMT+0100
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6
Test for script.readyState behavior started
Added script with onreadystatechange handler
test1.js: Start
test1.js: Start of closure
test1.js: End of closure
Onreadystatechange никогда не вызывается. В этих браузерах только приемник onload полезен для обнаружения конца загрузки script, onreadystatechange не требуется.
Ответ 3
В Internet Explorer обработчик onreadystatechange срабатывает, как и ожидалось, после окончания script.
Я выполнил тот же тест в Internet Explorer 6, Internet Explorer 7 и Internet Explorer 8 с аналогичными результатами в этих трех браузерах (здесь журналы, записанные в Internet Explorer 6):
Fri Dec 18 18:14:51 UTC+0100 2009
Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)
Test for script.readyState behavior started
Added script with onreadystatechange handler
test1.js: Start
test1.js: Start of closure
test1.js: End of closure
readystatechange: complete
Здесь, при тестировании с использованием локального файла, readyState всегда "завершен", и после обновления нескольких страниц он оставался неизменным.
Однако, как отмечено в этот пост Николаса К. Закаса, вы также можете наблюдать "загруженные" и "полные", или просто "загружен", при разных обстоятельствах.
Ответ 4
Я обнаружил, что Internet Explorer (тестирование в 9) НЕ ВСЕГДА имеет ваш script готовый, когда readyState === "загружен". У меня был успех с использованием этого обработчика событий (в 9, конечно) onactivate. Вытягивал мои волосы раньше.
Ответ 5
Аналогичные результаты в Chrome.
Не принимает onready...
Просто загрузите и включите.