See section 8E of the book for cloning elements.
Explorer 6 Windows hase serious trouble with radio buttons. Despite different names they see all generated radio buttons as one array, so the user can only select one radio button in all the copies.
On this page I treat a very simple W3C DOM script. It serves to explain why I think the W3C DOM will allow us to see interaction design in a radically new way.
Suppose you have an online CD ranking tool. You want your users to review as many CD's as they like. However, how do you know how many CD's an average user wants to review? How many form fields should you add to the page?
Before the W3C DOM this was quite a problem. Suppose you add form fields for 7 CD's. Some users will review only one CD and don't need the rest of the form (it might even frighten them). Other users might want to add their entire CD collection of hundreds of titles to your database and have to submit the form dozens of times. This is quite annoying.
Only using the W3C DOM you can allow your users to generate as many form fields as they need. This effect is impossible to mimic with any previous JavaScript technique.
Which CD's did you listen to recently?
When you hit 'Send form' the form is sent to a script that lists the parameters it has received. This is to check whether the generated fields are really sent to the server. Unfortunately it turns out that Explorer Mac and Safari don't send any fields to the server.
Unfortunately there are two serious problems in Explorer Windows:
First of all it sees all generated radio buttons as belonging to one single array, even if they have different
name
s. Thus the user can select only one radio button in all generated fields. Basically this means that
you cannot use radio buttons at all in generated forms.
A reader said that generating radio buttons through innerHTML
works fine. If you must use
radio buttons, you might try this approach.
Secondly the generated form fields are unreachable by a traditional document.forms
call: Explorer simply
doesn't enter them in the arrays. This can be worked around by giving the form field an ID and then using getElementById()
.
The HTML of the form is:
<div id="readroot" style="display: none"> <input type="button" value="Remove review" onclick="this.parentNode.parentNode.removeChild(this.parentNode);" /><br /><br /> <input name="cd" value="title" /> <select name="rankingsel"> <option>Rating</option> <option value="excellent">Excellent</option> <option value="good">Good</option> <option value="ok">OK</option> <option value="poor">Poor</option> <option value="bad">Bad</option> </select><br /><br /> <textarea rows="5" cols="20" name="review">Short review</textarea> <br />Radio buttons included to test them in Explorer:<br /> <input type="radio" name="something" value="test1" />Test 1<br /> <input type="radio" name="something" value="test2" />Test 2 </div> <form method="post" action="/cgi-bin/show_params.cgi"> <span id="writeroot"></span> <input type="button" id="moreFields" value="Give me more fields!" /> <input type="submit" value="Send form" /> </form>
The actual form fields are in a DIV with id readroot and display: none
.
This DIV is a template that should not be changed by the user. When the user wants more
fields we clone the DIV and append this clone to the form. We do this once onLoad, so that
the user sees one set of form fields when entering the page.
The DIV is outside the actual FORM so that the template fields aren't sent to the server when the form is submitted.
The span with id writeroot serves as a marker. The new sets of form fields should be inserted just before it.
This script adds sets of form fields when necessary:
var counter = 0; function moreFields() { counter++; var newFields = document.getElementById('readroot').cloneNode(true); newFields.id = ''; newFields.style.display = 'block'; var newField = newFields.childNodes; for (var i=0;i<newField.length;i++) { var theName = newField[i].name if (theName) newField[i].name = theName + counter; } var insertHere = document.getElementById('writeroot'); insertHere.parentNode.insertBefore(newFields,insertHere); } window.onload = moreFields;
First of all we need a counter
, because all sets of form fields should get
unique names. We do this by appending counter
to the names in the template. Initialize
counter
:
var counter = 0;
Then for the actual function. Start by increasing counter
by 1.
function moreFields() { counter++;
Then we clone our template, remove its id
and set its display
to block
. The id 'readroot' should remain unique in the document, and the clone
of the template should be visible to the user.
var newFields = document.getElementById('readroot').cloneNode(true); newFields.id = ''; newFields.style.display = 'block';
We go through the child nodes of the clone
var newField = newFields.childNodes; for (var i=0;i<newField.length;i++) {
Whenever a child node has a name
we append counter
to it. Thus
the names of all form fields remain unique.
var theName = newField[i].name if (theName) newField[i].name = theName + counter; }
Now the clone is ready to be inserted into the document. We insert it just before the span with id="writeroot".
var insertHere = document.getElementById('writeroot'); insertHere.parentNode.insertBefore(newFields,insertHere); }
Finally we execute this function once onLoad so that the user will initially see one set of form fields.
window.onload = moreFields;
Each clone of the template contains a 'Remove review' button:
<input type="button" value="Remove review" onclick="this.parentNode.parentNode.removeChild(this.parentNode);" /><br /><br />
Clicking on it causes the button to remove its parent node (the DIV) from its own parent node (the FORM). Thus one set of form fields disappears entirely, never to return.