Erzwingen Sie das erneute Auslösen aller (Skripte / Funktionen) bei Inhalten mit Ajax

Lesezeit: 14 Minuten

Ich haz kodes Benutzeravatar
Ich habe kode

tl;dr

Ich verwende Ajax, um neue Inhalte abzurufen. Der Inhalt wird abgerufen und der Seite hinzugefügt. Skripte werden jedoch nicht “erneut ausgelöst”, da ihre Aufrufe außerhalb des Ajax-Codes liegen div.

Die Skripte werden beim ersten Laden der Seite problemlos geladen und ausgelöst, aber nicht, wenn ich neuen Inhalt über Ajax hinzufüge. Ich erhalte keine Konsolenfehler und es gibt keine Probleme, wenn ich die URL direkt aufrufe.


Verwandt:

  1. Ausführung des Skripts auf einer geladenen AJAX-Seite erzwingen – Bezieht sich auf ein bestimmtes Skript. Ich möchte alle Skripte, einschließlich dynamischer, reparieren (neu auslösen). Cloudflare-Apps
  2. Verwenden von jQuery-Skript mit Ajax in WordPress – Wie oben, bezieht sich dies nur auf ein bestimmtes Skript
  3. ajax geladener Inhalt, Skript wird nicht ausgeführt

Einleitung:

ich benutze Ajaxify-WordPress-Site (AWS) auf einer WordPress-Website.

Das Plugin lässt Sie eine auswählen div anhand seiner ID und ruft dann neue Inhalte darin ab div mit Ajax

HTML-Markup

<html>

<head></head> 

<body>
  <div id="page"> 
    <header></header>
    <main id="ajax"> <!-- This is what I want to ajaxify -->
      <div class="container">
        main page content
      </div> 
    </main> <!-- end of ajaxfied content  -->
    <footer></footer>
  </div> <!-- #page -->
</body>

</html>

Problem

Das Plugin funktioniert und ich bekomme neue Inhalte geladen und gestaltet, aber es gibt ein Problem. Einige meiner Seite Skripte und Funktionsaufrufe befinden sich außerhalb des div, für das ich Ajax verwende. Ich habe zwei Beispiele

1- Mauerwerk wird geladen und eingezogen <footer>

<html>

<head></head> 

<body>
  <div id="page"> 
    <header></header>
    <main id="ajax"> <!-- This is what I want to ajaxify -->
      <div class="container">
        main page content
      </div>  
    </main>   <!-- end of ajaxfied content  -->
    <footer></footer> <!-- Masonry script is loaded and called here -->
  </div> <!-- #page -->
</body>

</html>

2- Google Maps-Aufruf ist in der <head>

<html>

<head></head>  <!-- Google maps is called here -->

<body>
  <div id="page"> 
    <header></header>
    <main id="ajax"> <!-- This is what I want to ajaxify -->
      <div class="container">
        main page content
      </div>  
    </main>  <!-- end of ajaxfied content  -->
    <footer></footer> 
  </div> <!-- #page -->
</body>

</html>

Das sind nur zwei Beispiele. Es gibt andere an anderen Orten. Wie Sie sehen können, werden solche Skripte nicht erneut aufgerufen, da das einzige, was auf der Seite neu geladen wird, ist <main id="ajax">. Während der neue Inhalt darin <main> Gibt eseinige der Skripte, die zum ordnungsgemäßen Rendern erforderlich sind, werden nicht erneut aufgerufen, und so lande ich bei fehlenden Elementen / defektem Layout.


Ich bin nicht der einzige, der mit diesem Problem konfrontiert war; ein kurzer Blick auf die Plugins Support-Forum auf wordpress.org zeigt, dass dieses Problem häufig vorkommt.

Hinweis: Ich würde nicht versuchen, dies zu beheben, wenn das Plugin viele andere Probleme hätte. Es funktioniert für mich, ich brauche nur die Skripte zum erneuten Auslösen.

Die offizielle Antwort ist, dass es möglich ist, Skripte neu zu laden / erneut auszulösen, indem man Folgendes in die PHP-Dateien des Plugins einfügt:

$.getScript(rootUrl + 'PATH TO SCRIPT');

Was funktioniert. Es funktioniert gut. zum Beispiel wenn ich hinzufüge

$.getScript(rootUrl + '/Assets/masonry.js');

