Rolf B: jQuery array mit each(function) zusammenlegen

Beitrag lesen

Hallo Maxet,

ich versuche mal, mein Verständnis der Dinge wiederzugeben und darauf einen Lösungsvorschlag aufzubauen.

Aber vorweg: Wenn es Dir darum gehen sollte, die Daten aus den Eingabefeldern einzusammeln um sie per $.post an den Server zu senden, dann lege besser ein form um alles und benutze jQuery.serialize. Oder nimm einfach das FormData-Objekt, das die heutigen Browser alle kennen - sogar schon der Internet Explorer 10.

Zu meinem Vorschlag der auf deiner Lösungsstruktur basiert:

Du hast eine HTML Seite. Die besteht aus 2 gleichartigen Blöcken, jeder davon besteht aus einem select, zwei text-Inputs und einer textarea.

Kleiner Copy/Paste Fehler am Rande: die zweite textarea darf nicht die gleiche ID wie die erste haben. Eine Suche nach der zweiten textarea mittels ID wird nicht funktionieren

Kleines HTML Missverständnis: input ist ein inhaltsloses Element. Es gibt nur <input>, kein </input>. Darum ist etwas wie <input type="text" /> in normalem HTML falsch, das Tag endet mit >, nicht mit />. Es sei denn, du machst XHMTL, aber das wäre sehr ungewöhnlich. Bei <textarea> ist es anders, dieses Element hat Inhalt, und </textarea> muss immer explizit geschrieben werden.

Großer HTML Fehler mittendrin: Eingabe-Elemente benötigen eine Beschriftung. Für eine Spielseite, die den Datentransfer testen soll, ist es egal. Aber denk an die <label> Elemente wenn Du damit live gehst

Wenn ich dein Musterarray richtig deute, dann möchtest Du ein Array mit zwei Einträgen haben. Der erste Eintrag enthält ein Array mit den Inhalten aus dem ersten HTML Block, in der Reihenfolge Select-Wert, input-Wert 1, input-Wert 2, textarea-Wert. Der zweite Eintrag enthält ein gleichartiges Array für den zweiten HTML Block.

Was ich damit nicht übereinanderbringe, sind deine beiden jQuery each Aufrufe. Der erste sammelt alle select-Werte ein, der zweite alle input Werte, und die textarea-Werte werden gar nicht gelesen.

Um die gewünschte Arraystruktur erhalten zu können, empfehle ich Dir zunächst einmal eine gleichartige Gliederung des HTML. Derzeit hast Du 4 div-Gruppen, wovon jeweils 2 zusammengehören. Ich würde <fieldset> vorschlagen, das erzeugt einen Rahmen, und mit <legend> auch eine Überschrift im Rahmen, so wie eine Groupbox in Desktop-UIs. Das Beispiel zeigt auch die beiden Möglichkeiten, wie Du Labels setzen kannst: Als umschließendes Element, oder separat, aber mit for und id verknüpft.

<fieldset>
   <legend>Eingabegruppe 1</legend>
   <label>Optionen: <select ... /select></label>
   <label for="input1a">Eingabe 1:</label><input id="input1a" ...>
   <input ...>
   <textarea>
</fieldset>
<fieldset>
   <legend>Eingabegruppe 2</legend>
   ...
</fieldset>

Nachdem Du diese Struktur eingebaut hast, solltest Du zweistufig vorgehen. Die äußere Stufe verarbeitet die fieldsets. Wenn Du noch andere fieldsets hast, musst Du entweder Klassen vergeben oder einen weiteren Container drum herum legen und den jQuery Selector entsprechend anpassen:

var arr = $("fieldset")
            .map(function() {
                   var fvalues = $("input,textarea,select", this)
                                  .map(function() { return this.value; })
                                  .get();
            })
            .get();

Dieser "Einzeiler" hat es in sich. Die map Funktion für jQuery-Sets wendet die übergebene Funktion auf jedes Element des Sets an, das vom $(...) Aufruf gebildet wurde. D.h. das äußere Map wird einmal pro Fieldset aufgerufen. Die Callback-Funktion von map bekommt zwar Parameter - das kannst Du Dir im Zweifel in der jQuery-Doku durchlesen - aber einfacher ist es, this zu verwenden, weil jQuery die Callback-Funktion so aufruft, dass this ebenfalls auf das gerade verarbeitete Element zeigt.

Wenn Dir map völlig rätselhaft ist - es macht eigentlich überhaupt nichts anderes als dein each, das seine Ergebnisse per push in ein Array schreibt. map nimmt Dir das Anlegen des temporären Arrays und den push ab. Unterschied ist nur, dass das map von jQuery kein Array erzeugt, sondern ein jQuery-Set. Dieses Set kann man mit der get Funktion in ein Array umwandeln.

Der innere jQuery Aufruf sammelt alle Inhalte des Fieldset ein. Ich weiß nicht ob Du diese Form des jQuery-Aufrufs kennst: Wenn die Variable elem auf ein Objekt des DOM verweist, dann sammelt $("input", elem) alle input-Elemente ein, die innerhalb dieses Objekts zu finden sind.

Ein Selektor "input,textarea,select" findet alle select-, input- und textarea-Elemente, und zwar in der Reihenfolge, wie sie im DOM stehen, nicht so sortiert wie die Teile im Selektor angegeben sind. Auf diese Sammlung wird wieder map angewendet, mit einer Callback-Funktion, die lediglich die ausgewählten Werte herausholt. Ergebnis ist ein jQuery-Set, das die in den Eingabeelementen ausgewählten oder eingegebenen Werte enthält.

Auf dieses neue Set wird die get Funktion angewendet, um ein normales Array daraus zu machen. Und dieses Array wird an das äußere map zurückgegeben. Weil der äußere map die fieldsets verarbeitet, produziert dies nun ein jQuery-Set aus Arrays mit Feldinhalten. Ein letztes get() macht aus diesem Set wieder ein Array.

Wenn Dir diese Schachtelung zu wüst ist, kann man das auch trennen. Die Callback-Funktion des äußeren map kann auch eine benannte, allein stehende Funktion sein:

var arr = $("fieldset").map(sammleFieldsetWerte).get();

function sammleFieldsetWerte() {
   return $("input,textarea,select", this)
                    .map(function() { return this.value; })
                    .get();
}

Rolf

--
sumpsi - posui - clusi