Maxet: jQuery array mit each(function) zusammenlegen

Ich versuche mit jQuery die Selects und Inputs Werte in einem Array hinzufügen. So das der Array so aussieht:

var testarr=[["Option1","","Input2",""],["Option2","Input1","","Text"]];

mit den leeren Werten.

<div>
  <select id="idTest1" class="select_css" name="selectTest[]">
    <option value="" selected>Option</option>
    <option value="Option1">Option1</option>            
    <option value="Option2">Option2</option>    
  </select>


    <input type="text" class="input_css" data-room="1"  name="input_1[]" />
    <input type="text" class="input_css" data-other="1" name="input_2[]" />
</div>

<div>
  <textarea name="textareaTest[]" placeholder="Test" class="textarea_css" id="textarea1"></textarea>
</div>



<div>
  <select id="idTest2" class="select_css" name="selectTest[]">
    <option value="" selected>Option</option>
    <option value="Option1">Option1</option>            
    <option value="Option2">Option2</option>    
  </select>


    <input type="text" class="input_css" name="input_1[]" />
    <input type="text" class="input_css" name="input_2[]" />
</div>

<div>
  <textarea name="textareaTest[]" placeholder="Test" class="textarea_css" id="textarea1"></textarea>
</div>


<button id="addButton">Values in Array</button>

<div id="arrlist"></div>

Hier komme ich nicht weiter, wie kann ich arraytest und arraytest_1 zusammenlegen so das es wie testarr aussieht?

var arraytest = []; 
$('select').each(function(){ arraytest.push($(this).val()); }); 

var arraytest_1 = []; 
$('input').each(function(){ arraytest_1.push($(this).val()); });
<script>

$("#addButton").on("click", function() {

    var testarr=[["Option1","","Input2",""],["Option2","Input1","","Text"]];

    for(var i=0; i<testarr.length; i++) {
    text = '<div>'+testarr[i][0]+'<br>'+testarr[i][1]+'</div><div>'+testarr[i][2]+'</div>';
    $("#arrlist").append(text);
    }

});
</script>

Danke für die Hilfe

Maxet

  1. 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
    1. Vielen vielen Dank für deine Antwort.

      Was ich erreichen möchte ist, das der Array so aussieht:

      var testarr=[["Option1","","Input2",""],["Option2","Input1","","Text"]];

      und ich ihn dann mit dieser For-Schleife ausgeben kann:

      for(var i=0; i<testarr.length; i++) {
          text = '<div>'+testarr[i][0]+'<br>'+testarr[i][1]+'</div><div>'+testarr[i][2]+'</div>';
          $("#arrlist").append(text);
      }
      

      Ich habe ein Form und dieser Array dient nur dazu da, das ich die Werte nochmals anzeigen kann, bevor der eigentliche Submit von dem Formular stattfindet.

      Und ich habe mehrere solche Blöcke, die sind einfach versteckt. Mit klick auf dem Button "hinzufügen" wird wieder ein neuer Block sichtbar.

      <div>
        <select id="idTest1" class="select_css" name="selectTest[]">
          <option value="" selected>Option</option>
          <option value="Option1">Option1</option>            
          <option value="Option2">Option2</option>    
        </select>
      
      
          <input type="text" class="input_css" data-room="1"  name="input_1[]" />
          <input type="text" class="input_css" data-other="1" name="input_2[]" />
      </div>
      
      1. Hallo Maxet,

        gehört die textarea ins Array oder nicht? Dein Beispiel-Array scheint sie zu enthalten, dein Testoutput verwendet den 4. Eintrag aber nicht.

        Wie gesagt: Es ist einfacher, wenn die Teile, die einen Array-Eintrag bilden, auch im HTML zu einer Einheit zusammengefasst sind. Das, was ich oben aufgeschrieben habe, sollte dann den von Dir gewünschten Zweck erfüllen.

        Ein Hinweis zur Blockverwaltung: wenn Du Blöcke einblendest - warum? Wenn Du beispielsweise 10 Blöcke "auf Vorrat" im HTML hast, dann hast Du beim elften angeforderten Block verloren. In solchen Fällen setzt man besser nur einen Block ins HTML und fügt weitere Blöcke - falls nötig mit entsprechend angepassten Namen und IDs - per JavaScript hinzu. Das dynamische Hinzufügen wird durch fieldsets ebenfalls einfacher. Wenn die Struktur so aussieht:

        <section id="fieldcontainer">
           <fieldset>...</fieldset>
           <fieldset>...</fieldset>
           <fieldset>...</fieldset>
           <fieldset>...</fieldset>
        </section>
        

        dann musst Du dem fieldContainer nur ein neues Fieldset hinzufügen (document.createElement("fieldset")) und das innerHTML des neuen fieldset aus einer Vorlage füllen. Das kann ein template-Element sein (nicht im Internet Explorer), oder einfach ein Vorlagenstring im JavaScript. Wenn Du Namen oder IDs anpassen musst, dann baust Du in den Vorlagenstring etwas ein wie $$$ oder $#$ - egal was, Hauptsache es wird nicht als normaler Text verwendet - und mit der replace-Funktion ersetzt Du das durch die laufende Nummer der Gruppe.

        Rolf

        --
        sumpsi - posui - clusi
    2. @@Rolf B

      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 />.

      Das Missverständnis liegt auf deiner Seite. Selbstverständlich ist <input/> korrekt. Sonst könnte man ja kein polyglottes HTML schreiben (das als HTML und als XHTML verarbeitet werden kann).

      Nachzulesen in der Spec: “Then, if the element is one of the void elements, or if the element is a foreign element, then there may be a single U+002F SOLIDUS character (/).”

      LLAP 🖖

      --
      „Wer durch Wissen und Erfahrung der Klügere ist, der sollte nicht nachgeben. Und nicht aufgeben.“ —Kurt Weidemann