Wykonywanie skryptów po załadowaniu DOM
22 maja 2007
Zaletą rozdzielenia projektowania na warstwy zawartości, prezentacj i zachowania jest skrócenie czasu ładowania się strony. Linkujemy w head pliki CSS i JavaScript, a te przechowywane w cache nie ładują się za każdym razem spowalniając połączenie. W przypadku skryptów JS istnieje jednak dość poważny mankament, który powstrzymuje wielu webdeveloperów przed stosowaniem zewnętrznych skryptów.
Czas wykonania. Linkując skrypty z head mamy dostęp do elementów dopiero gdy cała strona się załaduje, po zdarzeniu window.onload. Gdy zaprojektowaliśmy stronę bogatą w multimedialną zawartość (choćby same obrazki) to minie dłuższa chwila zanim nasze skrypty zostaną przywołane do życia.
Znakomicie widać to na przykładzie strony komentarzy na Wykop.pl — serwer łączy się z odległym Gravatarem ściągając po kolei obrazki użytkowników. Wystarczy, że będzie działo się to wolniej niż zwykle albo połączenie zostanie zakłócone i nie możemy odpowiadać na komentarze użytkowników (inna sprawa, że ta funkcja powinna być nieinwazyjna).
Od niedawna istnieje wydajny sposób na przyspieszenie wykonywania skryptów na naszych stronach, wymyślony przez hakerów JS (Dean Edwards, Matthias Miller, John Resig). Mimo, że wspominałem już o szybszym odpalaniu skryptów, to w międzyczasie pojawiło się kilka usprawnień i haków, które determinują szerokie użycie zdarzenia załadowania drzewa dokumentu DOM – bo to właśnie ratuje nasze skóry przed czekaniem na wszelkie binarne plki.
Firefox, Opera
Najprościej uruchomić skrypty po załadowaniu się DOM w Firefoksie (Mozilli) i Operze (od wersji 9) – wspierają one niestandardowe zdarzenie DOMContentLoaded. Użycie zwyczajne, przez addEventListener:
if (document.addEventListener) {document.addEventListener("DOMContentLoaded", callback, false);}
Drugi parametr callback to nazwa uruchamianej funkcji.
Internet Explorer
W przeglądarce Microsoftu dostępny jest dodatkowy atrybut dla elementu script - defer. Sygnalizuje on IE, że ma poczekać z wykonaniem skryptów aż do momentu załadowania się DOM.
Najlepiej w tym miejscu wykonać małą detekcję Explorera przez komentarze warunkowe:
var browser = {};browser.explorer = /*@cc_on!@*/false;
Dodajemy przez szatański document.write (w innym przypadku nie zadziała) tag.
if (browser.explorer) {document.write('<script id="_defer" defer="true" src="//:"><\/script>');}
I czekamy aż zostanie załadowany i uruchomiony.
if (browser.explorer) {var deferScript = document.getElementById('_defer');if (deferScript) {deferScript.onreadystatechange = function() {if (this.readyState == 'complete') {callback();}};deferScript.onreadystatechange();deferScript = null;}}
Safari, Konqueror
Dzięki twórcy jQuery możliwe jest także dodanie DOMContentLoaded do przeglądarki Apple (i Konquerora). Potrzeba tutaj znowu wykonać sprawdzanie na czym operujemy (korzystając z lepszych metod niż sam ciąg UA):
browser.webkit = /Apple|KDE/i.test(navigator.vendor);
Dodajemy timer, który sprawdza co 10 milisekund, czy wartość document.readyState zmieniła się na loaded albo complete - wtedy wiemy, że DOM został cały załadowany.
if (browser.webkit) {var _timer = setInterval(function() {if (/loaded|complete/.test(document.readyState)) {clearInterval(_timer);callback();}}, 10);}
Inne przeglądarki
Pozostałe przeglądarki dostają zwyczajny window.onload.
Wywołanie funkcji
Aby zabezpieczyć się przed parukrotnym wywołaniem funkcji callback, należy dodać taki kod:
function callback() {if (arguments.callee.done) { return; }arguments.callee.done = true;//twoje funkcje. np: roundCorners()}
Finalny kod
Dla przejrzystości możemy zebrać wszystkie funkcje w jedną, której przekazujemy callback (na przykład funkcję init() włączającą nasze skrypty).
Pobierz: document.ready.js.
Demo przedstawiające kod w działaniu.
Biblioteki
Dodam jeszcze tylko, że jeśli korzystacie ze wspaniałej biblioteki jQuery, to używając $(document).ready (albo $().ready ew. $(function() {})) nie musicie się martwić o te skomplikowane kody powyżej - odpalane tak funkcje będą uruchamiane po załadowaniu się DOM.
Inne biblioteki też zaczynają wprowadzać wsparcie dla DOMContentLoaded, ale nie wiem w jakim stopniu i jak szeroko.


