Formular dynamisch erweitern.


südpol

Erfahrenes Mitglied
#1
Hi,

ich möchte gerne ein formular dynamisch erweitern. Sprich wenn jemand auf einen button klickt sollen weitere Felder erscheinen. Klickt er noch mal kommen noch mehr etc.

Es soll also z. B. ein input feld da stehen darunter ein button <noch eins> und nach jedem klick wird ein weiteres feld hinzugefügt, dass als prfix im namen einfach eine zahl hat die um 1 hochgezählt wird...

Wie geht das? hat jemand zufällig ein gute Anleitung oder ein speispielscript zur hand?

P. S. komme aus der php - Ecke und habe wenig Ahnung von javascript - muss ich leider zugeben...

Gruß
 
#2
Beispiel:
Code:
<html>
<head>
<title>Test</title>
<script type="text/javascript">
<!--
function clone_this(objButton)
{
if(objButton.parentNode)
    {
    tmpNode=objButton.parentNode.cloneNode(true);
    objButton.form.appendChild(tmpNode);
    for(j=0;j<objButton.form.lastChild.childNodes.length;++j)
        {
        if(objButton.form.lastChild.childNodes[j].type=='text')
            {
            objButton.form.lastChild.childNodes[j].value='';
            break;
            }
        }
    objButton.value="entfernen";
    objButton.onclick=new Function('f1','this.form.removeChild(this.parentNode)');
    }
}
//-->
</script>
</head>
<body>
<form>
 <div>
  <input type="text"size="20"name="textfeldname[]"><br>
  <input type="button"value="noch eins"onclick="clone_this(this)">
 </div>
</form>
</body>
</html>
Erläuterung:
Der Button und das Textfeld stehen in einem <div>(kann auch was anderes sein...span bspw)
Beim Aufruf wird der Funktion der Button als Objekt übergeben...dadurch hat man Zugriff auf alle seine Eigenschaften.

if(objButton.parentNode)
Abfrage, ob der Button einen Elternknoten hat...in DOM-Browsern ist dies der Fall...der Elternknoten ist das <div>

tmpNode=objButton.parentNode.cloneNode(true);
Von diesem Elternknoten wird eine Kopie erstellt

objButton.form.appendChild(tmpNode);
diese Kopie wird ans Ende des Formulars gehängt

Code:
for(j=0;j<objButton.form.lastChild.childNodes.length;++j)
        {
        if(objButton.form.lastChild.childNodes[j].type=='text')
            {
            objButton.form.lastChild.childNodes[j].value='';
            break;
            }
        }
...der Inhalt des grad erstellten Textfeldes wird gelöscht... der wurde nämlich mitgeklont :)

Danach habsch noch als Feature das Ändern des angeklickten Buttons eingefügt...sein Wert wird auf "entfernen" geändert...wenn man jetzt draufklickt, wird dieser Button incl. des dazugehörigen Textfeldes wieder gelöscht.
(kannst du natürlich rausnehmen, wenn du es nicht brauchst)

Auf das Durchnummerieren der Textfelder hab ich verzichtet....das wäre unnützer Mehraufwand.
In PHP kannst du durch die Klammer in "textfeldname[]" auch so bequem auf die Textfelddaten zugreifen.

Testbeispiel
 

südpol

Erfahrenes Mitglied
#3
... wenn wir uns jemals treffen geb ich dir einen aus! Super Anleitung super Beispiel! ... das man textfelder als array speichern kann wusste ich auch noch nicht *grübel* ich dachte, dass hatte ich schon mal getestet...

Eine, für dich vermutlich einfache, Frage verbleibt noch. Was muss ich an dem Skript ändern um das neue Textfeld an die Stelle zu bekommen an der bis jetzt der "noch ein Feld" Button war. Wenn das geht bin ich fast restlos glücklich bzw. ab dann sollte ich alle Änderungen durch try and error rausfinden können...

noch mal vielen vielen Dank!
 
#4
Das ginge z.B. so:
Code:
<html>
<head>
<title>Test</title>
<script type="text/javascript">
<!--
function clone_this(objButton)
{
    /**
      *  Das div, in welchem sich das erste input befindet, wird geclont
      **/
    
    tmpNode = objButton.form.elements[0].parentNode.cloneNode(true);
    
    
    /**
      *  das geklonte div wird vor dem Button eingefügt
      *  Syntax...Elternknoten.insertBefore(einzufügenderKnoten,KnotenVorDemEingefügtWerdenSoll);
      **/
    
    objButton.form.insertBefore(tmpNode,objButton);
    
    
    /**  Den Wert des eingefügten inputs wieder löschen
      * previousSibling ist der vorige Knoten eines Typs vor einem anderen Knoten...
      * in diesem Fall das neue div vor dem Button....firstChild wieder das erste Kindelement darin...also das input
      **/
      
    objButton.previousSibling.firstChild.value='';
    
}
//-->
</script>
</head>
<body>
<form>
 <div><input size="20" name="textfeldname[]" type="text"></div>
 <input value="noch eins" onclick="clone_this(this)" type="button">
</form>
</body>
</html>
 

südpol

Erfahrenes Mitglied
#5
VIELEN DANK SOWEIT SCHON MAL!

das sieht alles nicht so schwer aus - aber so richig gut anpassen funktioniert bei mir leider noch nicht. Könnt ihr mir zu dem Thema DHTML ein gutes Buch empfehlen. Das Thema scheint eine sehr gute Ergänzung für meine php - Seiten zu sein.

Wenn ihr Lust habt könnt ihr mir - solange das Buch auf dem Postweg ist :D - ja noch ein paar Tipps geben. Ich muss die Funktion nun nämlich noch erweitern *heul* :eek:

Ich habe ein ganz langes Formular, welches sich dynamisch mit Daten füllt. Ganz am Ende des Formulars soll nun ein Button hin der das Eingeben eines weiteren Datensatzes ermöglicht. Die Probleme vor denen ich stehe sind folgende:
1. ich muss eine ganze Tabelle inkl. Formularelemente einfügen
2. das ganze soll wie von euch schon gezeigt per klick auf einen Button immer weiter erweitert werden können.

Vielen Dank für eure Tipps!
 

südpol

Erfahrenes Mitglied
#9
Hi,

ich hoffe ich nerve nicht zusehr. Ich habe das ganze jetzt mal 'selber' versucht. Ist zwar sehr primitiv (vermutlich). Allerdings funktioniert es nicht. Ihr als Profis müsst bestimmt nich lange suchen um den Fehler zu finden. Ich hänge an dieser Stelle mal wieder.

So - sollte - der Ablauf sein: html Teil in ein div - Tag packen und über css "verstecken". Dieses Element soll dann zukünftig durch eine funktion geclont und vor dem Button wieder eigefügt werden. (so ist die Funktion auch noch in anderen Teilen einsetzbar und muss nicht angepasst werden :) -> man ist ja faul *g*)

So dachte ich könnte das js aussehen:
PHP:
function clone_this(button, objid)
{
  var clone_me = document.getElementById(objid).cloneNode(true);
  document.getElementById(button).insertBefore(clone_me, document.getElementById(button));
}
diese funktion wird dann im head eingefügt. Das html zeugs könnte dann so aussehen:

PHP:
<div style="visibility:hidden; display:none">
<div "dolly">
  <table>
    <tr>
      <td>test</test>
      <td><input type="text" name="test[]"></input></td>
    </tr>
      <td>test2</test>
      <td><input type="text" name="test2[]"></input></td>
    </tr>
  </table>
</div>
</div>

<div class="new_passage">
  <input value="mehr" onclick="javascript:clone_this(this, 'new_passage');" type="button"></input>
</div>
Leider funktioniert das nicht - hat jemand einen Tipp bzw. einen verbesserungsvorschlag, damit das ganze funktioniert?

DANKE!
 

Quaese

Moderator
Moderator
#10
Hi,

mit folgender Funktion clonest Du die Tabelle, die sich innerhalb des DIVs befindet. Es ist zu
beachten, dass sich zwischen dem DIV- und dem TABLE-Tag kein Zeichen befindet. Dieses
würde in einigen Browsern als firstChild-Objekt erkannt werden. Damit würde nicht die
Tabelle, sondern das Zeichen gecloned werden.
Code:
function clone_this(button, objid){
    // Tabelle innerhalb des DIVs mit ID "new_passage" clonen
    // Achtung: Es dürfen keine Zeichen zwischen dem DIV- und dem TABLE-Tag stehen!
    var clone_me = document.getElementById(objid).firstChild.cloneNode(true);

    // Im Parent-DIV vor dem Kindknoten "button" einhängen
    button.parentNode.insertBefore(clone_me, button);
}
Deinen bisherigen Code müsstest Du folgendermassen abändern:
HTML:
<div style="visibility:hidden; display:none">
<div id="new_passage"><table name="cloneTable">
    <tr>
      <td>test</test>
      <td><input type="text" name="test[]"></input></td>
    </tr>
      <td>test2</test>
      <td><input type="text" name="test2[]"></input></td>
    </tr>
  </table>
</div>
</div>

<div>
  <input value="mehr" onclick="javascript:clone_this(this, 'new_passage');" type="button"></input>
</div>
Ich hoffe, das hilft Dir weiter.

Ciao
Quaese
 

südpol

Erfahrenes Mitglied
#11
Vielen Dank!

jetzt funktioniert es soweit perfekt! Hmm hoffentlich kommt mein dhtml Buch bald - ich will langsam auch mal selber auf solche Ideen kommen bzw. genau verstehe, was ich da mache...

noch mal vielen Dank für die Hilfe!
 

südpol

Erfahrenes Mitglied
#12
Vielen DANK! Das clonen funktioniert nun wunderbar! Langsam aber sicher fange ich sogar an zu verstehen was ich da abschreibe... :D Wenn jetzt noch mein dhtml Buch kommt kann ich richtig anfangen...

Mein letzter Schritt für dieses Formular ist nun noch einen löschen Button einzufügen (wie im oberen Beispiel).

PHP:
<div style="visibility:hidden; display:none"> 
  <div id="new_passage"><table name="cloneTable">
    <tr>
      <td>test</test><td>
      <input type="text" name="test[]"></input>
      </td>
      <td rowspan="2"><a href="#" onclick="javascript:remove_this(this); return false;">LÖSCHEN</a>
      </td>   
    </tr>
      <td>test2</test>
     <td><input type="text" name="test2[]"></input></td>
    </tr>
  </table></div>
 </div>
<div>
  <input value="mehr" onclick="javascript:clone_this(this, 'new_passage');"  type="button">
  </input> 
</div>
Bei der Funktion zu remove_this() scheitere ich aber schon wieder ich habe mir mit hilfe von alert schon rausgesucht, dass ich 5 mal parentNode verwenden muss um zum richtigen div tag zu kommen unter dem alles gelöscht werden soll. Wenn es dann allerdings ums löschen selber geht (die funktion ist wohl removeChild() ) dann hängt es bei mir wieder an mehreren Stellen. Hat hier zufällig auch noch jemand eine gute Idee bzw. ein code - schnippsel der mir weiterhelfen kann? (am besten ohne id´s anzusprechen - da ich die Felder ja clone könnte das zu Problemen führen, da diese auch gleich sind...)

Liebe Grüße
 
#13
Es mag etwas albern aussehen(dasmit 5xparentNode hast du ja schon rausbekommen), ist aber tatsächlich so:suspekt:
Code:
function remove_this(objLink)
{
objLink.parentNode.parentNode.parentNode.parentNode.parentNode.removeChild(objLink.parentNode.parentNode.parentNode.parentNode);
}
die Syntax von removeChild ist
Code:
elternknoten.removeChild(Kindknoten);
die Funktion macht also folgendes:
Code:
objLink[link].parentNode[td].parentNode[tr].parentNode[tbody].parentNode[table].parentNode[div].removeChild(objLink[link].parentNode[td].parentNode[tr].parentNode[tbody].parentNode[table]);

Zu beachten dabei ist, dass du bei Tabellen u.U. einen Elterknoten weiter hoch gehen musst, als du im Dokument stehen hast.... die Browser erzeugen in der Knotenstruktur von sich aus einen <TBODY>, wenn keiner vorhanden ist.
 

südpol

Erfahrenes Mitglied
#14
Danke! :) Da mein Buch weiter auf sich warten lässt muss ich euch noch mal nerven (ich hätte es neu bestellen sollen diese Gebrauchtmärkte sind einfach viel zu langsam...)

Ich habe die Funktion nun eingebaut. Leider erhalte ich immer die JS Meldung, dass ein Fehler auf der Seite ist. In den Details steht dann was von ungültigem Argument. Was kann das sein? Die Debug infos insbesondere die Zeilenangaben scheinen fast so brauchbar zu sein wie bei php :D (in der Umgebung von Zeile xy :) )

So sieht meine Seite (Ausschnitt) gerade aus. (sorry für die Struktur, ist via php erzeugt daher etwas durcheinander...)

Code:
<form method="post" action="new_or_edit_policy_capture_forward.php?rap_id=24">
<input type="hidden" name="rap_id" value="24"></input>
<br />
<div id="hidden" style="visibility:hidden; display:none">
  <div id="new_passage"><table width="95%" name="cloneTable">
    <tr>
      <td class="hellgrautab" colspan="2">
      NEW_POLICY_PASSAGE      </td>
      <td class="helltab" rowspan="3">
      <a href="#" id="delete_button" onclick="javascript:remove_this(this); return false;">
      <img src="images/trash.gif" border="0" alt="LOESCHEN"></a>
      </td>
    </tr>
    <tr>
      <td class="helltab">
              CHAPTER:&nbsp;
            <input type="hidden" name="new_pointer_character[]" value="1"></input>
      <input type="text" size="3" name="new_pointer_character_value[]" value="000" onfocus="kill_content(this, 000)"></input>
      </td>
      <td class="helltab">
      <input type="text" size="91" name="new_passage_headline[]" value="PASSAGE_HEADLINE" onfocus="kill_content(this, 'PASSAGE_HEADLINE')"></input>
      </td>
    </tr>
    <tr>
      <td class="helltab" colspan="2">
      <textarea cols="85" rows="5" name="new_passage[]" onfocus="kill_content(this, 'INSERT_THE_PASSAGE_HERE')">INSERT_THE_PASSAGE_HERE</textarea>
      </td>
    </tr>
  </table></div>
</div>
<div>
  <input class="button" value="FURTHER_PASSAGE" onclick="javascript:clone_this(this,'new_passage');" type="button"></input>
</div>
<br />
<div>
  <input class="button" type="submit" value="SUBMIT"></input>
</div>
Die funktion sieht gerade so aus:
Code:
function remove_this(objLink)
{
  objLink.parentNode.parentNode.parentNode.parentNode.parentNode.removeChild(objLink.parentNode.parentNode.parentNode.parentNode.parentNode);
}
Kann hier jemand auf anhieb sehen, was da falsch läuft? Die ID des Remove Buttons möchte ich jedoch nicht ansprechen, da diese objekte geclont werden und somit mehrfach auf einer Seite vorkommen können :D

Vielen Dank!
 

südpol

Erfahrenes Mitglied
#16
*mist* du hast recht. Dank dir! Jetzt ist meine Seite schon fast fertig *freu* dhtml fängt an mir zu gefallen. Hoffentlich kommt mein Buch bald *freu*

Eine Schlussfrage zum besseren Verständnis. Wie müsst so eine Löschfunktion nun aussehen wenn ich den Inhalt eines div - tags mit einer bestimmten ID löschen möchte.

Sprich ich habe ein ewig langes dokument in dem an einer beliebigen Stelle folgendes steht:
Code:
<div id="kill_mich_12"><table><tr><td>&nbsp;</td></tr></table></div>
irgendwo im Dokument ist dann ein Button. Sobald der gedrückt wird soll dieser, durch das div - Tag eingeschlossene Bereich, gelöscht werden...

Geht das? Oder muss man immer alle knoten durch gehen um etwas zu löschen?
 
#17
Wie du die Knoten ansprichst, ist eigentlich egal, solange sie gefunden werden, und der zu löschende Knoten ein Kindknoten des angegebenen Elternknoten ist.
Du kannst zur Identifizierung der Knoten auch Methoden verwenden, welche nichts mit dem Node-Objekt zu tun haben,
z.B.
Code:
<form>
    <input type="button"value="lösch mich"onclick="this.form.removeChild(this)">
</form>
der Button ist ein Kindknoten des Formulars.
Das Formular wird aus dem Button heraus per "this.form" angesprochen, der Button selbst per "this"...klickt man auf den Button,ist er weg.

Zurück zur Frage, das geht, indem du den Kindknoten in deinem Beispiel per "document.getElementById('kill_mich_12').firstChild"
ansprichst.

Dies setzt aber vorraus, das der Elterknoten wie in deinem Fall nur einen einzigen Kindknoten besitzt(beachte dabei, dass schon ein Leerzeichen zwischen <div> und <table> einen Kindknoten darstellen würde, wie Quaese weiter oben schon erwähnt hatte).

Eine andere, etwas schlampige Methode(welche zudem weniger Browser interpretieren) wäre das Zuweisen einer leeren Zeichenkette an die innerHTML-Eigenschaft des Elterknotens:
Code:
document.getElementById('kill_mich_12').innerHTML='';
Bis dein Buch eintrifft kannst du ja mal bei SelfHTML:Nodes schmökern ;)
 

südpol

Erfahrenes Mitglied
#18
Hi,

gestern abend ist mein Buch eingetroffen :D. Da ich jetzt 1 Woche Urlaub habe passt das genau.

Ok, das mit dem nur einen Kinderknoten erklärt, warum es bis jetzt nicht so richtig funktioniert hat... innerhalb meines divs sind mehrere Tabellen etc.

Ich werde daher auf die unsaubere Methode umsteigen müssen. Da das ganze jedoch "nur" im Intranet läuft und hier nur eine Browserversion eingesetzt wird dürfte das kein Problem sein ;) Ich dank dir für die vielen Tipps und die Umfangreiche Hilfe!

Gruß
 
#19
Hallo!

Muss dann mal diesen alten Thread reaktivieren!
Ich hab mir jetzt auch son schickes Formular gebaut, nun habe ich viele Konstrukte ala

<input type="text" name="test2[]">

drin. Wie hier auch beschrieben.
An der Stelle frage ich mich wie kann ich denn par PHP auf "geclonte" Felder zugreifen die derart benannt sind, nachdem sie per post gesendet wurden?

Füllt Java diese "clone" mit irgendwas, oder heißen die dann alle test2[] wenn geclont und ich muss eine Nummer oder dergleichen direkt mit in die Clonfunktion bauen?