Linuchs: Ajax feuert mehrfach - Problem, die Antworten zuzuordnen

Moin,

ich statte eine Webseite mit mehreren (z.B. 30) Help-Symbolen aus, jedes hat eine ID:

<img id="VIP_1102" class="help" src="img/icon_info.png" alt="info" />

nach Laden der Seite wird denen ein Klick-Event zugeordnet:

window.addEventListener('DOMContentLoaded', function ( ) {
  var erster  = document.getElementsByTagName( "body" )[0].firstChild;
  var newDiv  = document.createElement("div");
  newDiv.id   = "helptext";
  document.body.insertBefore( newDiv, erster ); 

/* *************************************************
   *
   * HELP-Icons/Buchstaben anschliessen
   *
   ************************************************* */
  obj_help = document.getElementsByClassName( "help" );
  for ( i=0; i<obj_help.length; i++ ) {
    if ( obj_help[i].id ) {
      obj_help[i].addEventListener('click', function (event) {
        getHelptextXY( bia_domain, bia_owner, bia_kw, bia_ll, this.id, bia_lg, event.clientX, event.clientY );
      });
    }
  }
});

Wenn man draufklickt, wird per Ajax der Help-Text zu diesem Feld geholt und angezeigt. So weit, so gut.

Nun möchte ich das erweitern und nach dem Laden des HTML-Dokuments alle Help-Symbole der Seite "durchhecheln", um diejenigen auszugrauen, für die (noch) kein Helptext vorliegt. Ich sende also vermutlich im Millisekunden-Takt 30 Anfragen los und irgendwann - undefiniert - trudeln die Antworten ein.

Doch wie kann ich wissen, welche Antwort zu welcher ID gehört?

Zuerst habe ich die ID der Anfrage ausserhalb der Funktionen gespeichert und in der Antwort-Funktion genutzt. Das klappte nicht, denn vermutlich haben nachfolgende Ajax-Anfragen die ID längst neu gesetzt und bei ankommendem request wurde "irgendeine" ID genommen.

Dann habe ich versucht, die ID zu übergeben:

  ...
  request.onreadystatechange = zeigeHelptext(id); // Request auswerten
  ...
function zeigeHelptext(id) {
... 
}

Ohne Angabe eines Fehlers wurde nun kein Helptext mehr angezeigt, auch nicht bei Klick. Ich nehme an, die bei request.onreadystatechange anzuspringende Funktion darf nicht mit Parameter versehen werden.

Wie - zum Teufel - kann ich Frage und Antwort zuordnen, wenn Ajax wie ein Maschinengewehr feuert?

Auch hier wieder geschlossene Benutzergruppe, leider kein Online-Beispiel.

