Using insertBefore

Thanks to those who helped me with my issue related to the value of input fields.

Now I am onto my next confusion: how to use insertBefore().

In the code below, I clone a row to create a template for the row I want to insert and then modify the input field value per the help I got from this group in a previous post. I then identify the row I want to insert this row before and then implement the insertBefore() method. I’m not sure what the mother object is for this method (what comes before the dot) but assumed it was document.

The code below is commented to explain how I did all of this.

At any rate, this code doesn’t work. I’d love some help understanding what I am doing wrong.

Thanks,

–Kenoli


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>

<title>Title</title>

<style text/css>

a {text-decoration: none}

</style>

<script type="text/javascript">

function newRow(t){
var row = t.parentNode.parentNode.cloneNode(true);  // Clone current row as template for row to be inserted
row.firstChild.nextSibling.firstChild.value = 'Input Name'; // Alter value of the input field in this row
var targetRow = row.nextSibling; // Row before which new row is to be inserted
document.insertBefore(row, targetRow); // Insert row before targeRow (what's wrong here?)
}

function removeRow(t){
var y = t.parentNode.parentNode;
y.parentNode.removeChild(y);
}
</script>

</head>
<body>

<table border="1" >
<tr id="74" ><td><a href="#" onclick="newRow(this);" >+</a>&nbsp;&nbsp;&nbsp;<a href="#" onclick="removeRow(this);" >-</a>&nbsp;&nbsp;</td><td class="small" ><input type="text" value="Charles" ></td></tr>
</table>

</body>
</html>

It’s the parent of where you’re inserting that needs to be used, so for example:


targetRow.parentNode.insertBefore(row, targetRow);

Thanks. I seem to be having another issue as well.

When I display an alert on row it confirms that it is a row element, but when I do an alert on targetRow (row.nextSibling) I get a null. Of course, this keeps insertBefore() from working.

When you clone a node, that cloned node is unattached to the document so it’s not possible to use that cloned node to obtain things like parent relationships of the source node.

What you need to do is to use something other than the cloned node to obtain the target row reference.

Also, when using nextSibling, it’s entirely possible for that next sibling to be a whitespace element, or a comment element, so you need to check that the element is proper node element (type 1) and if it’s not you need to keep on walking forward until you find one.

Possibly by using something like this:


var existingRow = t.parentNode.parentNode;
var targetRow = existingRow.nextSibling;
// if nextSibling is not an element, keep on walking forward
while (targetRow.nodeType !== 1 && targetRow.nextSibling) {
    targetRow = targetRow.nextSibling;
}
var row = existingRow.cloneNode(true);
...

But what the code says it wants to do doesn’t make sense.

Do you want to insert before the clicked row, or after the clicked row?
If before, you use insertBefore in reference to a known existing element.
If after, you need to use an existing element after it as the target. If none exists, you need to append to the parent instead.

For example:


var currentRow = t.parentNode.parentNode;
var row = currentRow.cloneNode(true);
var targetRow = currentRow.nextSibling;

// get following row
while (targetRow.nodeType !== 1 && targetRow.nextSibling) {
    targetRow = targetRow.nextSibling;
}

// reset input field
row.getElementsByTagName('input')[0].value = 'Input Name';

// add new row
if (!targetRow) {
    currentRow.parentNode.appendChild(row);
} else {
    targetRow.parentNode.insertBefore(row, targetRow);
}

This code has been tested and works fine.
Before your page works though, other problems need to be fixed too.

Thanks. This is so obvious, I’m surprised it didn’t bite me.

Also, when using nextSibling, it’s entirely possible for that next sibling to be a whitespace element, or a comment element, so you need to check that the element is proper node element (type 1) and if it’s not you need to keep on walking forward until you find one.

Of course, now, nextSibling gives me something. In this case it is [object text] which must be a type 1 node as it inserts the cloned row successfully. There is nothing in the source code between the row tags. I wonder why it sees a text object there. Does the DOM see a text object between every tag?

I suspect your while loop is the safe way to go.

Thanks for your help.

–Kenoli

What it is seeing is the whitespace (newlines, tabs, spaces) between elements.

That depends on the browser. IE doesn’t, while most others do.
When setting the input value, you will find nextSibling behaves differently depending on browser and how you format your HTML code. I’ve suggested a more reliable technique in the sample code that uses getElementsByTagName instead.

Once you have obtained an element (or none, which is entirely possible) you need to use different techniques depending on whether you have found an element after it. My previous post shows what should be done there too.

Paul – Thanks so much for your assistance. It has been both helpful and a great learning opportunity.

Even though I didn’t respond explicitly to your most recent suggestions, I appreciated them and will take advantage of the good advice.

–Kenoli