PDA

View Full Version : Resolved dynamic cloning/inserting a node



boogyman
11-10-2008, 04:34 PM
I am trying to dynamically insert a cloned node, changing the the value of some of the attributes of a "second level" clone at the same time.



<script type="text/javascript">
function addOrderFormFields(dynamicFormReadId, dynamicFormWriteId, counter, counterStart)
{
if(!counters[counter])
counters[counter] = counterStart;

counters[counter]++;

var newFields = document.getElementById(dynamicFormReadId).cloneNode(true);
newFields.id = '';
var newField = newFields.childNodes;

/* initiate the first level (td) loop */
for (var i=0;i<newField.length;i++)
{
var iFields = newField[i].cloneNode(true);
var iField = iFields.childNodes;

/* initiate the second level (input) loop */
for(var j=0; j<iField.length; j++)
{
var theName = iField[j].name;
if (theName)
{
iField[j].name = theName.substr(0, theName.length - 1) + counters[counter];
iField[j].value = ''; /* clear any default value that may be present */
}
}
}

var insertHere = document.getElementById(dynamicFormWriteId);
insertHere.parentNode.insertBefore(newFields,insertHere);
}
</script>
<table>
<tr id="dynamicFormReadPart">
<td><input type="text" name="partQuantity0" value="{top.QUANTITY}" size="5"></td>
<td><input type="text" name="partNumber0" value="{top.NUMBER}"></td>
<td><input type="text" name="partDescription0" value="{top.DESCRIPTION}" size="50"></td>
<td><input type="text" name="partSerial0" value="{top.SERIAL}"></td>
</tr>
<tr id="dynamicFormWritePart"><!-- USED AS PLACEHOLDER TO INSERT DYNAMIC FIELDS -->&nbsp;</tr>
<tr>
<td colspan="4" align="left"><input type="button" value="Add More Parts to Order" onclick="javascript:addOrderFormFields('dynamicFormReadPart', 'dynamicFormWritePart', 'c0', 0)"></td>
</tr>
</table>


This script will insert the new table row correctly, however it is not incrementing the name attribute of the input fields.

any help would be appreciated.

clueful
11-10-2008, 11:00 PM
I am trying to dynamically insert a cloned node, changing the the value of some of the attributes of a "second level" clone at the same time.



