Viele standen schon einmal vor dem Problem, dass in einem Formular zu einem Bereich mehrere Eingaben möglich sein sollten. Meist wird dies durch mehrere Text-Felder gelöst, die in einer Tabelle angeordnet sind. Um nun aber dem Benutzer die Möglichkeit zu geben eine weitere Zeile einzufügen musste oft das Formular „abgeschickt“ werden, wobei aber der Speicher-Algorithmus umgangen wurde. Anschließend wurde das gesamte Formular neu geladen mit einer neuen Zeile für den entsprechenden Bereich.
Zum Glück sind wir heute wesentlich weiter. Zwar war eine Lösung per JavaScript auch in der Vergangenheit schon möglich, aber durch die Verwendung modernen Frameworks wie Prototype lässt sich das gesamte Problem auf eine Zeile Code reduzieren:
$$('#table tr').last().insert({'after' : this.cloneNode(true)});
Zugegeben, hier werden einige Funktionen in Reihe aufgerufen, aber diese Zeile Code lässt sich ohne weiteres per inline Eventhandler verwenden.
Doch der Reihe nach, was wird mit dieser Zeile eigentlich gemacht? Fangen wir einfach vorne bei der $$() Funktion an. Hier übergeben wir einen CSS-Selektor um alle TR Tags innerhalb der Tabelle mit der ID „table“ zu selektrieren und bekommen durch anwenden der last() Funktion die letzte davon ausgewählt. Auf diese Zeile wenden wir nun die insert() Funktion an. Dabei wird die Zeile selbst übergeben und steht über „this“ zur Verfügung. Dies machen wir uns zu Nutze und duplizieren die letzte Zeile durch die Funktion cloneNode(). Diese kopierte Zeile wird nun nach der vorherigen eingefügt, was durch die Angabe von „after“ angegeben wird.
Da die letzte Zeile kopiert wird, werden auch alle Werte, die sich in Formluarfeldern dieser Zeile befinden mitkopiert. Um dies zu verhindern müssten die Felder zurückgesetzt werden. Das ganze in eine Funktion gepackt und mit zweit Callback-Funktionen erweitert sieht dann wie folgt aus:
function addRow(selector, options){ var lastRow = $$(selector).last(); var newRow = lastRow.cloneNode(true); if (options.reset) { newRow.select('select', 'textarea', 'input').each(function(elm){ elm.clear().checked = ''; }); } if(options.beforeInsert) options.beforeInsert(newRow); lastRow.insert({'after': newRow}); if(options.afterInsert) options.afterInsert(newRow); }
Wenn im Options Hash der reset Parameter auf true gesetzt wird, werden vor dem Einfügen der neuen Zeile alle Felder zurückgesetzt. Dazu werden die Formular-Elemente mit der select() Funktion ausgewählt und mit Hilfe der each() Funktion wird jede Zeile durch die clear() Funktion zurückgesetzt. Bei Radio-Buttons und Checkboxen werden zusätzlich die „checked“ Attribute zurückgesetzt.
Die Funktion kann nun jedem Beliebigen Element zugerdnet werden. Hier bietet sich ein Link, ein SPAN oder ein Bild an:
<span onclick="addRow('table', {reset: true, beforeInsert: doSomeMagic})">Zeile hinzufügen</span>
Bei diesem Beispiel-Button wurde der reset Parameter auf „true“ gesetzt und zusätzlich der Name einer Callback-Funktion angegeben, die vor dem einfügen aufgerufen wird. Die Callback-Funktion „beforeInsert“ bietet sich an, um z.B. LABEL Tags in der einzufügenden Zeile zu ersetzen. Die Callback-Funktion „afterInsert“ kann verwendet werden, um z.B. Eventhander oder andere Funktionen einem Element der neuen Reihe zuzuordnen, was besser nach dem einfügen vorgenommen werden sollte. Beide Callbacks erhalten eine Referenz auf die einzufügende Zeile.
Als kleines Beispiel habe ich das Formular aus meinem vorherigen Post etwas umgebaut und die Funktion zum Hinzufügen einer E-Mail-Adresse eingesetzt. Hier das Beispiel und der Quellcode dazu:
Hierbei kommt ein sehr viel komplexerer CSS-Selektor zum Einsatz:
<span onclick="addRow('form dl:first-of-type dd ul li', {reset: true})">Add address</span>
Wie Ihr also seht, könnt ihr die Funktion für beliebige Elemente verwenden, bei denen es Sinn macht, sie zeilenweise zu duplizieren.
Falls ein jQuery-Profi unter euch ist, würde mich eine Lösung in jQuery sehr interessieren. Ich habe leider noch nicht genug Erfahrung darin, um die Funktion ebenfalls in jQuery zu schreiben. Wenn ihr Anmerkungen zur Prototype-Version habt würde mich diese aber auch interessieren.
Den Zeilen Duplizierer finde ich sehr gut. Ich möchte eine Zeile kopieren, die einen Autocompleter von Scriptaculous enthält. Funktioniert das auch mit diesem Duplizierer? Könnte ich ein Beispiel bekommen?
Hallo trenied,
ja das funktioniert. Und witziger weise habe ich die Funktion auch in einem solchen Fall im Einsatz. Aber da man das ganze etwas ausführlicher erklären sollte habe ich mich entschlossen dazu einen neuen Artikel zu schreiben: Zeilen-Duplizierer mit AutoComplete Felder nutzen. Ich hoffe, das ist genau das, was du suchst.
Gruß
Bernhard
[…] bekam heute in einem Kommentar die Frage gestellt, ob mein Zeilen-Duplizerer aus dem Beitrag: Einfacher Zeilen-Duplizierer mit Prototype auch auf Feldern funktioniert, die die Scriptaculous Ajax.AutoCompleter Funktion […]
[…] meinem vorherigen Post zum Zeilen-Duplizierer stand ich vor dem Problem, dass ich Elemente eines bestimmten Tags innerhalb eines ausgewählten […]