views:

82

answers:

3

I have the following HTML:

<html>
  <head><title>Title</title></head>
  <body>
    <div id='div2'>
      <a href='#'>1</a>
      <div id='div1'>
        <a href='#'>2</a>
      </div> 
    </div>

  </body>
</html>

... and the following Javascript code, which I'm running through Greasemonkey:

var nodes = document.body.getElementsByTagName('a');
for (var i = 0; i < nodes.length; i++) {
  var node = nodes[i];
  node.parentNode.removeChild(node);    
}

I would expect it to find and remove all A tags; instead it finds the first, but not the second. As far as I can tell it's having difficulty with the way the second A tag is nested.

Could someone please let me know how to remove all the tags, using getElementsByTagName? There are reasons I'd prefer not to use XPath if at all possible.

A: 

change your code to

var nodes = document.body.getElementsByTagName('a');
for (var i = 0; nodes.length > 0; i++) {
    var node = nodes[0];
    node.parentNode.removeChild(node);    
}

nodes.length gets evaluated everytime you remove a child.

Vinay B R
Even if nodes.length does get evaluated every time, why does `node.parentNode.removeChild(node)` actually remove it from the array, as opposed to just from the DOM node?
Jamie Wong
@Wong try debugging the original script, you will understand what i mean by reevaluated. as to y it happens you can look into any article on javascript best practices for an explaination.
Vinay B R
+1  A: 

Following Vinay's answer:

var nodes = document.body.getElementsByTagName('a');
while(nodes.length > 0) {
  nodes[0].parentNode.removeChild(nodes[0]);
}

Since using a for loop like that is bizarre if you aren't actually using the iterator for anything.

Jamie Wong
Actually I am using it in my code; the snippet I pasted was the smallest subset of my code that reproduced the problem.
Duncan Bayne
+1  A: 

Capture the length and remove in reverse order. This will eliminate side effects.

var nodes = document.body.getElementsByTagName('a');

for (var J=nodes.length-1;  J >= 0;  J--) //-- Kill the last, first, to avoid orphan problems.
{
    var node    = nodes[J];
    if (node)
    {
        node.parentNode.removeChild (node);
    }
}

But a better way...
Add this directive to your header:

// @require http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js

Then your whole code becomes:

$("a").remove ();
Brock Adams