Dann werden die Masonry-Funktionsaufrufe erneut ausgelöst, wenn der Ajax-Inhalt abgerufen wird, selbst wenn masonry.js außerhalb des Ajax-Inhalts geladen wird div

Ich verweise auf die Plugin-Dateien auf github für mehr Klarheit darüber, was der Fix tatsächlich tut (ich kann nicht verstehen, was wann passiert $.getScript wird genutzt)


Zusammenfassend

Der offizielle Fix funktioniert gut, wenn Sie 3-4 Skripte haben, die für Ajax-Inhalte erneut ausgelöst werden müssen.

Dies funktioniert nicht für mein Ziel, weil

  1. es ist zu starr und fest codiert
  2. Einige der Skripte werden der Seite dynamisch über hinzugefügt Cloudflare-Apps

Eine mögliche Lösung könnte darin bestehen, ein Ereignis hinzuzufügen, das den Auslöser nachahmt, der bewirkt, dass die Skripts am Ende des Ajaxings ausgelöst werden div

Frage:

Wie erzwinge ich alle Skripte – einschließlich dynamisch hinzugefügter – erneut auslösen, wenn nur ein bestimmter Teil der Seite über Ajax neu geladen wurde?


Anmerkungen:

  1. Ich versuche zu vermeiden, Skripte einzeln aufzurufen, da dies die Kenntnis ihrer Aufrufe im Voraus erfordern würde. Ich bin wahrscheinlich über meinen Kopf reden aber…

    ich versuche zu nachahmen die Seitenlast und/oder Document Ready Events – bei denen die meisten Bedingte Skripte werden ausgelöst (korrigiere mich, wenn ich falsch liege) – am Ende von <main> in meinem HTML, wenn neuer Ajax-Inhalt hinzugefügt wird, aber ohne das Dokument zu beeinflussen, wenn die Seite über die direkte Verwendung der URL geladen wird … oder ein anderer ähnlicher Ansatz.

  2. Nur für ein bisschen Kontext, Hier ist eine Liste einiger Ereignis-Listener auf der Seite während das Plugin ausgeschaltet ist. Ich weiß, dass da Dinge drin sind, die ich nicht auslösen muss. Ich habe dies nur als Referenz hinzugefügt. Bitte beachten Sie auch, dass dies ein Beispiel von einer der Seiten ist. andere Seiten können abweichen.

DOMContentLoaded
beforeunload
blur
click
focus
focusin
focusout
hashchange
keydown
keyup
load
message
mousedown
mousemove
mouseout
mouseover
mouseup
mousewheel
orientationchange
readystatechange
resize
scroll
submit
touchscreen
unload

  • Alle Ihre Plugins verfügen möglicherweise über Methoden, die es ihnen ermöglichen, das zu tun, was sie beim Laden der Seite tun, auch nachdem die Seite geladen wurde. zB Mauerwerk, (das ich persönlich nicht kenne) legt a frei masonry() -Methode für die Objekte von jQuery. Sie können diese Methoden also anstelle von zweifelhaften Hacks verwenden.

    – Kaiido

    15. August 2017 um 5:00 Uhr


  • Was bedeutet ‘tl;dr’ in Ihrer Frage? Anfang der Frage…

    – Ich bin die dümmste Person

    15. August 2017 um 5:01 Uhr

  • @DonkeyKing acronymfinder.com/TLDR.html => “Too long; Don’t Read” (Lesen Sie nicht alles und springen Sie zum Ende / zur Zusammenfassung / zum Schluss, wenn Sie möchten)

    – Vivek Athalye

    16. August 2017 um 4:44 Uhr

  • @VivekAthalye Danke 🙂 Ich dachte, es wird nur im Stack Exchange-Netzwerk verwendet …

    – Ich bin die dümmste Person

    16. August 2017 um 4:48 Uhr

Benutzeravatar von cjg
cjg

Welche Lösung Sie hier wählen, hängt davon ab, wie die Skripte initialisiert werden. Es gibt ein paar allgemeine Möglichkeiten:

  1. Die Aktionen des Skripts werden unmittelbar nach dem Laden des Skripts aufgerufen. In diesem Fall könnte das Skript etwa so aussehen:

    (function() {
         console.log('Running script...');
    })();
    
  2. Die Aktionen des Skripts werden als Reaktion auf ein bestimmtes Ereignis ausgelöst (z. B. Document Ready (JQuery) oder Window Onload (JavaScript)-Ereignisse). In diesem Fall könnte das Skript etwa so aussehen:

    $(window).on('load', function() {
        console.log('Running script...');
    });
    

Einige Optionen für diese beiden Möglichkeiten werden im Folgenden betrachtet.

Für Skripte, die sofort beim Laden ausgeführt werden

Eine Möglichkeit wäre, die einfach zu entfernen <script> Elemente, die Sie aus dem DOM auslösen möchten, und hängen Sie sie dann erneut an. Mit JQuery könnten Sie tun

$('script').remove().appendTo('html');

Wenn sich das obige Snippet selbst in a befindet <script> -Tag, dann erstellen Sie eine Endlosschleife, in der Sie alle Skripte ständig lösen und erneut anhängen. Darüber hinaus gibt es möglicherweise einige Skripts, die Sie nicht erneut ausführen möchten. In diesem Fall können Sie den Skripten Klassen hinzufügen, um sie entweder positiv oder negativ auszuwählen. Zum Beispiel,

// Positively select scripts with class 'reload-on-ajax'
$('script.reload-on-ajax').remove().appendTo('html');

// Negatively select scripts with class 'no-reload'
$('script').not('no-reload').remove().appendTo('html')

In Ihrem Fall würden Sie eines der obigen Snippets in den Ereignishandler für die AJAX-Vervollständigung einfügen. Das folgende Beispiel verwendet einen Schaltflächenklick anstelle eines AJAX-Abschlussereignisses, aber das Konzept ist dasselbe (beachten Sie, dass dieses Beispiel in der StackOverflow-Sandbox nicht gut funktioniert, versuchen Sie also, es als separate Seite zu laden, um das beste Ergebnis zu erzielen). :

<html>
  <head></head>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

  <script class="reload-on-ajax">
    console.log('Script after head run.');
  </script>

  <body>
    <button id="reload-scripts-button">Reload Scripts</button>
  </body>

  <script class="reload-on-ajax">
    console.log('Script after body run.');
  </script>

  <script>
     $('#reload-scripts-button').click( () => $('script.reload-on-ajax').remove().appendTo('html') );
  </script>
</html>

Beachten Sie, dass, wenn die Skripte nicht inline sind (z. B. über die src -Attribut), dann werden sie je nach Browser und Einstellungen vom Server neu geladen oder aus dem Browser-Cache abgerufen. In diesem Fall ist es wahrscheinlich am besten, alle zu entfernen <script>s, die mit den AJAX-geladenen Inhalten arbeiten, und sie über etwas wie das von JQuery laden/ausführen getScript() Funktion aus einem AJAX-Abschlussereignishandler. Auf diese Weise werden Sie die Skripte nur einmal zum richtigen Zeitpunkt laden/ausführen (d. h. nachdem der AJAX-Inhalt geladen wurde):

// In AJAX success event handler
$.getScript('script1.js');
$.getScript('script2.js');

Ein potenzielles Problem bei beiden Varianten dieses Ansatzes besteht darin, dass das asynchrone Laden des Skripts ursprungsübergreifenden Einschränkungen unterliegt. Wenn die Skripte also auf einer anderen Domain gehostet werden und Cross-Origin-Anfragen nicht erlaubt sind, funktioniert es nicht.

Für Skripts, die als Reaktion auf ein Ereignis ausgeführt werden

Wenn die Skripte beim Laden des Fensters ausgelöst werden, können Sie einfach dieses Ereignis auslösen:

   $(window).trigger('load');

Wenn die Skripte selbst das Document-Ready-Ereignis von JQuery verwenden, kenne ich leider keine einfache Möglichkeit, es manuell auszulösen. Es ist auch möglich, dass die Skripte als Reaktion auf ein anderes Ereignis ausgeführt werden.

Natürlich können Sie die oben genannten Ansätze nach Bedarf kombinieren. Und, wie andere bereits erwähnt haben, wenn es einige Initialisierungsfunktionen in den Skripten gibt, die Sie einfach aufrufen könnten, dann ist das der sauberste Weg.

Benutzeravatar von Benni
Benni

Wenn Sie in Ihren externen Skripten eine globale Initialisierungsfunktion oder einen Codeblock identifizieren können, können Sie sich das Ereignis „ajaxComplete“ ansehen. Sie können diesen Code in Ihren Seitenkopf einfügen und die initialisierenden Funktionsaufrufe oder Codeblöcke in den ajaxComplete-Callback einfügen.

$(document).ajaxComplete(function(){
    module1.init();
    $('#my_id').module2_init({
        foo : 'bar',
        baz : 123
    });
});

Wenn die Skripte, von denen Sie sprechen, keine so einfach zu verwendenden exponierten Initialisierungsfunktionen haben, sich aber beim Laden des Skripts selbst initialisieren, wird es meines Erachtens keine sofort einsatzbereite Methode geben, die für alle Skripts funktioniert.

Hier ist, was Sie versuchen können –

Die meisten Skripte wie Mauerwerk oder Google Map sind so eingestellt, dass sie beim Ändern der Fenstergröße neu initialisiert werden. Wenn Sie also das Größenänderungsereignis auslösen, nachdem Ajax abgeschlossen ist, hilft es, diese Skripte automatisch erneut auszulösen.

Versuchen Sie den folgenden Code –

$( document ).ajaxComplete( function() {
    $( window ).trigger('resize');
} );

Dadurch werden die Skripte gezwungen, neu zu starten, sobald Ajax abgeschlossen ist, da es nun das Resize-Ereignis auslöst, nachdem der Inhalt geladen wurde.

Benutzeravatar von lscmaro
lscmaro

Vielleicht riskant, aber Sie sollten in der Lage sein, es zu verwenden DOMSubtreeModified auf Ihrem <main> Element dafür.

$('#ajaxed').bind('DOMSubtreeModified', function(){
  //your reload code
});

Dann sollten Sie in der Lage sein, alle Ihre Skripte einfach erneut in Ihrem Reload-Bereich anzuhängen

var scripts = document.getElementsByTagName('script');
for (var i=0;i<scripts.length;i++){
   var src = scripts[i].src;
   var sTag = document.createElement('script');
   sTag.type="text/javascript";
   sTag.src = src;
   $('head').append(sTag);

}

Eine andere Option könnte darin bestehen, einen eigenen Ereignis-Listener zu erstellen und denselben Neuladecode darin zu haben.

$(document).on('ajaxContentLoaded', function(){//same reload code});

Dann könnten Sie ein Ereignis im Plugin auslösen, sobald der Inhalt aktualisiert wurde.

$(document).trigger('ajaxContentLoaded');

Oder möglicherweise eine Kombination aus der Bearbeitung des Plugins, um einen Listener auszulösen, und dem Hinzufügen zu Ihrer Codebasis, um alles erneut auszuführen, was Ihrer Meinung nach diesen Listener erneut ausführen muss, anstatt etwas neu zu laden.

$(document).on('ajaxContentLoaded', function(){
   //Javascript object re-initialization
   myObj.init();
   //Just a portion of my Javascript?
   myObj.somePortion();
});

Benutzeravatar von Hitmands
Hitmands

Eine Lösung könnte darin bestehen, alle Skripte zu duplizieren …

$(document).ajaxSuccess((event, xhr, settings) => {
  // check if the request is your reload content
  if(settings.url !== "myReloadedContentCall") {
    return;
  }

  return window
    .setTimeout(rejs, 100)
  ;
});

function rejs() {
  const scripts = $('script[src]');
  // or, maybe alls but not child of #ajax
  // const scripts = $('*:not(#ajax) script[src]');

  Array
    .prototype
    .forEach
    .call(scripts, (script, index) => {
      const $script = $(script);

      const s = $('<script />', {
        src: $script.attr('src')
      });  
      // const s = $script.clone(); // should also work

      return s
        .insertAfter($script)
        .promise()
        .then(() => $script.remove()) // finally remove it
      ;
    })
  ;

}

Ich hatte genau dieses Problem, als ich versuchte, mit Ajax eine Seite mit Browserzuständen und history.js in WordPress neu zu laden. Ich habe history.js direkt in die Warteschlange gestellt, anstatt ein Plugin zu verwenden, um dies für mich zu tun.

Ich hatte Tonnen von JS, die “erneut ausgeführt” werden mussten, wenn auf eine neue Seite geklickt wurde. Dazu habe ich eine globale Funktion in meiner Haupt-Javascript-Datei mit dem Namen erstellt global_scripts();

Stellen Sie zunächst sicher, dass diese JS-Datei nach allem anderen in Ihre footer.php eingereiht wird.

Das könnte etwa so aussehen:

wp_enqueue_script('ajax_js', 'url/to/file.js', 'google-maps', 1, true);

Mein Javascript, das ich in die Warteschlange stelle, ist unten.

jQuery(document).ready(function($) {

    // scripts that need to be called on every page load
    window.global_scripts = function(reload) {


       // Below are samples of the javascript I ran that I needed to re run.
       // This includes lazy image loading, paragraph truncation.

       /* I would put your masonry and google maps code in here. */

        bLazy = new Blazy({
            selector: '.featured-image:not(.no-blazy), .blazy', // all images
            offset: 100
        });


        /* truncate meeeee */
        $('.post-wrapper .post-info .dotdotdot').dotdotdot({
            watch: 'window'
        });

    }

    // call the method on initial page load (optional)
    //global_scripts();

});

Ich habe mich dann in das JS eingehängt, das immer dann ausgeführt wurde, wenn eine Seite mit history.js geladen wurde, und global_scripts() aufgerufen.

Es scheint, als ob das von Ihnen verwendete Plugin auch history.js verwendet. Ich habe es nicht speziell mit Ihrem Plugin getestet, aber Sie können versuchen, was ich unten gepostet habe (was ich in meiner App verwende). Dies würde in die gleiche JS-Datei oben gehen.

History.Adapter.bind(window, 'statechange', function() { 
     global_scripts();
}

Mein Code ist etwas komplizierter als das, was oben einfach eingefügt wird. Ich überprüfe das Ereignis tatsächlich, um festzustellen, welche Inhalte geladen werden, und lade darauf basierend bestimmte JS-Funktionen. Es ermöglicht mehr Kontrolle darüber, was neu geladen wird.

Hinweis: Ich verwende window.global_scripts beim Deklarieren der Funktion, da ich separate JS-Dateien habe, die diesen Code enthalten. Sie können sich dafür entscheiden, dies zu entfernen, wenn sie alle gleich sind.

note2: Mir ist klar, dass diese Antwort genau wissen muss, was neu geladen werden muss, daher wird Ihre letzte Notiz ignoriert, in der Sie gefragt werden, dass dies kein solches Wissen erfordert. Es kann Ihnen trotzdem helfen, Ihre Antwort zu finden.

Benutzeravatar von Nils Rückmann
Nils Rückmann

Kurze Antwort: Das ist nicht generisch möglich. Woher soll Ihr Skript wissen, welche Ereignisse ausgelöst werden müssen?

Längere Antwort: Es ist eher eine strukturelle als eine programmatische Frage. Wer ist für die gewünschte Funktionalität verantwortlich? Nehmen wir als Beispiel Mauerwerk:

Masonry.js selbst hört auf keine Ereignisse. Sie müssen selbst eine Masonry-Instanz erstellen (was höchstwahrscheinlich auf domready in Ihrem WordPress-Plugin erfolgt). Wenn Sie die Größenänderungsoption aktivieren, wird dem Größenänderungsereignis ein Listener hinzugefügt. Aber was Sie eigentlich wollen, ist ein Zuhörer auf “DOM-Inhalt ändern” (siehe https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver für mögliche Lösung). Da masonry.js eine solche Funktion nicht bereitstellt, haben Sie folgende Möglichkeiten:

  1. Warten Sie auf die Implementierung (oder machen Sie es selbst) in masonry.js
  2. Warten Sie auf die Implementierung (oder machen Sie es selbst) im Maurer-Wordpress-Plugin.
  3. Fügen Sie die Funktionalität woanders hinzu (Ihre Vorlage, Ihr Ajax-Plugin usw.)
  4. 4.

Wie Sie sehen können, beinhaltet jede Option einige zu erledigende Arbeiten, und diese Arbeit muss für jedes Skript durchgeführt werden, das Sie auf Ihre von AJAX aufgerufenen DOM-Änderungen hören möchten.

1406890cookie-checkErzwingen Sie das erneute Auslösen aller (Skripte / Funktionen) bei Inhalten mit Ajax

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy