SitePoint Sponsor

User Tag List

Results 1 to 8 of 8
  1. #1
    SitePoint Enthusiast
    Join Date
    Aug 2007
    Posts
    94
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    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

    Code HTML4Strict:
    <!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>

  2. #2
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    3 Thread(s)
    It's the parent of where you're inserting that needs to be used, so for example:

    Code javascript:
    targetRow.parentNode.insertBefore(row, targetRow);
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  3. #3
    SitePoint Enthusiast
    Join Date
    Aug 2007
    Posts
    94
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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.

    Quote Originally Posted by pmw57 View Post
    It's the parent of where you're inserting that needs to be used, so for example:

    Code javascript:
    targetRow.parentNode.insertBefore(row, targetRow);

  4. #4
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    3 Thread(s)
    Quote Originally Posted by kenoli View Post
    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.
    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:

    Code javascript:
    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);
    ...
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  5. #5
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    3 Thread(s)
    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:

    Code javascript:
    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.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  6. #6
    SitePoint Enthusiast
    Join Date
    Aug 2007
    Posts
    94
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by pmw57 View Post
    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.
    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

  7. #7
    Unobtrusively zen silver trophybronze trophy
    paul_wilkins's Avatar
    Join Date
    Jan 2007
    Location
    Christchurch, New Zealand
    Posts
    14,526
    Mentioned
    83 Post(s)
    Tagged
    3 Thread(s)
    Quote Originally Posted by kenoli View Post
    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.
    What it is seeing is the whitespace (newlines, tabs, spaces) between elements.

    Quote Originally Posted by kenoli View Post
    Does the DOM see a text object between every tag?
    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.

    Quote Originally Posted by kenoli View Post
    I suspect your while loop is the safe way to go.
    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.
    Programming Group Advisor
    Reference: JavaScript, Quirksmode Validate: HTML Validation, JSLint
    Car is to Carpet as Java is to JavaScript

  8. #8
    SitePoint Enthusiast
    Join Date
    Aug 2007
    Posts
    94
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •