Проблема window.onload() и ее решение

Собственно, вся проблема состоит в том, что событие onload() возникает после полной загрузки документа. Конечно, было бы здорово выполнить какие-нибудь действия с загруженным html-документом не дожидаясь загрузки изображений, flash-роликов и пр.

В браузерах Mozilla, а также в Opera9 для этого определено событие(не документированное) DOMContentLoaded. Для них, чтобы определить, что html-код документа уже загружен достаточно будет написать:


if (document.addEventListener) {
    document.addEventListener("DOMContentLoaded", init, false);
}

Internet Explorer при массе недостатков имеет и некоторые достоинства, к которым можно отнести поддержку атрибута defer для тэга <script>. Этот атрибут сообщает браузеру, что скрипт не должен быть запущен для генерации какого-либо контента(например, "document.write" в javascript), а браузер может продолжить загрузку документа, его обработку и вывод. Чтобы скрипт, написанный для IE, не обрабатывали другие браузеры - его придется заключить в комментарии, понятные только самому же IE. Для Internet Explorer скрипт такой:


/*@cc_on @*/
/*@if (@_win32)
document.write("<script id=__ie_onload defer src=javascript:void(0)>");
document.write("<\/script>");
var script = document.getElementById("__ie_onload");
script.onreadystatechange = function() {
    if (this.readyState == "complete") {
        init();
    }
};
/*@end @*/

Для Safari подойдет такой код:


if (/WebKit/i.test(navigator.userAgent)) { // sniff
    var _timer = setInterval(function() {
        if (/loaded|complete/.test(document.readyState)) {
            clearInterval(_timer);
            delete _timer;
            init(); // call the onload handler
        }
    }, 10);
}

А для остальных браузеров, которые не Mozilla, не Opera9 (не используют DOMContentLoaded) и не IE (выполняют скрипт немедленно, не обращая внимание на атрибут defer) и не Safari, остается использовать обыкновенный:


window.onload = init;

Как можно было заметить, все примеры вызвают функцию init(), поэтому следующий код посвящен именно ей:


function init() {
// выход из функции, если она уже вызывалась
    if (arguments.callee.done) return;

// флаг, чтобы не запускать функцию дважды
    arguments.callee.done = true;

// далее пишите скрипт
}

Полный пример кроссбраузерного решения описанной проблемы.
Замечу, что скрипт лучше брать из примера, а для демонстрации работы скрипта используется "тяжелая" (примерно 1,5МБ) картинка. Javascript в примере размещен внутри html-документа с единственной целью - так проще его продемонстрировать. Javascript из примера правильнее размещать в отдельном файле (например, script.js).

The window.onload Problem - Solved!
window.onload (again)



Много комментариев (13) к “Проблема window.onload() и ее решение”

  1. AKS :

    А ведь все можно сделать намного проще! Достаточно лишь проверять загружено ли тело документа через определенные промежутки времени.
    Вот здесь можно взглянуть на мой пример:
    тест


  2. Vitaly Harisov :

    Почему бы просто перед закрытием body не вызвать функцию init.


  3. RaR :

    Насколько я знаю, проблема таких очевидных решений по крайней мере в gecko в том что ДО нступления определенного момента (очевидно DOMContentLoaded) не гарантируется полный доступ к DOM, т.е. корректная работа getElementById/cloneNode/appendChild

    сразу скажу что эксперименты чаще всего показывают обратное :)


  4. Баранов Андрей :

    AKS, наверное, так тоже можно, хотя правильно ли проверять загрузку body? для чистоты эксперимента предлагаю попробовать на каком-нибудь большом документе. clearTimeout тоже не помешал бы, думаю. кстати, к картинке надо случайное число добавить в примере, а то она в кэше остается, приходится чистить, чтобы посмотреть еще раз.


  5. Баранов Андрей :

    Vitaly Harisov, если вас не смущает смесь html и javascript в одном файле, то можно и так сделать. однако, в том случае, когда по каким-либо причинам не загрузится внешний файл с javascript, а функцию init разместите именно в нем, то последует предупреждение об ошибке, что вряд ли украсит сайт. в-общем, так действительно просто, но и не лишено недостатков.


  6. AKS :

    Андрей, я ведь не готовое решение предложил, лишь концепт. На самом деле, лучше проверять нужно загрузку тех элементов, с которыми нам не терпится начать работать.
    clearTimeout? Так ведь при выполнении первого условия в функции test таймера просто нет…
    И, вообще, мне придется скоро убрать пример – не мой домен, так что не до картинки…


  7. Баранов Андрей :

    AKS, давайте его здесь тогда разместим, все-таки для обсуждения лучше видеть обсуждаемый предмет.

    … собственно, теперь он уже здесь. .


  8. Виктор :

    А почему нельзя ?


  9. Виктор :

    Съелись тэги.

    &lgbody onload=”init ()”>


  10. AKS :

    2 Виктор
    Вы что, не прочли первый параграф?


  11. Виктор :

    Прошу прощения, действительно, не заметил.


  12. dev :

    “однако, в том случае, когда по каким-либо причинам не загрузится внешний файл с javascript, а функцию init разместите именно в нем, то последует предупреждение об ошибке, что вряд ли украсит сайт”

    Решается очень просто:

    if(init) init();

    Но в любом случае, если уж вынесли скрипт в отдельный файл, то придется дожидаться события onload. Или добавлять вызов init() в конец файла со скриптом.


  13. mikirada :

    а если нужно запустить скрипт из основной формы
    до загрузки страницы внутри Iframe (грузится долго)
    то как такое сделать



22 queries 0.187 seconds.