T-Rex: Seite per Javascript laden - Javascript ausführen

Moin,

hab da eine Webseite. Die hat mehrere Seiten. Wenn ich auf einer Seite bin und ein Link geklickt wird, dann möchte ich den Content der zweiten Seite per Javascript laden und anzeigen. Soweit kein Problem.
Jedoch haben einige Seiten ein Javascript Code, welches nach der Seiten-Generierung abläuft (onload). Wenn ich die Seite jedoch mittels Ajax lade ist onload bereits abgelaufen. Ich müsste also alle Javascript Funktionen nochmals manuell aufrufen. Und das kann unter Umständen zu viel sein. Also einfache Frage, wie löst man sowas am einfachsten?

Gruß
nachgelagerter
T-Rex

  1. Hallo,

    Ich nehme einmal an, du weist den Inhalt mit innerHTML oder so zu.

    Die Scripte werden dabei nicht ausgeführt, das musst du manuell machen, indem du im hinzugefügten Teilbaum alle script-Elemente suchst (document.getElementByTagName('script')), den Inhalt ausliest (text- oder textContent-Eigenschaft) und eval auf dem Inhalt aufrufst.

    Diese Scripte können natürlich nicht window.onload = function() {}; oder gleichwertiges enthalten, denn der Code im Handler würde nicht sofort ausgeführt.

    Du brauchst also eine Weiche »wenn das Dokument bereits geladen ist, führe den Code sofort aus, ansonsten beim document ready«. Erst dann kann der Code separat und eingebettet ausgeführt werden.

    Beispiel:

    var onDomReady = (function() {  
      var EVENT_TYPE = 'DOMContentLoaded';  
      // Speichert, ob DOMContentLoaded schon passiert ist  
      var loaded = false;  
      document.addEventListener(EVENT_TYPE, function() {  
        loaded = true;  
      });  
      return function(handler) {  
        if (loaded) {  
          handler();  
        } else {  
          document.addEventListener(EVENT_TYPE, handler);  
        }  
      };  
    })();
    

    Dann kannst du überall Code so einbinden:

    <script>  
    onDomReady(function() {});  
    </script>
    

    Dieser Code kann problemlos mit eval() ausgeführt werden.

    Das macht jQuerys $(document).ready() übrigens automatisch. jQuery führt auch automatisch alle Scripte aus, wenn du HTML mit $().html() ins DOM schreibst (was nicht ganz unproblematisch ist, weil es schnell zu XSS führt). Ich würde stark raten, solche Operationen mit jQuery durchzuführen.

    Hintergrund:
    http://molily.de/js/event-handling-onload.html
    http://api.jquery.com/ready/
    http://api.jquery.com/html/
    https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
    http://api.jquery.com/jQuery.globalEval/
    http://perfectionkills.com/global-eval-what-are-the-options/

    Mathias

    1. Klingt alles super. Nur habe ich kein <script>. Naja eins halt, das bindet ein externes Script ein. Dort stehen alle jquery ready Sachen drin. Mit Eval ist da denke ich nix mehr zu machen?

      Danke für deine Hilfe

      Gruß
      Vergaloppierter
      T-Rex

      1. Hallo,

        Nur habe ich kein <script>. Naja eins halt, das bindet ein externes Script ein. Dort stehen alle jquery ready Sachen drin. Mit Eval ist da denke ich nix mehr zu machen?

        Zumindest nicht direkt. Du müsstest du das Script mit XMLHttpRequest laden und dann den Inhalt ausführen.

        Mathias

        1. Ja wäre es da nicht einfacher, die onload Sachen irgendwie nochmal aus zu führen? Sind ja schon alle geladen.
          Vielleicht ein jquery.ready().again(); :D

          Gruß
          einfach denkender
          T-Rex

          1. Hallo,

            Ja wäre es da nicht einfacher, die onload Sachen irgendwie nochmal aus zu führen? Sind ja schon alle geladen.

            Inwiefern würde dir das helfen? Welches Problem würde das lösen?

            Du lädst HTML-Code, welches auf ein externes Script verweist. Wenn du dessen JavaScript-Code ausführen willst, dann musst du das Script per JavaScript laden und ausführen.

            Code darin kann ruhig $(document).ready() aufrufen. Der Handler wird dann sofort ausgeführt. So wie ich es im ersten Posting beschrieben habe arbeitet auch jQuerys $(document).ready().

            Mathias

            1. Wieso soll ich das Javascript nochmal laden wenn es doch schon komplett vorhanden ist?
              Lediglich das HTML wird ausgetauscht.

              Das Problem ist, dass alle ready() Sachen bereits ausgelöst sind. Wenn ich z.B. alle Inputfelder blau machen möchte nachdem die Seite geladen wurde dann funktioniert das für die initiale Seite, jedoch nicht für jeglichen Code der per Ajax geladen wird. Außer ich schaffe es die Sachen die in ready() stehen nochmals aus zu lösen.
              Aktuell würde ich es so machen, dass ich alle ready() Sachen in eine externe Funktion ausgliedere und diese nach dem Ajax response nochmals aufrufe. Das ist aber sehr umständlich.
              Deine Methode das Javascript erneut ein zu binden ist natürlich eine Alternative. Es wäre aber schick JQuery irgendwie mitzuteilen das es nochmal alle readys auslösen soll.

              Gruß
              ready()
              T-Rex

              1. Hallo,

                Das Problem ist, dass alle ready() Sachen bereits ausgelöst sind. Wenn ich z.B. alle Inputfelder blau machen möchte nachdem die Seite geladen wurde dann funktioniert das für die initiale Seite, jedoch nicht für jeglichen Code der per Ajax geladen wird.

                Verstehe.

                Allgemein: Du baust hier anscheinend eine komplexere Single-Page-App, aber all deine Scripte funktionieren wie auf clientseitig statischen Websites, die mit Page-Reloads arbeiten und auf DOM-ready aktiv werden.

                Für deine Zwecke ist DOM-ready nicht geeignet, sondern du brauchst eine saubere und automatische Initialisierung für jeden nachgeladenen Inhalt. Simples Ajax, das bloß statische HTML-Inhalte nachlädt und ins Dokument kippt, ist sooo 2005. ;)

                Beim Event-Handling kannst du mit Event-Delegation arbeiten und somit dir eine Re-Initialisierung für nachgeladene Inhalte sparen. jQuery macht das sehr einfach möglich.

                Bei komplexeren UI-Controls geht das natürlich nicht. Hier würde ich zu einer bewährten MVC-Struktur raten. Das machen alle Frameworks für JavaScript-Anwendungen so: Angular, Ember, Backbone/Marionette/Chaplin usw. Das würde ich dir letztlich raten, wenn die JS-Logik komplexer wird.

                Aktuell würde ich es so machen, dass ich alle ready() Sachen in eine externe Funktion ausgliedere und diese nach dem Ajax response nochmals aufrufe. Das ist aber sehr umständlich.

                Ja. Das würde ich nicht empfehlen, denn das wird schnell unübersichtlich und fehleranfällig.

                Du vermischt hier zwei Paradigmen, klassische HTML-Dokumente mit JavaScript, das beim DOM-ready aktiv wird, und Single-Page-Verhalten. Wenn deine Site zu letzterem tendiert, solltest du entsprechende Frameworks und Architekturen verwenden.

                Mathias

                1. Tach!

                  Entschuldigt bitte, dass ich mich mal hier reindrängle. Ich möchte demnächst eine SPA (Single Page Application) aufsetzen und muss mich dazu für ein Framework entscheiden. Leider hatte ich bisher noch nicht die Zeit und Muße gefunden, mich näher mit den Angeboten zu befassen. Daher versuche ich mal auf diesem Weg, Erfahrungswerte abzugreifen, gern auch in Form von Verweisen (aber bitte keine Metaverweise auf Suchmaschinen, das kann ich selbst, ist nur aufwendig, Spreu von Weizen zu trennen). Anforderungen gibts nach derzeitigen Planungen keine, die über 08/15 hinausgehen.

                  Bei komplexeren UI-Controls geht das natürlich nicht. Hier würde ich zu einer bewährten MVC-Struktur raten. Das machen alle Frameworks für JavaScript-Anwendungen so: Angular, Ember, Backbone/Marionette/Chaplin usw. Das würde ich dir letztlich raten, wenn die JS-Logik komplexer wird.

                  Ist in der Reihenfolge der Nennung schon deine Wertung inbegriffen?

                  Vor einiger Zeit hatte ich mal kurz KnockoutJS angeschaut, fand es aber nicht so hübsch, besonders nicht die Art und Weise, wie überwachbare Objekte und Eingenschaften erstellt werden. Dazu sind oftmals Funktionsaufrufe (oder ähnliches) notwendig. Auch Magic Strings mag ich nicht (falls die Verwendung finden). Sowas ist für IDEs nicht gut, weil diese nicht wissen, dass solche Strings letztlich nutzbare Eigenschaften darstellen. Das ist für mich ein wichtiger Punkt, dass die IDE (im konkreten Fall wird das PhpStorm sein) weiß, was es autozuvervollständigen gibt, oder notfalls dieses Wissen aus einem von ihr auswertbaren Kommentar ziehen kann.

                  Dann lief mir AngularJS über den Weg und das sah auf den ersten und zweiten Blick viel gefälliger aus. Da liegt nicht so viel zum Framework gehörender Code in der Anwendung, das arbeitet mit POJOs (Plain Old Javascript Objects) und betreibt seinen nötigen Änderungsüberwachungsmechanismus im Hintergrund. Insofern gefallen mir auch Patterns wie Active Record nicht, zu viel in einer Klasse, Daten und eventuell Geschäftslogik gemischt mit Verwaltungskram.

                  Stimmt mein oberflächlicher Eindruck soweit mit der Realität überein? Haben die anderen Frameworks herausragende Eigenschaften, die man gern haben möchte? Ein Hipness-Faktor interessiert mich jedoch nicht wirklich, ich muss nur effizient produktiv damit arbeiten können. Kurz und pauschal gefragt: Welches ist das beste?

                  dedlfix.

                  1. hi dedlfix,

                    Stimmt mein oberflächlicher Eindruck soweit mit der Realität überein? Haben die anderen Frameworks herausragende Eigenschaften, die man gern haben möchte? Ein Hipness-Faktor interessiert mich jedoch nicht wirklich, ich muss nur effizient produktiv damit arbeiten können. Kurz und pauschal gefragt: Welches ist das beste?

                    Ich finde noch: http://en.wikipedia.org/wiki/Single-page_application#JavaScript_Frameworks (da noch YUI App Framework) und https://plus.google.com/+EugeneKrevenets/posts/Pgz7KgeuUmb und http://discuss.emberjs.com/t/how-do-we-beat-angularjs-in-the-developers-mindset/3948

                    mfg

                    tami

                  2. Hallo,

                    Das ist für mich ein wichtiger Punkt, dass die IDE (im konkreten Fall wird das PhpStorm sein) weiß, was es autozuvervollständigen gibt, oder notfalls dieses Wissen aus einem von ihr auswertbaren Kommentar ziehen kann.

                    Strings werden in Backbone und Ember zum Lesen und Setzen von Modeleigenschaften verwendet, weil so das Observable-Pattern umgesetzt wird. In ReactJS ist ebenfalls ein expliziter Setter-Aufruf mit einem Objektliteral nötig. Der Grund ist einach, dass ES5 Setter und Object.observe() noch nicht breit unterstützt werden.

                    [Bei Angular] liegt nicht so viel zum Framework gehörender Code in der Anwendung, das arbeitet mit POJOs (Plain Old Javascript Objects) und betreibt seinen nötigen Änderungsüberwachungsmechanismus im Hintergrund.

                    Ja, Controller-Scopes in Angular sind einfache Objekte, was wiederum ausschließt, dass die IDE sinnvoll vervollständigt, weil die Typinformation fehlt. Ich weiß nicht, ob sich das typisieren oder annotieren lässt.

                    Insofern gefallen mir auch Patterns wie Active Record nicht, zu viel in einer Klasse, Daten und eventuell Geschäftslogik gemischt mit Verwaltungskram.

                    Das funktioniert gut, solange es wenig konventioneller Code bleibt. Man ist davon abgekommen, entsprechende Models aufzublasen und hält die Implementierungen so schmal wie möglich. Für komplexes Querying, Persistenz, Validierung, Transformation usw. stehen andere Pattern zur Verfügung, die vom Model getrennt sind.

                    Haben die anderen Frameworks herausragende Eigenschaften, die man gern haben möchte?

                    Ember versteht sich mehr als Framework, das Best Practices und Konventionen forciert. Das haben die Ember-Leute gut erklärt:
                    http://www.youtube.com/watch?v=jScLjUlLTLI
                    http://eviltrout.com/2013/06/15/ember-vs-angular.html

                    Angular hat andere Alleinstellungsmerkmale, wie z.B. die Nutzung von Plain-HTML für Templates, die Scope-Chain sowie die Nutzung von Factories.

                    Das ist nun jeweils nichts, was einem fundamental andere Dinge ermöglicht, aber es sind wichtige Architekturentscheidungen.

                    Ich würde auf jeden Fall mit einem halbwegs vollständigen Framework wie Angular oder Ember loslegen. Es gibt viele kleinere und modularere, aber die klammern viele Fragen, die sich fast in jeder Anwendung stellen, kurzerhand aus.

                    Mathias

                    1. Tach!

                      Ich würde auf jeden Fall mit einem halbwegs vollständigen Framework wie Angular oder Ember loslegen.

                      Auf Ember hab ich mittlerweile einen Blick geworfen gehabt, der hat mir aber noch nicht so den Kick gebracht, den Angular erzeugt hat. Das Ember-Tutorial gab mir als ersten Eindruck erstmal Fundamente zu bauen, bevor man richtig loslegt. Grundsätzlich ist das ja nicht verkehrt, wenn man eine Anwendung erstellt. Aber damit verkauft man nicht unbedingt Embers Stärken. AngularJS hingegen zeigte drei Zeilen und die erste kleine Anwendung stand. So geht Erfolgserlebnis. Die beiden werde ich jedenfalls nochmal einer genaueren Analyse unterziehen. Danke für die Entscheidungshilfe.

                      dedlfix.

                2. Event-Delegation habe ich (hoffentlich) überall wo möglich eingebaut. Es gibt aber leider Dinge die funktionieren nur bei ready(). Da gibt es z.B. ein Slider Framework. Das baut nach dem ready() tolle Pfeile, damit man was zum drücken hat. Oder das Chosen Ding, was Tim hier im forum mal ausgegraben hat. Das baut eine Selectbox in eine "suchbox" um. Funktioniert auch nur bei ready().

                  Du vermischt hier zwei Paradigmen, klassische HTML-Dokumente mit JavaScript, das beim DOM-ready aktiv wird, und Single-Page-Verhalten. Wenn deine Site zu letzterem tendiert, solltest du entsprechende Frameworks und Architekturen verwenden.

                  Da hast du recht. Den Begriff Single-Page höre ich jetzt auch zum ersten mal.
                  Generell stell ich mir das relativ einfach vor: Der User klickt auf einen Link. Hat er kein JS aktiv wird der Link normal geladen. Ist JS aktiv wird die gewünschte Seite per Ajax aufgerufen. Auf der Serverseite läuft alles wie gehabt ab. Datenbank wird geladen, Templates werden geladen die Seite wird zusammen gebaut. Anstatt jetzt aber wirklich alles (z.B. header und Menü) zurück zu liefern, gibt der Server nur einen bestimmte HTML Ebene zurück. Bei mir wäre das <main>...</main>. Das JS ersetzt (oder erweitert) den bestehenden main Knoten.
                  Hab schon den Ansatz gesehen das anstatt des kompletten HTML nur ein Objekt zurück gegeben wird. Das wird dann auf JS Seite geparst. Das gefällt Google halt nicht ganz so gut, könnte ich mir vorstellen. Hab von einem Bekannten auch schon negative Erfahrung diesbezüglich gehört - mehr aber auch nicht.
                  Das Single-Page verhalten soll nicht das zentrale Merkmal der Seite werden. Ich möchte eventuell Effekte einbauen (Fading) um so die subjektive Warten des Users abzukürzen. Wenn es zu aufwendig wird mit dem Single-Page verhalten lasse ich es einfach.
                  Aber wie gesagt aktuell stelle ich mir das naiv einfach vor. Bin aber für eine Belehrung offen.

                  Gruß
                  offener
                  T-Rex

                  1. Hallo,

                    Event-Delegation habe ich (hoffentlich) überall wo möglich eingebaut. Es gibt aber leider Dinge die funktionieren nur bei ready().

                    ...Anstatt jetzt aber wirklich alles (z.B. header und Menü) zurück zu liefern, gibt der Server nur einen bestimmte HTML Ebene zurück. Bei mir wäre das <main>...</main>. Das JS ersetzt (oder erweitert) den bestehenden main Knoten.

                    Vielleicht denke ich jetzt etwas naiv, aber statt "DomReady" hast du ja dann "AjaxReady". Einbauen kannst Du ja nur was da ist und wenn es eingebaut ist, dann kannst du auch die entsprechenden Skripten drauf loslassen.

                    Viele Grüße
                    Siri

                    1. Vielleicht denke ich jetzt etwas naiv, aber statt "DomReady" hast du ja dann "AjaxReady". Einbauen kannst Du ja nur was da ist und wenn es eingebaut ist, dann kannst du auch die entsprechenden Skripten drauf loslassen.

                      Ja richtig. Die Frage ist was sind die richtigen Scripte? Jeglicher JS Code ist bei mir in einer Datei und die ist ja bereits geladen. Deshalb hab ich ja schon geschrieben, dass es eine Alternative wäre für alle ready() Sachen eine einzige Funktion zu machen JQueryReady(). Und da sind alle Sachen drin die beim ready() geladen werden. Somit müsste ich beim AjaxReady nur diese Funktion laden.
                      Ich denke aber das es sehr umständlich ist, worin mich molily bereits betätigt hat.
                      Da ich jedoch wie molily empfohlen hat bereits auf event delegation setze könnte es sich nur um ein paar Zeilen handeln die ich in diese zentrale Funktion ausgliedern muss.

                      Genialer wäre aber eine ready().again() möglichkeit gibt oder ein $.trigger("load") oder so.

                      Gruß
                      T-Ready()

                      1. Hallo,

                        Ja richtig. Die Frage ist was sind die richtigen Scripte?

                        Diese Frage stellt sich nicht mehr, wenn man zwei Grundtechniken von JS-Anwendungen nutzt:

                        • Modulare, objektorientierte Interface-Komponenten
                        • Clientseitiges Routing über URLs (es sind auch andere Techniken möglich, um das »richtige Script« zu finden, aber das ist die verbreiteste)

                        Jeglicher JS Code ist bei mir in einer Datei und die ist ja bereits geladen.

                        Das ist das Grundproblem. Der Code sollte besser objektorientiert in Komponenten aufgeteilt sein.

                        Komponenten können Inhalte nachladen und dafür weitere Komponenten instantiieren.

                        Genialer wäre aber eine ready().again() möglichkeit gibt

                        So etwas zu bauen wäre trivial. Du willst es aber nicht! jQuery als solches kennt keine Widget-Abstraktion, würde DOM-Änderungen einfach noch einmal vornehmen und Event-Handler einfach noch einmal hinzufügen. Das würde sehr schnell zu Fehlern führen.

                        Mathias