Szybciej niż DOMContentLoaded
02 lipca, 2010
Oddzielenie warstwy zawartości, prezentacji i zachowania w dzisiejszych czasach stało się już nie trendem, a jednym z podstawowych umiejętności koderów. Takie rozwiązanie ma wiele zalet - daruje sobie i Wam ich wyszczególnianie. Są oczywiście również wady. Jedną z bolączek tego rozwiązania jest problem skakania strony podczas ładowania dłuższych dokumentów. Związane jest to z faktem, że zanim DOM dokumentu zostanie załadowane wszystkie bloki które zostaną ukryte za pomocą JavaScriptu na chwilę pojawią się na ekranie po czym znikną (np. w przypadku kart zakładek).
- Oczywiście jeżeli koder stwierdzi: czemu by nie ukryć za pomocą stylów wszystkich kart zakładek z wyjątkiem tej wybranej - bo przecież JavaScriptem można zmieniać zakładki, to rzecz jasna problemu nie będzie...
- Ale jeżeli chcemy być przyjaźni dla osób bez JavaScriptu oraz QL&Trendy to problem jest.
Riddle kiedyś na swoim blogu wspominał, że w tylko w takich przypadkach wrzucanie skryptów gdzieś w bebechy <body> jest usprawiedliwione. To jedyne wyjście?
Ostatnio wpadłem na pomysł jak można by w niektórych przypadkach ten problem rozwiązać. Wystarczy, że za pomocą JavaScriptu umieszczonego w sekcji <head> dokumentu dorzucimy do tagu <html> klasę js. Możemy to zrobić nawet jeżeli jeszcze nie został dodany ani jeden element <body> do DOM. Odtąd tworząc style stosujemy prefiks .js ustalając zachowanie elementów z włączonym JavaScriptem. W efekcie otrzymujemy CSS dla użytkowników z JavaScriptem i bez.
Metoda działa we wszystkich nowoczesnych przeglądarkach jak i również w IE 6.0 i wyższych.
<script type="text/javascript"> document.getElementsByTagName('html')[0].className = "js"; </script>
.tab {display:block;} .js .tab {display:none;} .js .tab.select {display:block;}
Dzięki temu rozwiązaniu, również możemy np. w prosty sposób ukryć na stronie elementy które są dostępne tylko z JavaScriptem. Zastosowań jest wiele...
#twitter {display:none;} .js #twitter {display:block;}
Przygotowałem dwie prezentację, pierwsza to bardzo prosta pokazówka jak działa powyższy kod, druga zaś to przykład kart z zakładkami (nie zapomnieć zobaczyć karty bez JavaScriptu!).
Edit: kilka poprawek.
Komentarze do wpisu "Szybciej niż DOMContentLoaded":
1.
Wasacz napisał(a):
02 lipca 2010, 16:11:23
tip: DOMContentLoaded
edit: oh fail, to czemu w tytule wpisu jest onload? :/
2.
b4rtaz napisał(a):
02 lipca 2010, 16:12:25
@Wasasz: przeczytaj zanim napiszesz...
Dop: onload - po załadowaniu (jak się mylę to nie bić)?
3.
Wasacz napisał(a):
02 lipca 2010, 16:21:27
Ale w każdym razie, trochę nie tak to testujesz. Wpakowałeś na koniec
bodyskrypt, który się timeoutuje 3 sekundy, zanim się wczyta.Kolejność wykonywania (w twoim przykładzie) jest taka:
DOMContentLoadedhtml(nadal czekamy na załadowanie się DOM)
body, który wykonuje się natychmiast (bo siedzi wbody)DOMContentLoadedTakże ten test ma tyle do ładowania się DOM, co sondaże przedwyborcze do wyników wyborów.
4.
b4rtaz napisał(a):
02 lipca 2010, 16:37:28
@Wasacz: właśnie o to chodzi... Na miejsce tego skryptu mogłem wrzucić
<?php for($x = 0; $x < 10000000000; $x++) print $x; ?>Skrypt ten ma symulować, że strona się dłużej ładuje (o czym zresztą pisze we we wpisie).5.
Wasacz napisał(a):
02 lipca 2010, 16:49:11
No nie, bo JS jest wykonywany jeden po drugim, jak leci. A ty zatkałeś interpreter JS jednym skryptem, i się dziwisz, że on się zatrzymał.
Z tego powodu opóźniłeś odpalenie tamtego handlera w przybliżeniu o te 3 sekundy. Między innymi z tego powodu ktoś kiedyś napisał asynchroniczną wersję dla statystyk z Google Analytics, bo one siedziały w tym samym miejscu i opóźniały resztę na tej samej zasadzie (słowo kluczowe: asynchroniczna).
Faktyczna różnica by była wtedy, gdybyś rzeczywiście tam wypisał tyle markupu. Ale to nie daje takiego efektu, o którym myślisz (ładowanie DOM trwa i tak tyle samo), a tylko zaśmiecasz kod.
6.
b4rtaz napisał(a):
02 lipca 2010, 16:58:06
Gdzie ja się dziwie? Przecież o to chodzi! O.o Zatrzymanie na kilka sekund OnLoadedDOM ma, powtarzam, symulować długie ładowanie strony.
Proszę skopiuj sobie któryś z powyższych przykładów, w miejsce tego skryptu wstaw sobie 10MB losowych danych po czym z jakiegoś serwera pobierz sobie z transferem 15kb/s. Wtedy myślę, że zauważysz o co tutaj chodzi, i o co tutaj NIE chodzi.
Wejdź proszę w drugi przykład i wyłącz JS. Znajdź mi drugą stronę w sieci która tak potrafi...
7.
Wasacz napisał(a):
02 lipca 2010, 17:00:55
Problem w tym, że ty robisz odwrotnie: długim ładowaniem strony symulujesz zatrzymanie DOMContentLoaded (tak to się nazywa, tak w ogóle), przy czym „zatrzymanie DOMContentLoaded” to u ciebie zatkanie interpretera.
Rozumiesz?
8.
b4rtaz napisał(a):
02 lipca 2010, 17:13:04
Brawo, i o to właśnie chodzi...
Co za różnica czy wrzuciłem jakiś dziwny skrypt czy tonę śmieci skoro efekt jest ten sam?
9.
Wasacz napisał(a):
02 lipca 2010, 17:17:09
Proszę, tym razem ty, przeczytaj moje komentarze…
10.
b4rtaz napisał(a):
02 lipca 2010, 17:46:49
@Wasacz: dobra, to może raz jeszcze.
Aktualnie strony projektuje się tak aby wszystkie skrypty były umieszczone w <head>, oraz aby nasza strona działała również bez javascriptu. Problem zaczyna się w momencie gdy chcemy stworzyć jakiś element na zasadzie ukryty/zakryty (np. zakładki, blok zakryj/odkryj). Aby użytkownik bez JS'a mógł przeczytać wszystkie treści co użytkownik z włączonym JS'em należy wszystkie karty/elementy domyślnie w CSS'ie zostawić widoczne.
W przypadku gdy mamy JS, zwykło się używać OnLoadedDOM do zakrywania kart które mają być aktualnie nie wyświetlane. Czyli OnLoadedDOM i szukamy co trzeba ukryć, i ukrywamy.
Problem w tym, że jeżeli treści na stronie było dużo, albo serwer był typu ślimak, zanim strona została załadowana i event OnLoadedDOM wywołany, przez chwilę wszystkie karty były odsłonięte. Czy to problem? Owszem i to poważny. Wyobraź sobie, że wchodzisz na jakiś serwis, część strony już się załadowała, czytasz jakieś nagłówki a po chwili wszystko znika.
Dlatego zwykło się (czyli mówię o profesjonalnych projektach) albo olewać użytkowników bez JS'a, albo zaraz po bloku wrzucało się w środek skrypt automatycznie ukrywający co trzeba.
Ja przedstawiam rozwiązanie tego problemu, gdzie to sam CSS zajmuje się tym problemem.
Kiedyś sam popełniłem ten błąd, wejdź na http://rly.pl/ na wolnym łączu (albo zarzuć sobie coś do ściągania i wejdź) - blok z newsami najpierw będzie odkryty, potem się ukryje.
I na prawdę - nie ważne jak uważasz czy dobrze czy źle zrobiłem z tym blokowaniem eventu, przykład idealnie obrazuje co chciałem pokazać. Czyli możliwość wykonania elementów typu zakryj/pokaż bez skakania, działającej i dla osób z JS'em i bez.
Jeszcze dodam: ten dymek, że DOM jest załadowany ma pokazywać tylko to, że w przypadku czy to głupiego kodera który wrzuca jakieś "nie asynchroniczne" skrypty na dół dokumentu, czy w przypadku długiego ładowania strony, OnLoadedDOM jest wolniejszy od mojego rozwiązania.
11.
Wasacz napisał(a):
02 lipca 2010, 17:56:19
Problem opóźnienia wynika z czegoś zupełnie innego; zobacz sobie chociażby demo u Riddle'a. Możesz też poczytać o asynchronicznym odpalaniu skryptów JS. Nie mam już siły ci tego tłumaczyć, skoro stawiasz aż tak czynny opór.
Jeśli faktycznie byś przeczytał, co napisałem, skończyłbyś pisać „OnLoadedDOM”. Ale skoro „działa”, to niech „działa”.
12.
b4rtaz napisał(a):
02 lipca 2010, 18:01:55
Wytłumacz mi więc, z czego wynika opóźnienie w evencie "DOMContentLoaded" jak nie z czasu ładowania dokumentu?
13.
Wasacz napisał(a):
02 lipca 2010, 18:02:24
http://b.nano2.pl/2010/07/02/szybciej-niz-onload-dom/#comm1537649
14.
b4rtaz napisał(a):
02 lipca 2010, 18:06:30
Ale co to ma do rzeczy? Dobra EOT.
15.
riddle napisał(a):
02 lipca 2010, 18:30:37
Zabawne, ja od razu zrozumiałem o co b4rtazowi chodzi – nie wywołanie eventu
DOMContentLoaded, tylko odpalenie kawałka JS zanim DOM będzie dostępny.Kawałek JS dodany do
headbez wywołaniaaddEventListener, po prostu……sprawi, że element HTML dostanie klasę. DOM nie jest wygenerowany, ale root całego dokumentu już jest, od razu – dlatego można na nim wywoływać operacje. A dodanie klasy spowoduje nadpisanie CSS, nawet szybciej niż
DOMContentLoaded.16.
Wasacz napisał(a):
02 lipca 2010, 18:33:56
riddle: Zapewne nikt wcześniej nie rozumiał, co robi ta linia kodu. Zabawne, że spór był o co innego.
17.
b4rtaz napisał(a):
02 lipca 2010, 18:35:40
A wszystko w treści wpisu... :(
18.
Bartosz "BTM" Szczeciński napisał(a):
02 lipca 2010, 18:38:31
Dużo ma do rzeczy. To, że skrypt wstawiony na końcu w BODY jest wg. Ciebie odpalany później, nie ma nic do rzeczy z tym, że DomContentLoaded odpalany jest dopiero po zakończeniu de-facto ładowania się strony, a z tym, że JS w takim trybie jest synchroniczny i próba załadowania jednego skryptu blokuje drugi, tak na prawdę akcja dodająca klasę js do <HTML> odpala się od razu po tym jak Twoja przeglądarka otrzyma cały HTML strony, a to, że nie jest ona wykonywana wynika z kolejkowania egzekucji skryptów.
Łatwo się o tym przekonać np. w ten sposób: http://anfo.pl/blog/js-delay/test-case-1.html
Jak widać tekst od razu jest czerwony, strona się ładuje 3 sekundy. Czemu? Bo do symulacji opóźnienia ładowania strony nie użyłem ładowania zewnętrznego JS tylko obrazka.
Twoje rozwiązanie jest pomocne tylko w wypadku, kiedy ładujesz duże pliki JS. Jeszcze raz - TYLKO w takim wypadku, nic innego nie "blokuje odpalenia DOMContentLoaded" (ująłem w cudzysłów, bo jest to błędne założenie).
19.
Wasacz napisał(a):
02 lipca 2010, 18:40:40
BTM, dzięki.
20.
b4rtaz napisał(a):
02 lipca 2010, 18:53:27
@BTM: "próba załadowania jednego skryptu blokuje drugi". Nie prawda, to nie w tym przypadku. Dokument index.html może w momencie gdy browser czeka za sleep.php może jest już pobrany, ale DOM dalej nie jest załadowane. http://b4rtaz.nano2.pl/blog/szybciejnizdom/index2.html
Dop: <script> powoduje zatrzymanie parsowania dokumentu, albo dodawania jego dalszych części do DOM.
21.
Bartosz "BTM" Szczeciński napisał(a):
02 lipca 2010, 19:45:33
@b4rtaz: tak, zgodnie ze specyfikacją :-)
I popraw formularz komentowania, bo nie wiadomo czemu pokazuje pola dla niezalogowanego usera.
Dodaj komentarz: