views:

767

answers:

5

For instance, I want to have a html form that looks like this:

<table>
  <tr>
     <td>Field A:</td>
     <td><input type='text' name='fielda[1]'></td>
     <td>Field B:</td>
     <td><textarea name='fieldb[1]'></textarea></td>
  </tr>
</table>

What I want is to add a button that duplicates my entire above form, but changes the 1 to a 2 for all of the fields. Not just one field, but the entire section of code, including the table.There will be more/different fields than the ones I posted as well.

I've already tried this solution, which does exactly what I need:

http://www.quirksmode.org/dom/domform.html

But for some reason, could not duplicate the functionality when copying the code examples to test. I even tried literally copying the entire page source to get it to work, with no avail.

+4  A: 

There's a technique called supplant that does this. I didn't write it, I think Douglas Crockford did. But I'll quote it:

Now that your JavaScript program has received the data, what can it do with it? One of the simplest things is client-side HTML generation.

var template = '<table border="{border}"><tr><th>Last</th><td>{last}</td></tr>' +
   '<tr><th>First</th><td>{first}</td></tr></table>';

Notice that we have an HTML template with three variables in it. Then we'll obtain a JSON object containing members that match the variables.

var data = { 
    "first": "Carl", 
    "last": "Hollywood",    
    "border": 2 
};

We can then use a supplant method to fill in the template with the data.

mydiv.innerHTML = template.supplant(data);

JavaScript strings do not come with a supplant method, but that is ok because JavaScript allows us to augment the built-in types, giving them the features we need.

String.prototype.supplant = function (o) { 
    return this.replace(/{([^{}]*)}/g, 
        function (a, b) {  
            var r = o[b];
            return typeof r === 'string' ? 
                r : a; 
        }
    ); 
};

From http://www.json.org/fatfree.html

One technique I've seen to avoid that messy variable assignment of a template is to do:

<script id="rowTemplate" type="text/html">
  <tr>
     <td>Field A:</td>
     <td><input type='text' name='fielda[{id}]'></td>
     <td>Field B:</td>
     <td><textarea name='fieldb[{id}]'></textarea></td>
  </tr>
</script>

This will let your write much prettier code as you can reference it with getElementById.

So in your case, rather than populating it with a JSON object, you would just look over however many rows you need to add and replace the id's.

Tom Ritter
+2  A: 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
<html xmlns="http://www.w3.org/1999/xhtml"&gt;
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3/jquery.min.js" type="text/javascript"></script>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<script type="text/javascript">

    (function($){
       $countForms = 1;
       $.fn.addForms = function(){
       var myform = "<table>"+
        "  <tr>"+
        "     <td>Field A ("+$countForms+"):</td>"+
        "     <td><input type='text' name='fielda["+$countForms+"]'></td>"+
        "     <td>Field B ("+$countForms+"):</td>"+
        "     <td><textarea name='fieldb["+$countForms+"]'></textarea></td>"+
        "     <td><button>remove</button></td>"+
        "  </tr>"+
        "</table>";

        myform = $("<div>"+myform+"</div>");
        $("button", $(myform)).click(function(){ $(this).parent().parent().remove(); });

        $(this).append(myform);
        $countForms++;
       };
    })(jQuery);  

    $(function(){
     $("#mybutton").bind("click", function(){
      $("#container").addForms();
     });
    });

</script>
</head>
<body>
<button id="mybutton">add form</button>
<div id="container"></div>
</body>
</html>
andres descalzo
Wow! Very simple. But wouldn't this require some includes for jquery? (i'm sorry, I really don't know much about jquery) What else is needed to complete the code?
Citizen
Yes, but you could do it pretty much as easily using innerHTML from plain JavaScript if you wanted to.
bobince
include jquery on the page, you can put this code in a separate file as a list of plugins. wait a minute, and I complete with an example
andres descalzo
Do I just put this on a <input onclick button? What about removing the added form section?
Citizen
How do you remove one of these created areas?
Citizen
ready for remove
andres descalzo
Thanks! How do I upvote you twice? :)
Citizen
ejje , I do not know
andres descalzo
+2  A: 

The example you are linking to, has a hidden version of the form that is used as a template. This template has an ID (readroot), that allows it to be easily selected (getElementById), then cloned (node.cloneNode()) and injected into the DOM above (node.insertBefore()) a second marker in the source (writeroot).

The number of duplications is kept accounted for in the variable counter. Its' contents are updated (incremented) on every cloning, and its' value is then appended to all the field names in the cloned node. (The original template-nodes' fields have no numeric suffix.)

It's all really very simple, just study the javascript in the head-portion of the webpage. Google the parts you can't understand, and you will achieve what you're looking for, and will learn some in the process. Profit!

nikc
+1 for not using big chunks of hardcoded strings of "HTML".
annakata
I totally understand what the code is trying to do, but I can't isolate where the bug is when I literally copypasta the code.
Citizen
@Ryan: can you paste your complete source into a pastebin somewhere? Psychic debugging is too hard this time of day. (CET+1)
nikc
I tried first copying the code and editing it to do what I needed. When that didn't work (clicking the button did nothing), I tried copying the exact source for the entire page, and that did not work either.
Citizen
A: 

Oops, read you question incorrectly. This is better:

function duplicate(frm) {
  newfrm = $(frm).clone();
  $(newfrm).each(function(i,o) {
     if (o.name != null) { // or if ($(o).attr("name"))
       o.name = replaceIndex(o.name);
     }
  });
  $(frm).parent().append(newfrm);
}

Not sure about actual syntax. E.g. how do you test for attribute presence. Easy to find in google, though. Here replaceIndex(string) uses lastIndexOf() to find last [x] and replace it to [x+1].

queen3
A: 

I think you should use some kind of library like jQuery, as some have suggested. It'll pay off.

Regarding making a template and cloning it in javascript, there are some problems when using IE (all versions as far as I know). IE will disregard any programmatical changes to 'name' property when duplicating e.g. radio buttons in a form. So unless you create custom event handlers for the radio buttons, they will not work as expected when you copy them across sections (e.g. checking a button in one section will cause a button in another section to be unchecked).

goorj