Для инициализации страницы исторически использовалось событие window.onload, которое срабатывает после полной загрузки страницы и всех объектов на ней: счетчиков, картинок и т.п.
Событие onDOMContentLoaded - гораздо лучший выбор в 99% случаев. В этой статье рассмотрен код и основные приемы для его кроссбраузерной реализации.
Это событие срабатывает, как только готов DOM документ, до загрузки картинок и других не влияющих на структуру документа объектов.
Это очень удобно, т.к. картинки могут загружаться долго, а обработчик onDOMContentLoaded может произвести необходимые изменения на странице и инициализацию интерфейсов тут же, не дожидаясь загрузки всего.
"Родное" событие onDOMContentLoaded есть не во всех браузерах, поэтому мы рассмотрим код для кроссбраузерной поддержки этого события:
function bindReady(handler){
var called = false
function ready() { // (1)
if (called) return
called = true
handler()
}
if ( document.addEventListener ) { // (2)
document.addEventListener( "DOMContentLoaded", function(){
ready()
}, false )
} else if ( document.attachEvent ) { // (3)
// (3.1)
if ( document.documentElement.doScroll && window == window.top ) {
function tryScroll(){
if (called) return
if (!document.body) return
try {
document.documentElement.doScroll("left")
ready()
} catch(e) {
setTimeout(tryScroll, 0)
}
}
tryScroll()
}
// (3.2)
document.attachEvent("onreadystatechange", function(){
if ( document.readyState === "complete" ) {
ready()
}
})
}
// (4)
if (window.addEventListener)
window.addEventListener('load', ready, false)
else if (window.attachEvent)
window.attachEvent('onload', ready)
/* else // (4.1)
window.onload=ready
*/
}
Разберем его по шагам.
onDOMContentLoaded различными способами. Вполне может получиться так, что несколько способов сработают независимо.
Поэтому завернем обработчик handler в функцию ready(), единственный смысл которой - гарантировать, что handler будет вызван не более одного раза.
onDOMContentLoaded поддерживают достаточно новые Firefox, Opera, Safari/Chrome. Нет гарантии, что версия посетителя поддерживает это событие, но попробовать стоит.onDOMContentLoaded, поэтому для него используются обходные пути.
tryScroll() пытается скроллить документ вызовом doScroll. Если получается - значит, документ загрузился, если нет - заказывает повторную попытку через setTimeout, и так пока документ наконец не будет готов. На практике это очень надежный способ, но есть проблемы с фреймами, поэтому используется только для окон верхнего уровня.document.bodydocument.onreadystatechange с проверкой readyState="complete", как и onDOMContentLoaded/onload, срабатывает после загрузки документа. Но, к сожалению, оно происходит уже после загрузки картинок. Поэтому onreadystatechange - вообще говоря, не то, что нам надо. Но это событие работает для фреймов, и при этом срабатывает до window.onload. Поэтому будем использовать и этот способ.window.onload.
addEventListener/attachEvent, вы можете раскомментировать и строчку (4.1). При этом, разумеется, возможен конфликт с другими обработчиками onload.Этот код взят, с небольшими упрощениями, из библиотеки jQuery, а методы придуманы различными авторами.
Код из примера выше позволяет навешивать только один обработчик. Для поддержки нескольких - добавим дополнительную обертку:
readyList = []
function onReady(handler) {
if (!readyList.length) {
bindReady(function() {
for(var i=0; i<readyList.length; i++) {
readyList[i]()
}
})
}
readyList.push(handler)
}
Функция onReady при первом вызове вешает обработчик bindReady, который запускает все функции из списка readyList, а в дальнейшем просто добавляет новый обработчик в этот список.
Следующий пример демонстрирует использование onReady:
<html>
<head>
<script src="bindReady.js"></script>
<script src="onReady.js"></script>
<script>
onReady(function() {
var divs = document.body.getElementsByTagName('div')
alert(divs[divs.length-1].innerHTML)
})
</script>
<link rel="stylesheet" href="my.css" type="text/css">
</head>
<body>
<img src="img5.php"/>
<div>done</div>
</body>
</html>
Обработчик onReady выводит содержимое последнего тэга <div>, так что мы видим, что документ действительно загружен и разобран.
Картинка <img src="img5.php"/> загружается скриптом, который ждет 5 секунд. Это сделано для демонстрации, что onDOMContentLoaded вызывается до полной загрузки.
doScrollВ новых Firefox, Safari/Chrome и во всех Internet Explorer поддерживается атрибут defer тэга <script>. Он позволяет загружать скрипт не блокируя загрузку страницы, а параллельно с ней.
Такая отложенная загрузка скриптов позволяет странице грузиться и отображаться быстрее. Обычно откладывают загрузку для толстых библиотек.
Скрипт является объектом, необходимым для загрузки страницы, и событие onDOMContentLoaded всегда срабатывает после загрузки скриптов.
Но Internet Explorer заканчивает рендеринг документа и делает скроллинг возможным до загрузки скриптов с атрибутом defer.
Поэтому doScroll сработает до загрузки таких скриптов.
Поэтому в браузере Internet Explorer описанный код (а значит и код jQuery) при наличии
<script defer>будет работать некорректно, а именно - выполняться до загрузки таких скриптов.
Это может быть важно, если вы хотите использовать такие скрипты в коде инициализации.
onDOMContentLoadedВ качестве альтернативы событию onDOMContentLoaded и функции bindReady можно рассмотреть скриптовую вставку в самом конце <body>:
<body> ... <script>handler()</script> </body>
Основной плюс такого подхода - работает везде, не нужен дополнительный кросс-браузерный код поддержки события.
Основной минус - меньшее удобство, нужен дополнительный код в HTML. Кроме того, тег <body> не закрыт, поэтому body.appendChild может не работать.
Исходные коды вы можете скачать в архиве.