tbe: Dynamische Navigation auf HTML-Dateien in einem anderen Verzeichnis

Liebe Helfer und Spezialisten,

in unserer Schule nutzen wir eine Software, die uns HTML-Dateien mit den Vertretungsplänen der nächsten Tage ausgibt. Diese Dateien werden immer gleichnamig (subst_001.htm, subst_002.htm ...) in einem Verzeichnis abgelegt.

Die Anzahl dieser Dateien kann variieren: Es sind jedoch mind. 7 und max. 20. In jeder dieser subst-Dateien befindet sich ein DIV-Element, das ein Datum enthält: <div class="mon_title">10.10.2018 Mittwoch</div>

Vermutlich gut zu wissen: Das DIV-Element mit der entsprechenden class kommt in der Datei nur EINMAL vor.

Direkt eine Verzeichnisebene darüber möchte in eine eigene HTML-Datei ablegen. Diese soll per Javascript (?) eine Navigation auf die genannten subst-Dateien dynamisch generieren. Die Navigation soll mir das Datum aus jeder subst-Datei holen und mit einem Link auf die jeweilige subst-Datei versehen.

Ist soetwas mit Javascript und mit einfachen Mitteln überhaupt möglich. Anmerkung: PHP steht mir an dieser Stelle nicht zur Verfügung.

Ich dachte daran, die Navigation über einem iframe anzuzeigen, in dem die gewählte subst-Datei angezeigt wird. Denkbar ist auch, die Subst-Datei in ein DIV zu laden wie hier: https://www.xn--fundstcke-im-netz-72b.de/2013/10/24/javascript-navigation/

Ich hoffe, mir kann jemand einen Tipp geben.

Vielen Dank im Voraus.

  1. hallo

    Ist soetwas mit Javascript und mit einfachen Mitteln überhaupt möglich. Anmerkung: PHP steht mir an dieser Stelle nicht zur Verfügung.

    Du brauchst etwas, das den Zustand deines Serverdirektories erkunden kann. Da javascript auf dem Client läuft, ist javascript da falsch.

    PHP dürfte wohl das Mittel der Wahl hier sein.

    Ich dachte daran, die Navigation über einem iframe anzuzeigen, in dem die gewählte subst-Datei angezeigt wird.

    Falls Javascript möglich wäre, gäbe es für einen iframe keinen Bedarf.

    Wenn du aber PHP verfügbar hast, kannst du alle Zugriffe auf deine Detaildateien in .htaccess auf eine PHP-Datei intern umleiten. Diese Datei erstellt dann die erforderliche Navigation und stellt den Inhalt der Detaildatei dar.

    Wenn man ganz auf Serverseitige Techniken wie PHP verzichten will/muss, muss man gezwungenermassen selber eine sitemap erstellen, welche von Javascript dann genutzt werden kann.

    Meine eigene Site funktioniert nach diesem Prinzip.

    1. Hallo,

      Du brauchst etwas, das den Zustand deines Serverdirektories erkunden kann. Da javascript auf dem Client läuft, ist javascript da falsch.

      nein. Diese Seite zeigt in den Options den Inhalt dieses Ordners.

      Gruß
      Jürgen Siehe <> <>

  2. Hallo,

    wenn du in den Ordner mit den subst-Dateien eine Datei mit Namen .htaccess und dem Inhalt Options +Indexes legst, siehst du, wenn du den Ordner-URL als Adresse im Browser eingibst, die Namen der Dateien im Ordner.

    Das sieht bei mir dann so aus.

    Den Ordnerinhalt kannst du dann auch per XMLHttpRequest lesen, auswerten, die Dateien lesen und die Inhalte dann anzeigen.

    Gruß
    Jürgen

    1. hallo

      Hallo,

      wenn du in den Ordner mit den subst-Dateien eine Datei mit Namen .htaccess und dem Inhalt Options +Indexes legst, siehst du, wenn du den Ordner-URL als Adresse im Browser eingibst, die Namen der Dateien im Ordner.

      Das sieht bei mir dann so aus.

      Den Ordnerinhalt kannst du dann auch per XMLHttpRequest lesen, auswerten, die Dateien lesen und die Inhalte dann anzeigen.

      An diese Möglichkeit habe ich gar nicht gedacht. Ausgeliefert wird bei dir html3.2. Da stellt sich dann die Frage, kann man sich auf diese Form verlassen um es mit Javascript auszulesen?

      1. Hallo,

        ich verwende Options +Indexes schon seit ewigen Zeiten, am Aussehen/Format hat sich da bisher nichts geändert. Der Webserer muss nur .htaccess unterstützen.

        Gruß
        Jürgen

        1. Hi,

          ich verwende Options +Indexes schon seit ewigen Zeiten, am Aussehen/Format hat sich da bisher nichts geändert. Der Webserer muss nur .htaccess unterstützen.

          Das Verzeichnis auszulesen ist hier doch gar nicht nötig. Es ist ja bekannt, daß die Dateien einem Namensschema entsprechend durchnumeriert sind (Zitat: subst_001.htm, subst_002.htm ...)- einfach die mit der Nummer 1 holen, dann die mit der 2, usw., bis die 404 als Antwort kommt.

          cu,
          Andreas a/k/a MudGuard

          1. Hallo Andreas,

            stimmt, das sollte sogar einfacher umzusetzen sein, über eine onload-Kette von Requests.

            Gruß
            Jürgen

          2. Lieber Andreas, Lieber Jürgen,

            vielen Dank für Eure Unterstützung. Sorry, dass ich erst jetzt dazu komme, mir eure Tipps anzusehen…

            Vorweg, ich hatte das offensichtlich nicht vollständig beschrieben:

            • Ich habe keine lokale Umgebung, sondern eine Serverumgebung. Die Dateien befinden sich in einer Dokumentenbibliothek eines Sharepointservers.
            • Hier steht kein PHP zur Verfügung (schrieb ich bereits). JavaScript funktioniert aber.

            Es ist so, wie Andreas es gedeutet hat: Das Verzeichnis auszulesen ist nicht nötig. Die Dateien sind bekannt.

            Mein Problem besteht darin, dass ich den Inhalt eines konkreten DIV-Elements rausholen möchte und in einen Link daraus machen möchte.

            Ein JavaScript in meiner Navigations-HTML soll wenn möglich aus jeder der vorliegenden subst-Dateien dieses hier

            <div class="mon_title">10.10.2018 Mittwoch</div>

            finden, holen und mit Link anbieten. Z.B. so:

            <a href="Pfad/subst_001.htm" class="MenuTag" target="iframe" title="10.10.2018 Mittwoch">10.10.2018 Mittwoch</a>

            Mein Probleme:

            • Ich kann keine Programmiersprache und weiß nicht, ob das mit externen Dateien überhaupt möglich ist.
            • Falls ja: Wie sieht der Code aus?

            Vielleicht könnt ihr in dieser Hinsicht helfen.

            Danke euch.

            1. Hallo,

              das sieht nach mehr aus, als „mal eben im Forum helfen“. Wegen

              • Ich kann keine Programmiersprache

              suchst du jemanden, der das für dich programmiert.

              Vielleicht könnt ihr in dieser Hinsicht helfen.

              Ich leider nicht.

              Gruß
              Jürgen

              1. Sorry Jürgen,

                vermute, du hast mich falsch verstanden. Ich suche nicht jemanden, der für mich programmiert.

                Ich möchte 1. wissen,…

                ... Zitat: "... ob das mit externen Daten überhaupt geht."

                Für diese Antwort suchte ich KnowHow und keine Arbeitszeit.

                und 2. wollte ich nach einem Code fragen.

                Ich dachte, es könnte irgendwie ein "getelement...(xxx)" geben, mit dem ich den externen Dateiinhalt eines bekannten DIV holen kann.

                Nichts für Ungut und sorry, wenn ich den Eindruck hinterlassen habe, jemanden meine Arbeit machen zu lassen.

                Das war nicht meine Absicht. Ich suchte nur Support bzw. einen Impuls, um ein wenig weiter zu kommen.

                Liebe Grüße

                1. Hallo,

                  ... Zitat: "... ob das mit externen Daten überhaupt geht."

                  wahrscheinlich ja. Kannst du die subst_001.htm im Browser aufrufen? Liegt die Seite, in die der Link eingebaut werden soll, auch auf diesen Server? Evtl. ist das Stichwort CORS für dich von Interesse.

                  Für diese Antwort suchte ich KnowHow und keine Arbeitszeit.

                  Stichworte/Themen:

                  und 2. wollte ich nach einem Code fragen.

                  Ich glaube nicht, das es da was fertiges gibt.

                  Ich suchte nur Support bzw. einen Impuls, um ein wenig weiter zu kommen.

                  Die Stichworte und Links oben sind aber nur ein kleiner Schubs.

                  Gruß
                  Jürgen

                  1. Danke für deine erneute Antwort. Wenn ich weiß, dass es geht, lohnt es sich, weiter zu suchen.

                    Danke auch für die Links. Das ist mir dann Hilfe genug, wenn es keine einfachen und fertigen Codes geben sollte.

                    Viele Grüße

                    1. Finde die Aufgabe interessant und habe ein kleines Beispiel in Javascript programmiert.

                      Schau dir erstmal den Code der Datei http://osmer.de/studien/datei_grabber_1.htm an. Der Text zwischen <!-- ab hier --> und <!-- bis hier --> soll von datei_grabber.htm ausgelesen werden.

                      Dann rufe http://osmer.de/studien/datei_grabber.htm auf. Sie holt den String und zeigt ihn per alert an.

                      Schaue dir danach auch den Code dieser Datei an. Ist nicht kompliziert.

                      Gruß, Linuchs

                      1. Hallo Linuchs,

                        var my_request = new XMLHttpRequest(); // Mozilla, Safari, Opera

                        nur eine Anmerkung zum Kommentar: XMLHttpRequest kann in den meisten Fällen auch im IE ab 10 und im Edge benutzt werden. Siehe: https://caniuse.com/#search=XMLHttpRequest.

                        Gruß
                        Jürgen

                      2. Die Aufgabe besteht darin, Dateien mit fortlaufender Nummer einzulesen. Aber wie stellt man fest, wann Ende ist?

                        /*
                        onreadystatechange
                        Stores a function (or the name of a function) to be called automatically each time the readyState property changes
                        
                        readyState
                        Holds the status of the XMLHttpRequest. Changes from 0 to 4:
                        0: request not initialized
                        1: server connection established
                        2: request received
                        3: processing request
                        4: request finished and response is ready
                        
                        status
                        200: "OK"
                        404: Page not found
                        */
                        
                      3. Lieber Linuchs,

                        herzlichen Dank! Du hast offenbar genau verstanden, was ich brauche. Das hilft mir schon sehr und hat meinen Bedarf fürs Erste gelöst. Ich kann die Strings holen und einbinden. Im Moment ist die Anzahl der Dateien bekannt und begrenzt auf 5. So kann ich die nötigen Strings sicher aus den Dateien holen und in ein einfaches Menü samt Link einfügen. Funktioniert Super und erfüllt seinen Zweck!!

                        Später wird es aber noch auszubauen sein. Es bleibt zwar weiterhin klar, wie die Dateien heißen: subst_xxx.htm Allerdings ist dann Anzahl der Dateien unbekannt: Mindestens 7, maximal 20 (bis subst_020). Der Übersicht halber wäre es natürlich schön, das ginge mit einer Schleife.

                        Nun habe ich versucht, deinen Code mit einer Schleife anzustoßen. Das gelingt mir noch nicht 😟. Offensichtlich liegt es mindestens an der Übergabe meines Zählers in die Funktion.

                        So sieht mein derzeitiger Stand aus:

                        <script>
                        var my_request = new XMLHttpRequest(); // Mozilla, Safari, Opera
                        
                        for (a=1; a<21; a++) {
                          // die Zahl mit führender Null einfügen
                        	if (a < 10) { 
                            var url = "subst_00"+a+".htm";
                          } 
                          else {
                            var url = "subst_0"+a+".htm";
                          }
                          my_request.open('post', url, true);         // Request öffnen
                        	my_request.send(null);                      // Request senden
                        	my_request.onreadystatechange = auswertung(a); // Funktion, Request auswerten
                        }
                        	
                        function auswertung(a) {
                        	if ( my_request.readyState == 4 && my_request.status == 200 ) {
                        	empfangene_html = my_request.responseText;
                            var start = empfangene_html.indexOf( '<div class="mon_title">' ) +23; // Start String
                            var ende  = empfangene_html.indexOf( '<table class="mon_list" >' ) -8; // Ende String
                            var my_string = empfangene_html.substring(start, ende);
                        	  var inhalt = '<a href="' + url + '" target="iframe">' + my_string + '</a>'
                        	  document.getElementById('zielid'+a).innerHTML = inhalt;
                          }	
                          }
                        </script>
                        <ul>
                        	<li id="zielid1">Heute</li>
                        	<li id="zielid2">Morgen</li>
                        	<li id="zielid3">in 3 Tagen</li>
                        	<li id="zielid4">in 4 Tagen</li>
                        	<li id="zielid5">in 5 Tagen</li>
                        	<li id="zielid6">in 6 Tagen</li>
                        	<li id="zielid7">in 7 Tagen</li>
                          ...
                        </ul>
                        

                        Dankbare Grüße

                        tbe

                        1. hallo

                          Nun habe ich versucht, deinen Code mit einer Schleife anzustoßen. Das gelingt mir noch nicht 😟. Offensichtlich liegt es mindestens an der Übergabe meines Zählers in die Funktion.

                          Du musst für jeden Request ein eigenes Request-Objekt erzeugen.

                          for (var a=1; a<21; a++) {
                            var url = "subst_"+ ("000"+a).match(/\d\d\d$/) +".htm"; 
                            prepareXHR(url, a);
                          }
                          
                          function prepareXHR(url, a){
                            var xhr = new XMLHttpRequest()
                            xhr.open('post', url, true);
                            xhr.onreadystatechange = function(){ auswertung(xhr, a); }
                            xhr.send(null);
                          }
                          
                          function auswertung(xhr, a){
                            if ( xhr.readyState == 4 ){
                              if( xhr.status == 304 || xhr.status == 200 ) {
                                 // dein Code hier.
                                 // ...
                              }
                              else{
                                console.log(xhr.status, xhr.response);
                              }
                            }
                          }
                          

                          Ferner wollen wir auch in der Regel akzeptieren, falls der Status 304 ist.

                          1. hallo

                            for (var a=1; a<21; a++) {
                              var url = "subst_"+ ("000"+a).match(/\d\d\d$/) +".htm"; 
                              prepareXHR(url, a);
                            }
                            

                            Ehrlich, bitte nur auf localhost... Davon kann man eigentlich nur abraten.

                          2. Hallo,

                            Du musst für jeden Request ein eigenes Request-Objekt erzeugen.

                            das würde ich nicht machen. Ich würde auch auf eine Schleife verzichten. Ich würde erst im onreadystatechange-Handler[1] den nächsten Aufruf starten. Bei der ersten Datei, die es nicht mehr gibt, wird die Kette beendet.

                            Gruß
                            Jürgen


                            1. Ich würde allerdings mit dem onload-Handler arbeiten. ↩︎

                            1. hallo

                              Du musst für jeden Request ein eigenes Request-Objekt erzeugen.

                              das würde ich nicht machen. Ich würde auch auf eine Schleife verzichten. Ich würde erst im onreadystatechange-Handler[^1] den nächsten Aufruf starten. Bei der ersten Datei, die es nicht mehr gibt, wird die Kette beendet.

                              Das wäre sicher vorzuziehen, vor allem weil dann eine geregelte Folge von Dokument Responses vorliegt.

                              Aber um sauber zu arbeiten, muss jeder Request ein neues frisches Objekt sein.

                              1. Lieber beatovich,

                                danke für deine Unterstützung. Während du mir geschrieben hast, war ich schon an einer Form mit Schleife, die funktionierte. Danke auch für den 304-Hinweis - ist eingebaut.

                                Meine Lösung sieht fürs Erste nun so aus.

                                <script>
                                
                                window.onload = function(){
                                
                                var f = (function(){
                                        var my_request = [], i;
                                        for(i = 1; i < 21; i++){ //for loop
                                            
                                			(function(i){
                                                my_request[i] = new XMLHttpRequest();
                                                
                                				if (i < 10) { 
                                					var url = "subst_00"+i+".htm";
                                				  } 
                                				  else {
                                					var url = "subst_0"+i+".htm";
                                				  }
                                				
                                				my_request[i].open("post", url, true);
                                                my_request[i].onreadystatechange = function(){
                                                    if (my_request[i].readyState === 4 && my_request[i].status === 200){
                                                        empfangene_html = my_request[i].responseText;
                                						var start = empfangene_html.indexOf( '<div class="mon_title">' ) +23; // Start String 
                                						var ende  = empfangene_html.indexOf( '<table class="mon_list" >' ) -8; // Ende String
                                						var my_string = empfangene_html.substring(start, ende);
                                						var inhalt = '<a href="' + url + '" target="vertretungstag">' + my_string + '</a>'
                                						document.getElementById('zielid'+i).innerHTML = inhalt; 
                                                    }
                                					else {
                                					console.log(my_string.status, my_string.response)
                                					}
                                                };
                                                my_request[i].send();
                                            })(i);
                                        }
                                    })();
                                	}
                                </script>	
                                

                                Ich werde deinen Vorschlag aber noch studieren und schauen, was ich bei dir abschauen kann.

                                Danke.

                                tbe

                                1. Hallo tbe,

                                  IIFEs in allen Ehren, aber du übertreibst ein bisschen damit 😀

                                  var f = (function(){
                                     ...
                                  })();
                                  

                                  Die brauchst Du nicht, das Ganze ist ja schon in eine Function gekapselt die an onload zugewiesen ist.

                                  Bei der inneren IIFE fände ich es besser, wenn das eine eigenständige Funktion wäre. Allerdings etwas umsortiert. Schleifensteuerung und Bildung des Dateinamens nach außen, und dann eine Funktion, die sich NUR um den XMLHttpRequest kümmert. Das Prinzip heißt Aufgabentrennung (oder Separation of Concerns) und ist ein wichtiges Entwurfsprinzip für Programme. Es gibt Sprachen, in denen Funktionsaufrufe teuer sind, aber JavaScript gehört nicht dazu.

                                  Unabhängig von diesen strukturellen Überlegungen gibt es auch noch einen Hinweis zur Serverbelastung. Du schickst definitiv 20 HTTP Requeste los, egal wieviele Dateien vorhanden sind. Das hat den Vorteil, dass dadurch die maximale Anzahl an parallelen Requests unterwegs ist. Es hat aber den Nachteil, dass der Server bei 5 Files 15 unnötige Requeste bekommt.

                                  Deswegen schlug Jürgen ja vor, die nächste Datei erst anzufordern wenn Du die Antwort vom vorigen Request hast. Man KANN sich eine Mischform denken, bei der Du mehrere XMLHttpRequest Objekte hast und mehrere Dateien parallel anforderst. Immer, wenn ein Request zurückkehrt, schickst Du über ihn die nächste Anforderung los. Sobald einer HTTP 404 liefert, ist Schluss.

                                  Ich habe da mal was vorbereitet…

                                     function fetchFiles() {
                                        var fileNumber = 0;
                                        ["Adam", "Bernd", "Cäsar", "Dieter"].forEach(function(name) {
                                           var req = createRequest(name);
                                           fetchNextFile(req);
                                        });   
                                        
                                        function createRequest(name) {
                                           var request = new XMLHttpRequest();
                                           request["x-name"] = name;
                                           request.addEventListener("load", handleFileReceived);
                                           return request;
                                        }
                                        
                                        function fetchNextFile(req) {
                                           if (fileNumber < 0) 
                                              return;
                                           fileNumber++;
                                           var fileName = "file_"+("000"+fileNumber).substr(-3)+".txt";
                                           
                                           console.log(req["x-name"] + ": Start downloading " + fileName);
                                           req["x-fileNumber"] = fileNumber;
                                           req["x-fileName"] = fileName;
                                           req.open("GET", fileName, true);
                                           req.send();
                                        }
                                        
                                        function handleFileReceived() {
                                           if (this.status == 404) {
                                              fileNumber = -1;
                                              console.log("No more files.");
                                           }
                                           else if (this.status == 200 || this.status == 304) {
                                              var receivedText = this.responseText;
                                              var receivedNumber = this["x-fileNumber"];
                                              var receivedName = this["x-fileName"];
                                              
                                              fetchNextFile(this);   // Vor der Verarbeitung schonmal den nächsten Fetch losschicken
                                              
                                              verarbeite_html(this["x-name"], receivedNumber, receivedName, receivedText);
                                           }
                                        }
                                        function verarbeite_html(workerName, fileNumber, fileName, text) {            
                                           console.log("Datei #" + fileNumber + " (" + fileName + ") von " + 
                                                       workerName + " empfangen, " + text.length + " Zeichen.");
                                        }
                                     }
                                  

                                  Die Funktion erzeugt 4 XMLHttpRequests und erweitert sie um drei private Eigenschaften. Das würde man normalerweise über Symbol-Objekte machen, aber ich bin ein IE11-Junkie und der kann keine Symbols. Darum heißen die Eigenschaften x-name, x-fileNumber und x-fileName. Diese Eigenschaften sind nötig, damit der load-Eventhandler weiß, was er da bekommen hat. Es werden ja 4 Handler parallel laufen, und es gibt nur eine Handler-Funktion :). Statt der x-Eigenschaften könnte man sich auch ein Kapselobjekt je XMLHttpRequest denken, das diese Infos enthält.

                                  In createRequest wird dem Requestobjekt ein Name gegeben (just for fun) und gleich der erste Request gestartet. Die Steuerung, welche Datei abgerufen wird, erfolgt über eine Variable in fetchFiles, d.h. aus Sicht der Arbeitsfunktionen eine globale Variable. JavaScript führt Code rein sequenziell aus, es gibt daher keine Synchronsisierungsprobleme beim Zugriff auf diese Variable.

                                  Das Starten eines Requests erfolgt über fetchNextFile. Hier wird der Dateiname gebildet, die Dateinummer und der -name in den privaten Eigenschaften des Requestobjekts gespeichert und der Request gestartet (open und send). Send wartet nicht, d.h. die vier createRequest-Aufrufe erfolgen direkt hintereinander, während die Requestdaten gerade durch die Leitung flutschen.

                                  Irgendwann trifft die erste Antwort ein und auf dem betreffenden Handler wird das load Event gefeuert. Wichtig ist auch hier: Solange dieses Load-Event verarbeitet wird, müssen weitere eintreffende Load-Events warten. Es kann keinen Konflikt zwischen zwei "gleichzeitig" laufenden Eventhandlern geben. Pro Load-Event wird handleFileReceived aufgerufen. Die Funktion prüft, ob Status 404 zurückkam und setzt dann fileNumber auf -1, als Abbruchsignal. Bei Status 200 oder 304 wird der responseText sowie Dateinummer und -name in Variablen gelegt und der nächste Request gestartet, damit der Server sich nicht langweilt. Danach wird der empfangene Text verarbeitet. Das Kopieren in Variablen ist wichtig, weil das XMLHttpRequest Objekt durch fetchNextFile schon für den nächsten Request verwendet wird, während die empfangenen Daten noch verarbeitet werden.

                                  Rolf

                                  --
                                  sumpsi - posui - clusi