Кроссбраузерное событие onDOMContentLoaded
Для инициализации страницы исторически использовалось событие 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. Нет гарантии, что версия посетителя поддерживает это событие, но попробовать стоит.
- Браузер Internet Explorer не поддерживает
onDOMContentLoaded , поэтому для него используются обходные пути.
- Функция
tryScroll() пытается скроллить документ вызовом doScroll . Если получается - значит, документ загрузился, если нет - заказывает повторную попытку через setTimeout, и так пока документ наконец не будет готов. На практике это очень надежный способ, но есть проблемы с фреймами, поэтому используется только для окон верхнего уровня.
Дополнительный фильтр - проверка document.body
- Событие
document.onreadystatechange с проверкой readyState="complete" , как и onDOMContentLoaded/onload , срабатывает после загрузки документа. Но, к сожалению, оно происходит уже после загрузки картинок. Поэтому onreadystatechange - вообще говоря, не то, что нам надо. Но это событие работает для фреймов, и при этом срабатывает до window.onload . Поэтому будем использовать и этот способ.
- Для тех браузеров, в которых не сработали предыдущие методы (например, очень старый Firefox), добавим вызов обработчика при событии
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 вызывается до полной загрузки.
В новых Firefox, Safari/Chrome и во всех Internet Explorer поддерживается атрибут defer тэга <script> . Он позволяет загружать скрипт не блокируя загрузку страницы, а параллельно с ней.
Такая отложенная загрузка скриптов позволяет странице грузиться и отображаться быстрее. Обычно откладывают загрузку для толстых библиотек.
Скрипт является объектом, необходимым для загрузки страницы, и событие onDOMContentLoaded всегда срабатывает после загрузки скриптов.
Но Internet Explorer заканчивает рендеринг документа и делает скроллинг возможным до загрузки скриптов с атрибутом defer .
Поэтому doScroll сработает до загрузки таких скриптов.
Поэтому в браузере Internet Explorer описанный код (а значит и код jQuery) при наличии <script defer> будет работать некорректно, а именно - выполняться до загрузки таких скриптов.
Это может быть важно, если вы хотите использовать такие скрипты в коде инициализации.
В качестве альтернативы событию onDOMContentLoaded и функции bindReady можно рассмотреть скриптовую вставку в самом конце <body> :
<body>
...
<script>handler()</script>
</body>
Основной плюс такого подхода - работает везде, не нужен дополнительный кросс-браузерный код поддержки события.
Основной минус - меньшее удобство, нужен дополнительный код в HTML. Кроме того, тег <body> не закрыт, поэтому body.appendChild может не работать.
Исходные коды вы можете скачать в архиве.
|
Вопрос на счет фреймов. Какое из событий в них происходит? DOMContentLoaded или window.onload ? Я когда махался с фреймами то у меня почемуто DomContentLoaded фрейма не выдавал событие и onload тоже не во всех браузерах.
Какой браузер? Вы событие ловите внутри фрейма или снаружи?
Событие ловил с наружи. А вот в каждом браузере по разному. в частности в FF3.5 DOMContentLoaded для фреймма не выдавал событие.
Снаружи это событие не ловится...
>> * меньшее удобство, нужен дополнительный код в HTML
Чем удобство добавления скрипта в начало страницы отличается от удобства добавления скрипта в конец страницы? И какой нужен дополнительный код в HTML?
В конце HTML нужен тэг
script
с вызовомready
.При использовании функции bindReady - такой скриптовой вставки не нужно.
Если подключать все скрипты в конце страницы, то вообще никаких ready не надо, и код сильно упрощается.
Точно! Или, добавить проверку переменной gbLoaded в функции, которым нужна полная загрузка документа, а в конце HTML просто добавить:
...
gLoaded = true;
Таким образом не придётся ждать загрузки картинок.
у оперы 9 были проблемы если поставлен таймаут 0, ставьте хотя бы 10
yes, this sounds right! lubbock fence companies
thanks, this is great! excavating near me
В восьмом эксплорере не сработал пример...
Тоже самое. В IE8 сообщение появляется после загрузки страницы.
В конце кода (отметка 4.1) можно предотвратить конфликт с поставленным обработчиком window.onload:
вот нагуглил DOM ready
Между 14 и 15 строчкой в "Код кроссбраузерной поддержки"
нужно добавить:
window.addEventListener( "load", ready(), false );
--
без этого не работает в "хроме" и "сафари"
Можно подробнее? Что не работает?
Без этого не срабатывает handler в последних хроме и ff.
>> Браузер гарантирует последовательное выполнение скриптов,
>> поэтому к моменту выполнения этой вставки все скрипты будут загружены.
Вроде бы не рабодает все равно с defer. Пример: в хеад - jquery с defer, в конце подключаю файл с кодом, который использует jquery. Если бы скрипты выполнялись последовательно, тогда не выскакивало бы '$ is not defined'. Правильно? Или когда яваскрипт подключен, а не встроен в страницу браузер уже не гарантирует последовательную работу? Что-то запутался
Очень хотелось бы услышать комментарии к этому вопросу. Спасибо.
Скрипт с DEFER может сработать как до так и после onDOMContentLoaded.
Это не проясняет что вы имели в виду под "Корректно работает со скриптами с defer. Браузер гарантирует последовательное выполнение скриптов, поэтому к моменту выполнения этой вставки все скрипты будут загружены.". Или я в чем-то запутался. Пожалуйста "разжуйте" есть есть время. Спасибо.
Немного исправил статью. По взаимодействию с DEFER:
DOMContentLoaded может быть как до так и после скриптов с Defer. Обычно после, в Firefox может быть до, если скрипт в кеше.
почитав комментарии, понял что этот код, мало того что громоздкий, имеет кучу нюансов, так он ещё и ни фига не гарантирует
скорее в 99% и 999тысчяных процентов, он нафиг ни кому ненужен.
А автору принцип KISS. В коде и без этого забот хватает, а насчёт кросс браузерности, даже в самом худшем варианте,
if(ослик)alert("осликов на сайт не пускаем");
Почему миллионы разработчиков должны что то изобретать, когда ИЕ жопу морщит(извините но других слов ненашол). Объявить байкот ИЕ, хотя бы на личных сайтах, и они сами очень быстро все проблемы связанные с кроссбраузерностью, раз и на всегда устранят, иначе ИЕ безнадёжно устареет.
что мешает просто проверять /^(interactive|complete)$/.test( document.readyState ) и не париться? работает во всех современных браузерах.
.ня
Хорошо, когда работает во всех, а не только в современных. Хотя бы начиная с Firefox1.5/2.0, IE6, Netscape8/9, Opera9 и Safari3
Немного подумав об "Альтернатива событию onDOMContentLoaded", выношу на СУД свое решение:
создаем ready.js с кодом:
Подключаем его в начале страницы, внутри страницы используем регистрацию:
В комментариях код не нуждается
.
Не лучшее решение.
Сразу бросаются в глаза "тяжелые зависимости", вроде setTimeout('ready.work();'...);. Плюс исполнение кода в неявном eval'е и итерация по элементам массива циклом for-in.
Плюс, если предполагется множество обработчиков, то логичнее вызывать их в порядке добавления, чего не гарантирует выбранный Вами тип цикла.
Да и проверка document.body не гарантирует ожидаемого.
Для большей уверенности, в том, что это сработает там, где кто-либо преопределил прототип Array или ожидает срабатывания обработчиков в порядке их назначения используйте
А использовать:
Согласен, что немного поспешил с ready.js.
Да и если честно - не знал, что в setTimeout можно передавать функцию...
Переделал с учетом критики:
Параметр name передавать необходимо, чтобы исключить повторное выполнение.
В замыканиях я не силен, поэтому если можно их избежать без вреда для кода, то избегаю.
Интересное место - сначала думал, что передаваемая функция будет работать в глобальном контексте (по аналогии со строкой), и нужно будет ставить Ready.work, но все оказалось намного приятнее.
Толком не протестировал. Но вроде должно работать.
Вопрос к предыдущему Гостю: что Вы имеете в виду, когда говорите, что document.body не гарантирует ожидаемого? Я думаю, что если body доступен, то уже можно работать с его содержимым.
Не факт, document.body может быть доступен до загрузки всего содержимого
Есть ещё 1 простой вариант без поддержки фреймов. Можно проверять document.documentElement.innerHTML на наличие закрывающего тега body. Странно, что до этого раньше никто не додумался, или я чего-то не понимаю?
Пробовал так, но работает это не всегда:
docreadytimer = setInterval("if (document.getElementsByTagName('html')[0].innerHTML.indexOf('/body') > 0){clearInterval(docreadytimer); start();}", 50);
Видимо, современные браузеры достраивают документ и выводят его пользователю в ходе загрузки и еще до загрузки всего содержимого, т.е. в структуру документа проставляют < /body >< /html >
Какбэ да, потому что при парсинге открывающегося тэга браузер автоматом подставляет закрывающий
Спасибо за код. Почти везде работает.
Не могу понять логику кода. Помогите пожалуйста. Возможно я что-то недопонимаю. Попытался разобрать исходный код автора пошагово:
пункт_1. в OnReady в качестве аргумента "Хэндлера-1" передаётся функция, которая выводит алёртом значение последнего Дива.
пункт_2. внутри функции OnReady:
пункт_2-а) благодаря тому, что readyList[] пустой = вызывается функция bindReady, которой в качестве аргумента "Хэндлера-2" передаётся функция, которая вызывает все функции из списка readyList[], который пока ещё ПУСТ(!).
пункт_2-б) ПОСЛЕ вызова функции bindReady, переданный в неё из функции OnReady "Хэндлер-1" отправляется в массив readyList[].
пункт_3. внутри функции bindReady:
доходим по логике до 18-й строки кода, где, предположим, мы получим TRUE и выполнится функция tryScroll().
пункт_4. внутри функции tryScroll():
тут called=False, идём дальше, и предполагаем, что document.body=TRUE => тогда запускается блок TRY, в котором вызывается функция ready().
пункт_5. внутри функции ready():
called становится true, и запускается handler(), т.е. функция которая была передана в функцию bindReady через аргумент handler, т.е. выше упомянутый "Хэндлер-2", который запускает все функции из списка readyList[], который к данному моменту ПУСТ (!) из-за того, что из пункта "2-а" мы улетели в пункт "3" (и пункт "2-б" ещё не выполнялся). ТО ЕСТЬ, по идее НЕЧЕМУ выполняться.
пункт_6.
- из функции ready() возвращаемся в блок TRY;
- крутим tryScroll пока документ не будет ГОТОВ;
- выходим из конструкции if () на 30 строке кода;
- на 33-й строке ready() вешается как обработчик (при этом в теле этой функции на данный момент до сих пор содержится handler() который есть "Хэндлер-2", то есть выполнение ПУСТОГО списка readyList);
- и улетаем из функции bindReady обратно в функцию OnReady, из которой её вызывали, и попадаем на пункт "2-б" в котором добавляется "Хэндлер-1" в список функций вызываемых "Хэндлером-2" (который уже нигде по коду не вызывается);
- выходим из функции OnReady.
ВОПРОС: Откуда же тогда, и на каком этапе кода, будет вызван обработчик "Хэндлер-1", который собственно и отвечает за показ Названия Последнего тэга Дива с помощью алёрта ???
Не правильнее в строке 42 вместо:
if (window.addEventListener)
сделать
esle if (window.addEventListener)
Добрый день. А почему функция onReady не срабатывает если ее запихнуть в условие ?.....к примеру
Очень понравился материал, но можно и подробнее
О, благодаря этому обмену вы раскрыли многие мои идеи, поэтому я могу узнать больше о многих ценных вещах. run 3
Najnowsze anonse erotyczne
Roksa ciaza
Roksa twarze
Hi there, I am here to find new friends who love playing online games. We can make friends and play tunnel rush in our free time. It's so funny, let's try it!
wow, I went crazy reading your post, it's very good and informative. and if you have free time then I invite you to play the game wordle and io games with me.
Hope someone can explain more detail. I have the same problem five nights at freddy's.
I was looking for another article by chance and found your article keo nha cai I am writing on this topic, so I think it will help a lot. I leave my blog address below. Please visit once.
cuphead I can make out a great deal of essential information.
Вы вызываете мое неугасающее восхищение за то, что написали такое замечательное произведение. Мне очень понравилось читать эту статью, и я думаю, что она может быть лучшей из тех, что я читал до сих пор. В идеале вы должны поддерживать один и тот же высокий стандарт на протяжении всего выполнения этого задания. fnf mod
The information you share is very good and exciting, I will definitely follow your next posts. krunker
Photo gallery is filled and approved for the use of the offers. The discount of the Diesel Gas Station Near Me are ensured for the challenges. Mandatory look is fit for the mixing of the goals for the native and all joys for the main field for the citizens.
I am very impressed with the information drift boss you share, which will play an important role in adding material to the topic the impossible quiz people are researching.
This is an article full of interesting and useful information. You can share it on many subway surfers websites so that everyone can learn and learn together.
I appreciate your efforts over the past time, you have shown everyone that this article of yours wordle deserves everyone's recognition. Great article!
Before images and other backrooms game items that do not alter the structure of the document have finished loading, this event is fired as soon as the DOM document is ready. Because images can take a while to load, this is particularly practical because the onDOMContentLoaded handler can alter the page and instantiate interfaces right backrooms immediately without having to wait for everything to load.
No ready is required and the code is substantially streamlined if all the scripts are connected at the end of the page. flappy bird
I've heardle realized the importance of taking care of my physical health to maintain energy levels throughout the day
If all the scripts are linked to the bottom of the page, no ready is needed, and the code is greatly streamlined.
Great article! https://yonkersbasementremodeling.com/
Great blog! https://basementremodelingbrooklynny.com
Awesome post, thank you for sharing this one. https://asbestosremovalbrooklyn.com/asbestos-removal-brooklyn-ny/
You've come this far and I think that's great, keep chasing your dreams and one day you'll reach the top and then you'll see how great you've become . pge outage map
Your work is great. You can learn so much from dealing with you. vampire survivors
I wish I knew about this article sooner because the information in it is amazing and exactly what I was looking for. mapquest directions florida
Great information, I will recommend it to my friends for them to check out. Thanks for sharing! If you have more time, please visit: trap the cat
3D product animation services are the next big thing for businesses that want to go global and provide a better customer experience. We at Atellier Studio offer the best 3D rendering and visualization services, which allow you to have a realistic view of your products from any angle.
Since images and other media files can sometimes take a significant amount of time to load, mapquest driving directions using DOMContentLoaded allows the page to feel more responsive to users as it doesn't wait for these resources to be fully fetched.
This is very useful information for me. Thank you. Besides, I would like to share with you a website that I found very interesting, which is:dinosaur game
This is extremely helpful information for me. geometry dash scratch
You are on a mission to engage and educate your fnf game audience. The information you provide has been very helpful to me. And you succeeded brilliantly!
Enter the wild and wacky world of subway surfers online where the only rule is egg-stermination!
Это фантастический и информативный пост, к которому я обязательно вернусь и прочитаю еще раз. immaculate grid Спасибо.
JavaScript и все DOM-элементы - это всё, ради чего мы здесь, чтобы помочь мне манипулировать объектами. my location
Действительно впечатляющая статья! Есть много полезной информации. Я постараюсь следовать вместе с where am i.
Thank you for providing the text.Connections Game
I am happy to find this post very useful for me, as it contains lot of information. I always prefer to read the quality content and this thing I found in you post. Thanks for sharing. MCPE Apk
You really overloaded me with knowledge, and I appreciate it. after gaining new knowledge from the posts I've read on your blog. I'm excited to gain knowledge from your practical experience. I am grateful that you shared.
papa;s freezeria
Traverse the globe without leaving your gaming chair as you join the worldwide community of Watermelon Game players, all connected by a love for strategic fruit merging and avoiding the chaos of excessive stacking.
Отправить комментарий
Приветствуются комментарии:Для остальных вопросов и обсуждений есть форум.