heinetz: String per JS effizient splitten

Hallo Forum,

ich will diese Methode als Äquivalent zu jQuery $.load() umbauen.

  getModalContent() {
    return new Promise((resolve, reject) => {
      if (this.modalContentUrl) {
        addClass(this.elements.self, this.modalContentLoadingClass);
        const ajax = new XMLHttpRequest();

        ajax.open('GET', this.modalContentUrl, true);
        ajax.send();
        ajax.onload = () => {
          if (ajax.status === 200) {
            this.elements.body.innerHTML = ajax.responseText;
            resolve(ajax.status);
          } else {
            reject(ajax.status);
          }
        };
        ajax.ontimeout = () => {
          reject(ajax.timeout);
        };
      } else {
        resolve(true);
      }
    });
  }

Die bestehende Methode erwartet in dem Parameter this.modalContentUrl eine Url. Bspw. /folder/file.html. Im Gegensatz dazu kann man der jQuery-Methode load als Parameter folgendes mitgeben:

/folder/file.html .class

Das versuche ich gerade umzusetzen. Im Moment scheitere ich daran, dass ich zwar ajax.responseText aber nicht ajax.responseXML zur Verfügung habe.

Kann man da auf den ersten Blick sehen, warum das nicht geht?

danke für Tipps und

beste gruesse, heinetz

  1. hallo

    Ohne dir helfen zu können, ist mir aufgefallen dass du mit

          if (ajax.status === 200) {
            this.elements.body.innerHTML = ajax.responseText;
            resolve(ajax.status);
          } else {
            reject(ajax.status);
          }
    

    Eigentlich die Möglichkeit des Caches (status 304) verwirfst. Das sollte man ohne guten Grund für

    ajax.open('GET', ....

    nicht tun.

  2. Was verstehst Du unter String splitten? Splitten zu was? MfG

  3. @@heinetz

    Du verwendest Promise (modern), aber XMLHttpRequest (alt) – das passt nicht zusammen. Warum nicht das Fetch API?

    LLAP 🖖

    --
    „Wer durch Wissen und Erfahrung der Klügere ist, der sollte nicht nachgeben. Und nicht aufgeben.“ —Kurt Weidemann
    1. Ich hab's nicht geschrieben aber es funktioniert. Ich würde es nur gerne auf meine Bedürfnisse anpassen.

      gruss, heinetz

  4. Hallo heinetz,

    die jQuery-Methode lädt ein HTML Fragment und extrahiert dann daraus einen Container. Wie sie das macht, steht auf api.jquery.com:

    jQuery uses the browser's .innerHTML property to parse the retrieved document and insert it into the current document. During this process, browsers often filter elements from the document such as <html>, <title>, or <head> elements. As a result, the elements retrieved by .load() may not be exactly the same as if the document were retrieved directly by the browser.

    Man müsste sich den jquery sourcecode anschauen um zu sehen wie sie es genau machen, aber ICH würde so vorgehen:

    • html als text vom server holen
    • mit createDocumentFragment ein DocumentFragment erzeugen
    • darin ein div anlegen (weil das DocumentFragment kein innerHTML hat)
    • dem innerHTML des div das geladene html zuweisen
    • in dem div mit querySelector den gewünschten Container lokalisieren
    • den clonen und den Klon im eigenen DOM einsetzen

    Damit nutzt Du soweit es möglich ist nativen Browsercode. Wie jQuery das macht, weiß ich nicht, die wollen ja auch alte Browser supporten die kein DocumentFragment kennen; ich mutmaße mal, dass jQuery dafür ein verstecktes Dummy-div ins DOM legt und das geladene HTML temporär darin unterbringt.

    Rolf

    --
    sumpsi - posui - clusi
    1. hallo

      Hallo heinetz,

      die jQuery-Methode lädt ein HTML Fragment und extrahiert dann daraus einen Container. Wie sie das macht, steht auf api.jquery.com:

      jQuery uses the browser's .innerHTML property to parse the retrieved document and insert it into the current document. During this process, browsers often filter elements from the document such as <html>, <title>, or <head> elements. As a result, the elements retrieved by .load() may not be exactly the same as if the document were retrieved directly by the browser.

      Man müsste sich den jquery sourcecode anschauen um zu sehen wie sie es genau machen, aber ICH würde so vorgehen:

      • html als text vom server holen

      warum nicht als html

      • mit createDocumentFragment ein DocumentFragment erzeugen

      unntötig wenn als html

      • darin ein div anlegen (weil das DocumentFragment kein innerHTML hat)

      unnötg, wenn als html

      • dem innerHTML des div das geladene html zuweisen
      • in dem div mit querySelector den gewünschten Container lokalisieren
      • den clonen und den Klon im eigenen DOM einsetzen

      So ähnlich mach' ich es:

      	_.xhr = new XMLHttpRequest();
      	_.xhr.open("GET",_.sitemapUri);
      	_.xhr.responseType = 'document';
      	_.xhr.addEventListener('load', function(event) {
      		if( _.xhr.status == 200 || _.xhr.status == 304 ){
      			_.searchitems = _.xhr.responseXML.querySelectorAll('[data-for~="searchform"] a[href]');
      			_.sitemap = _.xhr.responseXML.querySelector('[data-for~="navigation"]');
      			if( _.sitemap ){
      				document.querySelector(".addSitemapContent").appendChild( _.sitemap );
      				if(_.searchitems) searchform(_.searchitems);
      			}
      			setAriaCurrentPage();
      			// initialise functions
      			_.functions = document.querySelectorAll("script[data-initfunction]");
      			for(var i=0; i<_.functions.length;i++){
      				_.funcname = _.functions[i].getAttribute("data-initfunction");
      				window[_.funcname](); 
      			}
      		} else {
      			console.warn(_.xhr.statusText);
      		}
      	});
      	_.xhr.send();
      
      
      1. Hallo beatovich,

        autsch. Einfacher geht's nicht…

        Rolf

        --
        sumpsi - posui - clusi
    2. Hallo Rolf,

      https://developer.mozilla.org/de/docs/Web/API/DOMParser wäre auch noch eine Option. Den verwende ich bei einer xml-Datei, da ich mich auf responseXML nicht verlassen kann.

      Gruß
      Jürgen

      1. Ja, damit hatte ich's dann auch gelöst:

          getModalContent() {
            return new Promise((resolve, reject) => {
              if (this.modalContentUrl) {
                const [contentUrl, contentSelector] = this.modalContentUrl.split(/\s/);
                addClass(this.elements.self, this.modalContentLoadingClass);
                const ajax = new XMLHttpRequest();
        
                ajax.open('GET', contentUrl, true);
                ajax.send();
                ajax.onload = () => {
                  if (ajax.status === 200) {
                    if (contentSelector) {
                      const contentElement = new DOMParser()
                        .parseFromString(ajax.responseText, 'text/html')
                        .querySelector(contentSelector);
                      this.elements.body.innerHTML = '';
                      this.elements.body.appendChild(contentElement);
                      this.contentElement = contentElement;
                    } else {
                      this.elements.section.innerHTML = ajax.responseText;
                    }
                    // history.pushState({ url: contentUrl }, null, contentUrl);
                    resolve(ajax.status);
                  } else {
                    reject(ajax.status);
                  }
                };
                ajax.ontimeout = () => {
                  reject(ajax.timeout);
                };
              } else {
                resolve(true);
              }
            });
          }
        
        1. Hallo Forum,

          in meiner Lösung steht (auskommentiert) mein erster Anlauf mit der history-API. Ich habe damit ein wenig herum experimentiert, würde das Verhalten nun aber gerne vorher mal planen. Helft Ihr mir dabei?

          Wie man an dem Code erkennen kann, lade ich per Ajax Content in ein Modal-Popup. Was ich erreichen möchte ist grundsätzlich, dass in der Adresszeile, die "richtige" URL steht wenn man ... navigiert. D.h.

          1. Beim Aufruf der Seite "home.html" ist das Modal initial nicht sichtbar ... in der Adresszeile steht also "home.html".

          2. Per Click auf der Seite wird das Modal sichtbar und der Inhalt von "page1.html" per Ajax nachgeladen ... in der Adresszeile soll also "page1.html".

          3. Wird das Modal geschlossen soll in der Adresszeile wieder "home.html" stehen.

          Bis dahin habe ich's relativ einfach hinbekommen. Interessant wird's hier:

          Beim initialen Aufruf der Seite und bei der Benutzung der Back/Forward-Buttons des Browsers soll sich nicht nur die Adresszeile ändern, sondern der entsprechende Zustand abgebildet werden.

          Dafür würde ich gerne einen Ansatz entwickeln.

          gruss, heinetz