JavaScript-Event onLoad und die bessere Alternative

Die meisten Webentwickler kennen das JavaScript-Event onload. Dieses wird aufgerufen, sobald die komplette Seite geladen ist. Sie dient dann beispielsweise dazu, direkt nach dem Laden der Seite Änderungen durchzuführen. Nun gibt es allerdings ein großes Problem mit diesem Event – und dieses soll dieser Beitrag lösen.

Eigentlich ist das onload-Event eine feine Sache. Man kann sich sicher sein, dass der gesamte HTML-Code geladen ist und somit Änderungen ausführen. Würde es dieses Event nicht geben, müsste man die JavaScript-Funtion direkt aufrufen – und das ginge dann natürlich erst ganz am Ende des body-Tags bzw. nach dem zu bearbeitenden HTML-Element. Das hätte sehr hässlichen Code zur Folge. Viel schöner ist es doch den JS-Block in den Head der HTML-Datei zu schreiben oder sogar ganz auszulagern und dann darauf zu referenzieren.

Und doch ist das Event onload problematisch. Es wartet nämlich bis der gesamte Inhalt der Seite geladen wurde – das bedeutet: auch ALLE externen Ressourcen müssen erst vollständig geladen werden. Dazu gehören Bilder, Videos und sonstige externe Dateien. Zu diesem Zeitpunkt interagiert der User aber vielleicht schon mit der Seite, weil es ihm egal ist, ob da noch ein Bild oder Video lädt. Und das wiederum kann einerseits zu JavaScript-Fehlern führen, da der Entwickler per onload oft irgendwelche Funktionalitäten initialisiert und dann davon ausgeht, dass die Funktion sofort nach dem Aufruf für die User-Interaktion zur Verfügung steht. Es ist ja nicht unbedingt vertrauensbildend, wenn bei einem unbedarften User dann so ein tolles “Diese Seite enthält Fehler” entgegenpoppt, wie der IE es bei JS-Fehlern anzeigt (bei entsprechender Einstellung, sonst zeigt er nur ein Ausrufezeichen unten links – aber auch das ist nicht schön).
Das zweite Problem ist, dass einfach eine Verzögerung eintritt. Der User möchte die Seite so schnell wie möglich benutzen – und es stört ihn garantiert, dass er eine bestimmte Funktion noch nicht nutzen kann, nur weil da noch ein Bild lädt (was mit der Funktion, die er nutzen möchte, vielleicht gar nichts zu tun hat).

Und wie lösen wir das Problemchen? Mit einem eigenen Eventchen.
Und zwar wollen wir ja eigentlich gar nicht warten bis das komplette Dokument mit allen externen Ressourcen geladen ist, sondern es würde vollkommen reichen, wenn alle DOM-Inhalte zur Verfügung stehen (das HTML-Dokument ist also vollständig geladen, sodass der DOM-Baum aufgebaut ist). Schon da können wir ja mit dem DOM arbeiten. Nativ bieten die Browser dafür leider keine Unterstützung, die für alle Browser gleichermaßen funktioniert, aber mit ein wenig Zusatzcode kann man den Seitenaufbau dadurch trotzdem beschleunigen.

//create onDomReady Event
      window.onDomReady = initReady;
 
      // Initialize event depending on browser
      function initReady(fn)
      {
      	//W3C-compliant browser
      	if(document.addEventListener) {
          document.addEventListener("DOMContentLoaded", fn, false);
        }
      	//IE
      	else {
          document.onreadystatechange = function(){readyState(fn)}
        }
      }
 
      //IE execute function
      function readyState(func)
      {
      	// DOM is ready
      	if(document.readyState == "interactive" || document.readyState == "complete")
      	{
      		func();
      	}
      }

In Zeile 1 wird unser neues Event namens onDomReady initialisiert. Dies geschieht durch den Aufruf der Funktion initReady(). Diese prüft nun, ob der Browser das W3C-Event-Model unterstützt. Für den IE gibt es eine kleine Sonderbehandlung (wie üblich).

Aufgerufen wird das Event nun so:

//execute as soon as DOM is loaded
window.onDomReady(onReady);
 
//do when DOM is ready
function onReady()
{
	alert("The DOM is ready!");
}

