Полное решение
function init() {
// выходим, если функция уже выполнялась
if (arguments.callee.done) return;
// устанавливаем флаг, чтобы функция не исполнялась дважды
arguments.callee.done = true;
// что-нибудь делаем
};
/* для Mozilla/Firefox/Opera 9 */
if (document.addEventListener) {
document.addEventListener("DOMContentLoaded", init, false);
}
/* для Internet Explorer */
/*@cc_on @*/
/*@if (@_win32)
document.write("
");
var script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
if (this.readyState == "complete") {
init(); // вызываем обработчик для onload
}
};
/*@end @*/
/* для Safari */
if (/WebKit/i.test(navigator.userAgent)) { // условие для Safari
var _timer = setInterval(function() {
if (/loaded|complete/.test(document.readyState)) {
clearInterval(_timer);
init(); // вызываем обработчик для onload
}
}, 10);
}
/* для остальных браузеров */
window.onload = init;
Неблокирующая загрузка JavaScript
Внешние JavaScript-файлы блокируют загрузку страницы и сильно влияют на ее производительность, но существует достаточно простой выход из этой ситуации: использовать динамические теги и загружать скрипты параллельно, увеличивая тем самым скорость загрузки страницы и улучшая пользовательское восприятие.
Давайте сначала рассмотрим, в чем заключается проблема с загрузкой скриптов. Все сводится к тому, что браузер не может сказать, что находится внутри скрипта, не загрузив его полностью. Скрипт может содержать вызовы document.write(), которые изменяют DOM-дерево, или вообще location.href, что отправит пользователя на другую страницу. В последнем случае все компоненты, загруженные на предыдущей странице, могут оказаться ненужными. Чтобы предотвратить загрузки, которые могут оказаться лишними, браузеры сначала загружают, затем анализируют и исполняют каждый скрипт перед тем, как переходить к следующему файлу в очереди на загрузку. В результате каждый вызов скрипта на вашей странице блокирует процесс загрузки и оказывает негативное влияние на скорость загрузки.
Ниже приведена временная диаграмма, которая демонстрирует процесс загрузки медленных JavaScript-файлов. Загрузка скриптов блокирует параллельную загрузку фоновых картинок, которые идут сразу за ним:

Рис. 7.2 . Временная диаграмма: неблокирующее поведение JavaScript-файлов
Как мы видим, файлы скриптов уже не блокируют загрузку, и браузер может начать работать с другими компонентами. Общее время загрузки при этом сократилось вдвое. Это будет работать только в том случае, если «динамические» скрипты не содержат вызовов document.write. Если это не так, то все такие вызовы нужно будет заменить на element.innerHTML либо отказаться от использования этой техники.
Зависимости
Еще одна проблема, которая связана с динамическим подключением скриптов, заключается в разрешении зависимостей. Предположим, что у вас есть 3 скрипта и для three.js требуется функция из one.js. Как вы гарантируете работоспособность в этом случае?
Наиболее простым способом является объединение всех скриптов в одном файле. Это не только избавит нас от самой проблемы, но и улучшит производительность страницы, уменьшив число HTTP-запросов. Увеличения производительности может и не произойти в случае кэширования отдельных файлов и повторного посещения, но от описанной проблемы это точно вылечит.
Если все же приходится использовать несколько файлов, то можно добавить на подгружаемый тег обработчик события onload (это будет работать в Firefox) и onreadystatechange (это будет работать в IE). Наиболее кроссбраузерным подходом будет использование меток внутри каждого файла (например, {filename}_loaded) и проверка их с некоторым интервалом на соответствие true. Это обеспечит выполнение зависимостей.
Как показали исследования динамической загрузки скриптов, проблемы с зависимостями были отмечены на IE6- и Safari3 (под Windows). Из 10 скриптов, которые загружались параллельно (на самом деле максимум они загружались по 6 в FF3, это связано с внутренними ограничениями браузера на число одновременных соединений с одним хостом), все 10 срабатывали в случайном порядке, начиная с 3-5, как раз в этих браузерах. В других браузерах (Opera 9.5, Firefox 2, Firefox 3, Internet Explorer 8) такого поведения отмечено не было.