Gruß, Linuchs

  1. hallo

    obj_help = document.getElementsByClassName( "help" );

    Besser

    obj_help = document.querySelectorAll(".help");

    request.onreadystatechange = zeigeHelptext(id); // Request auswerten

    Wenn schon:

    request.onreadystatechange = function(){ zeigeHelptext(id); }

    • Sammle zuerst alle Ids.
    • POSTE diese als JSON Object an den Server.
    • Lass die IDs mit Inhalt ergänzen und sende JSON Object zurück.
    • Arbeite die Liste der IDs einmal ab.

    Summa summarum 1 Request pro Client.

  2. Moin,

    Wie - zum Teufel - kann ich Frage und Antwort zuordnen, wenn Ajax wie ein Maschinengewehr feuert?

    Ganz einfach: Jeder Request bekommt eine eindeutige Id die auch über die Response geschleift wird. Ob Du die in einen dedizierten Header setzt oder in die Request/Response Datenstrukturen packst bleibt Dir überlassen. Ich würd's in einen x-Header setzen da musst Du nicht den ganzen Code ändern. Also xhr.setRequestHeader('x-id','123'); und xhr.getResponseHeader('x-id');

    Falls man die ganze Problematik nicht auch über einen einzigen Request/Response Zyklus abwickeln könnte.

    MfG

    1. xhr.setRequestHeader('x-id','123'); und xhr.getResponseHeader('x-id');

      Nicht verstanden. Ich habe Folgendes gemacht:

      function getHelptextXY( host, owner_id, kw, ll, feldname, lg, x, y ) {
        // Mausposition sichern
        help_x  = x;
        help_y  = y;
        if ( typeof request == "undefined" ) erzeugeRequestObject();
        var url = "http://" +host +"/000/p033_ajax.php?owner_id=" +owner_id +"&kw=" +encodeURIComponent(kw) +"&ll=" +ll +"&feldname=" +feldname +"&lg=" +lg;
        request.open('post', url, true);            // Request öffnen
        request.setRequestHeader('feldname',feldname); // Test
        request.send(null);                         // Request senden
        request.onreadystatechange = zeigeHelptext; // Request auswerten
      }
      
      function zeigeHelptext() {
        if ( request.readyState == 4 && request.status == 200 ) {
          // Test
          alert( "ResponseHeader=[" + request.getResponseHeader('feldname') + "]" );
      ... 
      }
      

      Antwort: ResponseHeader=[null]

      1. xhr.setRequestHeader('x-id','123'); und xhr.getResponseHeader('x-id');

        Nicht verstanden. Ich habe Folgendes gemacht:

        Schon falsch. Custom-Header müssen mit x- beginnen.

        Und dann guck mal in die Konsole, ob der gesendet wird. Serverseitig musst Du denselben Header natürlich auch setzten. Den dazugehörigen Wert findest Du im Array mit der Serverumgebung $_SERVER als HTTP_X_ID wenn der Header beim Senden x-id hieß.

        MfG

        PS: Da Du feldname sowieso schon in der Parameterliste hast, brauchst Du dafür keinen Customheader. Für die Response analog, d.h., den Header musst Du nur setzen wenn feldname nicht im Body gesendet wird.

  3. Hallo Linuchs,

    es ist die Frage, ob deine "Gib mir die Hilfedaten" Ajaxmethode hier das richtige Werkzeug ist. Denn - wenn Du ohnehin beim Seitenstart alle Hilfedaten abfragst, ist die ganze Idee mit asynchroner Beschaffung ad absurdum geführt. Dann kannst Du auch gleich beim Ausliefern der Seite ein Javascript-Objekt mit allen verfügbaren Hilfedaten in die Seite hineingenerieren.

    Es ist auch die Frage, ob ein Ajax-Feuerwerk sinnvoll ist. Browser führen nicht beliebig viele Requeste parallel durch, das Limit ist relativ klein (2 oder 4). D.h. es dauert eine ganze Weile, bis alle Requests durch die Queue sind.

    Deswegen würde ich Dir die Anregung von beatovich und PL sehr ans Herz legen: Sammle alle IDs, schick einen Request ab, und was Du dann tust, hängt vom Volumen deiner Hilfe ab. Wenn es nicht zu viel ist und Bandbreite keine Rolle spielt, dann rotz einfach alle Hilfeinformationen raus und lass sie vom Client cachen. Wenn das zu viel ist, schick ein Array zurück mit der ID als Key und true/false als Wert. true für "Es gibt Hilfe".

    Das alles hat aber nichts damit zu tun, dass die Fragestellung interessant ist: Wie bändige ich parallel laufende Ajax-Requeste. Mein Vorschlag wäre hier, für jeden Ajax-Aufruf eine Funktion aufzurufen, die alle Daten als Parameter bekommt, die für die Zuordnung der Response wichtig sind. Die readystatechange-Handlerfunktion, die du INNERHALB DIESER FUNKTION an den Ajax-Request anhängst, hat dann alle diese Parameter in Form einer Closure zur Verfügung. Und weil Du die Funktion für jeden Request neu aufrufst, wird auch jedesmal eine neue Closure mit neuen Inhalten gebildet.

    Closures sind ein kniffliges Gebilde - ich will jetzt nicht einfach loserklären. Wenn Du dazu Fragen hast: es steht was dazu im Wiki, aber natürlich kannst Du sie auch noch stellen, ich mache dann ein Beispiel.

    Rolf

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

      wenn Du ohnehin beim Seitenstart alle Hilfedaten abfragst, ist die ganze Idee mit asynchroner Beschaffung ad absurdum geführt.

      Ich trenne Programme (.php) und Templates mit Platzhaltern (.htm).

      Viele Platzhalter-Dateien enthalten diese Help-Funktionen, die ich mit einer zentralen .js - Datei ohne PHP verwalte.

      Und in dem heutigen Spezial-Fall geht es um eine Veranstaltung, die im textarea (Namens-Liste) mit Hilfen gespickt ist, von denen PHP gar nichts weiss.

      1. hallo

        wenn Du ohnehin beim Seitenstart alle Hilfedaten abfragst, ist die ganze Idee mit asynchroner Beschaffung ad absurdum geführt.

        Ich trenne Programme (.php) und Templates mit Platzhaltern (.htm).

        Viele Platzhalter-Dateien enthalten diese Help-Funktionen, die ich mit einer zentralen .js - Datei ohne PHP verwalte.

        Da würde ich dir raten diese Hilfen periodisch (bzw bei Erzeugung eines Templates) über ein Spezialscript in eine eigene Datei zu exportieren, und bei Bedarf diese einzubinden und deren Daten ins DOM bauen.

  4. hi

    Ohne Angabe eines Fehlers wurde nun kein Helptext mehr angezeigt, auch nicht bei Klick. Ich nehme an, die bei request.onreadystatechange anzuspringende Funktion darf nicht mit Parameter versehen werden.

    So ist es. Du kannst jedoch innerhalb dieser Funktion per this auf das xhr bzw requestObjekt zugreifen. Und damit hast Du auch die Response samt Responseheader.

    MfG