<script type="text/javascript">
function addOrderFormFields(dynamicFormReadId, dynamicFormWriteId, counter, counterStart)
{
if(!counters[counter])
counters[counter] = counterStart;

counters[counter]++;

var newFields = document.getElementById(dynamicFormReadId).cloneNode(true);
newFields.id = '';
var newField = newFields.childNodes;

/* initiate the first level (td) loop */
for (var i=0;i<newField.length;i++)
{
var iFields = newField[i].cloneNode(true);
There is no need to clone the children of a cloned parent. This causes the name to be applied to the wrong node


iField[j].name = theName.substr(0, theName.length - 1) + counters[counter];This will only work properly up to 9. I would use:
iField[j].name = theName.replace(/(.*?)(\d*)$/, "$1"+counters[counter]);

boogyman
11-11-2008, 01:32 PM
There is no need to clone the children of a cloned parent. This causes the name to be applied to the wrong node


Yeah I thought that would be the case, however using just one loop wouldn't even obtain the attributes of the <input> tags. Once I created that second loop, I was able to accurately "grab" and update the tags, however the calculation still wouldn't properly update, hense why I posted here



This will only work properly up to 9. I would use:
iField[j].name = theName.replace(/(.*?)(\d*)$/, "$1"+counters[counter]);

Regular expressions are generally slower, so I wanted to avoid them as much as possible.
I will assume you mean IE 9? above, and what exactly are you referring to by


"$1"


Thanks for the help. :D

jscheuer1
11-11-2008, 04:54 PM
I would use a different approach. Once I cloned the node as many times as I wanted to and inserted them into the markup, I would use the cloned nodes' new parent node (the table if I am following this correctly - actually the tbody, but the table will do) to cycle through its tagName node list, something like:


var t = reference to the table;
for(var c = 0, els = t.getElementsByTagName('*'), i = 0; i < els.length; ++i)
if(els[i] == t.rows[c + 1]){
++c;
if (c > 0) //not positive if this is right for your markup, but should work if the markup were simplified
els[i].id = '';
} else if(/^part/.test(els[i].name)){
els[i].name = els[i].name.replace(/\d+/, '') + c;
}

jscheuer1
11-11-2008, 05:09 PM
Maybe even:


var t = reference to the table;
for(var c = 0, els = t.getElementsByTagName('*'), i = 0; i < els.length; ++i)
if(els[i] == t.rows[c + 1]){
++c;
if (els[i].id != 'dynamicFormWritePart')
els[i].id = '';
else break;
} else if(/^part/.test(els[i].name)){
els[i].name = els[i].name.replace(/\d+/, '') + c;
}

boogyman
11-11-2008, 05:59 PM
Maybe even:


var t = reference to the table;
for(var c = 0, els = t.getElementsByTagName('*'), i = 0; i < els.length; ++i)
if(els[i] == t.rows[c + 1]){
++c;
if (els[i].id != 'dynamicFormWritePart')
els[i].id = '';
else break;
} else if(/^part/.test(els[i].name)){
els[i].name = els[i].name.replace(/\d+/, '') + c;
}

This is getting closer, however the term "take two backwards to take one forwards" applies.

This will in fact increment the elements as I would like, however




if (els[i].id != 'dynamicFormWritePart')
els[i].id = '';



is now deleting the "read" id from the form, so I am unable to clone it again. I will work on bypassing the initial element that gets cloned, and update the script here.

boogyman
11-11-2008, 06:10 PM
I know this is an adhoc way of doing it, but after the last loop, I just reassigned the appropriate table row the correct id.

John, or anyone else, if you can think of a better way to accomplish this within the loop please don't hesitate to share. Below is an updated script that works



<script type="text/javascript">
function addOrderFormFields(dynamicFormReadId, dynamicFormWriteId)
{
var newFields = document.getElementById(dynamicFormReadId).cloneNode(true);
var insertHere = document.getElementById(dynamicFormWriteId);
insertHere.parentNode.insertBefore(newFields,insertHere);

var t = document.getElementById('partFields');
for(var c = 0, els = t.getElementsByTagName('*'), i = 0; i < els.length; ++i)
{
if(els[i] == t.rows[c + 1])
{
++c;
if(els[i].id != 'dynamicFormWritePart')
els[i].id = '';
else
break;
}
else if(/^part/.test(els[i].name))
els[i].name = els[i].name.replace(/\d+/, '') + c;
}

els[6].id = 'dynamicFormReadPart';
}
</script>

<table id="partFields" summary="Parts associated with this order request">
<tbody>
<tr>
<th>Qty</th>
<th>Part Number</th>
<th>Part Description</th>
<th>Serial</th>
</tr>
<tr id="dynamicFormReadPart">
<td><input name="partQuantity0" value="" size="5" type="text"></td>
<td><input name="partNumber0" value="" type="text"></td>
<td><input name="partDescription0" value="" size="50" type="text"></td>
<td><input name="partSerial0" value="" type="text"></td>
</tr>
<tr id="dynamicFormWritePart"><!-- USED AS PLACEHOLDER TO INSERT DYNAMIC FIELDS --></tr>
</tbody>
</table>
<input value="Add More Parts to Order" onclick="javascript:addOrderFormFields('dynamicFormReadPart', 'dynamicFormWritePart')" type="button">

jscheuer1
11-11-2008, 06:37 PM
The code I wrote shouldn't even look at row 0 for id changing:


if(els[i] == t.rows[c + 1]){ // we are already at least to row 1
++c;
if (els[i].id != 'dynamicFormWritePart')
els[i].id = '';
else break;
}

<tr id="dynamicFormReadPart"> is row 0. But if I've miscalculated or if you've put a header row in there later:


if(c > 1 && els[i].id != 'dynamicFormWritePart')

Instead of cloning the row by its id in the first place, you may clone it as a member of the table's rows collection. The numbers may all be made to work out any way you like if you just count the rows starting with 0 for the first one and adjusting c's initial value and any tests against and uses of c accordingly.

You could also skip having a dynamicFormWritePart id by simply (as long as their is a last row to insert before, otherwise you could simply append):


table.rows[table.rows.length - 1].parentNode.insertBefore(clonedRow, table.rows[table.rows.length - 1]);

boogyman
11-11-2008, 07:10 PM
The code I wrote shouldn't even look at row 0 for id changing:


if(els[i] == t.rows[c + 1]){ // we are already at least to row 1
++c;
if (els[i].id != 'dynamicFormWritePart')
els[i].id = '';
else break;
}

<tr id="dynamicFormReadPart"> is row 0. But if I've miscalculated or if you've put a header row in there later:


There was always a header row, I probably just forgot to include it in my example.


<table id="partFields" summary="Parts associated with this order request">
<tbody>
<tr>
<th>Qty</th>
<th>Part Number</th>
<th>Part Description</th>
<th>Serial</th>
</tr>
<tr id="dynamicFormReadPart">
<td><input name="partQuantity0" value="" size="5" type="text"></td>
<td><input name="partNumber0" value="" type="text"></td>
<td><input name="partDescription0" value="" size="50" type="text"></td>
<td><input name="partSerial0" value="" type="text"></td>
</tr>
<tr id="dynamicFormWritePart"><!-- USED AS PLACEHOLDER TO INSERT DYNAMIC FIELDS --></tr>
</tbody>
</table>





if(c > 1 && els[i].id != 'dynamicFormWritePart')


I tried that, and I also substituted the c for an i neither of which worked



You could also skip having a dynamicFormWritePart id by simply (as long as their is a last row to insert before, otherwise you could simply append):

Well the only purpose of that last row is for the insert, however the method you suggested is still more elegant then the one I was proposing so I updated it accordingly.


below is the JS file as it reads right now. The html table hasnt changed from my last example



function addOrderFormFields(dynamicFormReadId, dynamicFormWriteId)
{
var newFields = document.getElementById(dynamicFormReadId).cloneNode(true);
var t = document.getElementById('partFields');

/* insert the cloned node */
t.rows[t.rows.length - 1].parentNode.insertBefore(newFields, t.rows[t.rows.length - 1]);

/* re-assign the input name attributes */
for(var c = 0, els = t.getElementsByTagName('*'), i = 0; i < els.length; ++i)
{
if(els[i] == t.rows[c + 1])
{
++c;
if(c>1 && els[i].id != 'dynamicFormWritePart')
els[i].id = '';
else
break;
}
else if(/^part/.test(els[i].name))
els[i].name = els[i].name.replace(/\d+/, '') + c;
}
}

boogyman
11-11-2008, 08:24 PM
changed the c+1 to c+2 to bypass the header row and the row I wish to clone



function addOrderFormFields(dynamicFormReadId, dynamicFormWriteId, tbl)
{
/* clone the appropriate node */
var newFields = document.getElementById(dynamicFormReadId).cloneNode(true);
/* get the table being cloned */
var t = document.getElementById(tbl);

/* remove any default values of the child elements */
var newField = newFields.childNodes;
for(var j=0; j<newField.length; j++)
{
newField[j].value = '';
}

/* insert the cloned node */
t.rows[t.rows.length - 1].parentNode.insertBefore(newFields, t.rows[t.rows.length - 1]);

/* re-assign the input 'name' and 'id' attributes accordingly */
for(var c = 0, els = t.getElementsByTagName('*'), i = 0; i < els.length; ++i)
{
if(els[i] == t.rows[c + 2])
{
++c;

if(els[i].id != dynamicFormWriteId)
els[i].id = '';
else
break;
}
else if(/^part/.test(els[i].name))
{
els[i].name = els[i].name.replace(/\d+/, '') + c;
}
}
}


Thanks John for your help