Im Prinzip also genauso wie onload, nur eben jetzt mit dem neuen Event onDomReady.

Zur Anschauung habe ich noch eine kleine Demo erstellt, die das neue Event mit dem onload-Event vergleicht bzw. anzeigt, welches Event schneller da ist. Das funktioniert am besten mit leerem Cache, denn da werden die Unterschiede aufgrund des großen Bildes sichtbar.
Ich habe die Datei mit IE7 und FF3 unter Vista getestet. Ob die anderen Browser das auch verstehen, weiß ich nicht.
Große JS-Libraries wie jQuery oder Prototype haben ähnliche Funktionen, aber ich mag solche Monster nicht so gern, da die dem User erstmal 100 kB entgegenwerfen, obwohl man nur ganz wenige Funktionalitäten daraus nutzt.

Wem diese Mini-Funktion noch nicht reicht, der sollte auch mal den Beitrag bei The Future of the web ansehen. Dort ist eine etwas größere Funktion, die eventuell auch etwas mehr kann (hab ich nicht ausprobiert).

Jan hat 152 Beiträge geschrieben

21 Kommentare zu “JavaScript-Event onLoad und die bessere Alternative

  1. macx sagt:

    jQuery als Monster zu bezeichnen, halte ich wenig für übertrieben, vor allen, weil es – sofern du die umfangreichen Basics nutzt – bei weitem nicht 100 kb anfallen, sondern exakt 30 kb. Auch wenn das den einen oder anderen zu viel ist, ist ein einfaches $(document).ready( … ); – für mich – doch wesentlich charmanter. Zudem erfüllt es doch den gleichen Zweck, wie deine 33 Zeilen, die mir 31 Zeilen zu viel wären.
    Doch für alle Nicht-jQuery-Nutzer ist dein Artikel sicher ein nützlicher Hinweis. Vielen Dank also dafür!

  2. Jan sagt:

    Gut, dann sinds keine 100 kB, trotzdem finde ich die meisten JS-Frameworks sehr aufgeblasen. Meist gehts mit recht wenig Aufwand auch kleiner, wenn man wenige Funktionen des Frameworks nutzt.
    Ich hab nix gegen Frameworks, aber man sollte sie nicht einsetzen, weil sie eine bestimmte Funktion haben sondern eher weil sie in Ihrer Gesamtheit das Leben erleichtern.

  3. Keyser Soze sagt:

    Der Vollständigkeit halber: auch die YUI bietet mit den Funktionen onContentReady und onAvailable ähnliche Funktionen.

  4. Tammo sagt:

    Bindet man das jQuery von der Google API Seite ein, kann es sogar vorkommen, dass der User die Datei schon gecached hat.. und man spart sogar traffic.

  5. tecM0 sagt:

    Dean Edwards und andere JS-Gurus haben sich die letzten jahre ja schon reichlichst am onLoad-Event ausgetobt…

    Ich habe den ganzen Wahnsinn früher auch “zu Fuss” erledigt, aber seit geraumer Zeit schwöre ich auf jQuery… write less, do more. Und das ist kein markiger Werbespruch 😉

  6. codestyling sagt:

    Gerade im Umfeld von WordPress oder ähnlichen Systemen, die bereits Bibliotheken “an Bord” haben, plädiere ich sogar dafür, die Bordmittel einzusetzen.
    Dies spart Supportaufwand. Wenn x eigene Script-Schnipsel, die sich dann mit Frameworks ins Gehege kommen, weil sie was überschreiben oder so nennen wie im Framework, am Laufen sind, dann sind diese als kritisch für Produktivsysteme anzusehen.
    Wer eine Seite von vorn herein ohne Frameworks aufsetzt und das alles selbst schreibt, ok. Aber überall wo Frameworks bereits benutzt werden, würde ich ein gepflegtes jQuery vorziehen und schliess mich tecMO an.

  7. Jan sagt:

    Gut, das ist klar. Wenn man sowieso Frameworks nutzt (z.B. weil das eingesetzte CMS sie einsetzt), dann sollte man dessen Funktionen auch nutzen. Mir ging es eher um performante Seiten ohne bzw. mit selbst gebautem CMS. Alles, was auf einem fertigen CMS aufsetzt, ist normalerweise nicht performant, weil mit Kanonen auf Spatzen geschossen wird.

  8. Jerry sagt:

    Hallo Jan,

    ich finde Deine Lösung sehr schön – und ich brauche für meine Site kein großes Framework!

    Wie kann ich in der aufgerufenen Funktion Parameter übergeben?

  9. Jan sagt:

    @Jerry: Danke 😉
    Parameter im klassichen Sinne kannst Du der Funktion nicht übergeben, weil sie eben ein Event-Handler ist (da ist automatisch das Event selbst der Parameter). Du könntest höchstens eine globale Variable außerhalb der Funktion setzen und drin dann darauf zugreifen.
    Aber eigentlich ist ein Parameter als Reaktion auf ein Event nicht nötig. Was hast Du denn vor? Vielleicht kann ich dann Genaueres sagen.

  10. Jerry sagt:

    @Jan: Danke für Deine Antwort! Ach klar, ich kann ja von eine globale Variable einfach abfragen ^^ – ich hätte erst einmal nachdenken sollen und dann die Frage posten …

    Danke noch einmal!

  11. [teK] sagt:

    Danke Jan! Du hast mir damit super weitergeholfen. 🙂
    Lange schon suchte ich eine Alternative zum onload-Event.

    Greetz!

  12. Kreezer sagt:

    Also der JavaScript-Code für den IE ist völlig falsch!
    Was soll das “interactive” ODER “complete”? Der IE durchläuft beide Phasen… Das heißt “readyState()” wird zweimal aufgerufen.
    Führe für die Funktion “readyState()” entweder eine static-Variable ein oder prüfe anders, ob die Funktion schon ausgeführt wurde!

  13. gnar sagt:

    völlig falsch sagt er,
    wenn beide werte = true sind führt
    er die function trotzdem einmal aus..
    ist vollkommen ok so…
    manche leute blasen sich immer zu sehr auf..
    aber typisch für it – foren

  14. GhostGambler sagt:

    Die Funktion wird aber in das „readyStateChange“-Event eingespeist und wird insofern mehrmals aufgerufen – gegenüber dem „domReady“-Event, welches nur einmal aufgerufen wird, nämlich wenn der DOM-Baum „ready“ ist. Infolgedessen wird auch func() ggf. mehrmals aufgerufen.

    Ich würde ohne weiteren Beleg auch unterstreichen, dass der IE-Part so falsch ist.

    Das sehen auch nicht nur wir so:
    „It’s possible the test should be “this.readyState == ‘loaded’ || this.readyState == ‘complete'”, but that risks triggering twice.“
    http://unixpapa.com/js/dyna.html

  15. nik sagt:

    @GhostGambler: Könnte man ja so machen und einen doppelten Aufruf abfangen. Mich stört ein bissel der Code, da könnte na sicher einiges mit anonymen Funktionen vereinfachen. Irgendwie nicht so hübsch, wenn bereits das ready-Event anfängt, den globalen Scope zuzumüllen.

    window.onDomReady = function (fn) {
    //W3C-compliant browser
    if(document.addEventListener) {
    document.addEventListener(“DOMContentLoaded”, fn , false);
    }
    //IE
    else {
    document.onreadystatechange = function() {
    // DOM is ready
    if (document.readyState == “interactive” || document.readyState == “complete”) {
    fn ();
    document.onreadystatechange = function() {};
    }
    };
    }
    }

    oder?

  16. svenskanda sagt:

    Hej Jan,

    wir schreiben heute das Jahr 2013, und ich bin nach wie vor auch der Meinung – wer von vornherein Frameworks benutzt hat einfach nicht das Verständnis für Javascript,
    was eigentlich schade ist.
    Lernt Javascript!!!
    P.s. Lieber habe ich mein eigenes CMS und verstehe was da abläuft, als das ich das ganze fremde ding komplett analysieren muss 🙁
    Der Lerneffekt ist doch viel größer wenn man sich selbst anstrengt!!

Eine Antwort schreiben

Ihre E-Mail-Adresse wird nicht veröffentlicht. Benötigte Felder sind markiert mit *

You may use these HTML tags and attributes: <a href=""> <blockquote cite=""> <pre lang=""> <b> <strong> <i> <em>