admin管理员组

文章数量:1352152

This is problem I was asked and am wondering how to do or whether it's a trick question. I've only been working with JavaScript for a short while, so I'm not too sure.

Assume you have a web page with lots of content. Without using any library or getElementsByClassName, traverse the DOM and find all elements which have a particular class name.

Sample HTML

<body>
    <div>
        <div class='myTarget'>
             Target exists here
        </div>
    </div>

    <div>
        <table>
            <tbody>
              <tr> <td class='myTarget'> Target exists here </td> </tr>
            </tbody>
        </table>
    </div>

    <div>
       <span class='myTarget notSameAsTarget'>Stuff<span>
    </div>

</body>

My first thought is that this should be a recursive function and should start at the root document.documentElement

JS:

var root = document.documentElement;
var targetClass = 'myTarget';
var elementsWithTargetClass = []; // store in array

function traverse(element, targetClassName){
    // get class of current element
    var currentClass = element.className;

    // add to array if class matches
    if(currentClass.trim() === targetClassName)
        elementsWithTargetClass.push(element);

    // recursive call
    if(element.children){
         traverse(element, targetClassName);
    }

}

Any suggestions on what I'm missing?

    // recursive call - updated
    if(element.children){
         for(var child in element.children)
             traverse(element.children[child], targetClassName);
    }

This is problem I was asked and am wondering how to do or whether it's a trick question. I've only been working with JavaScript for a short while, so I'm not too sure.

Assume you have a web page with lots of content. Without using any library or getElementsByClassName, traverse the DOM and find all elements which have a particular class name.

Sample HTML

<body>
    <div>
        <div class='myTarget'>
             Target exists here
        </div>
    </div>

    <div>
        <table>
            <tbody>
              <tr> <td class='myTarget'> Target exists here </td> </tr>
            </tbody>
        </table>
    </div>

    <div>
       <span class='myTarget notSameAsTarget'>Stuff<span>
    </div>

</body>

My first thought is that this should be a recursive function and should start at the root document.documentElement

JS:

var root = document.documentElement;
var targetClass = 'myTarget';
var elementsWithTargetClass = []; // store in array

function traverse(element, targetClassName){
    // get class of current element
    var currentClass = element.className;

    // add to array if class matches
    if(currentClass.trim() === targetClassName)
        elementsWithTargetClass.push(element);

    // recursive call
    if(element.children){
         traverse(element, targetClassName);
    }

}

Any suggestions on what I'm missing?

    // recursive call - updated
    if(element.children){
         for(var child in element.children)
             traverse(element.children[child], targetClassName);
    }
Share Improve this question edited Nov 13, 2014 at 22:31 SoluableNonagon asked Nov 13, 2014 at 22:08 SoluableNonagonSoluableNonagon 11.8k11 gold badges58 silver badges100 bronze badges 10
  • traverse(element) That isn't what you want. – SLaks Commented Nov 13, 2014 at 22:11
  • To full traverse the DOM you need to do a pre-order traversal of the tree. – Ryan Commented Nov 13, 2014 at 22:11
  • 1 You're recursively calling traverse() on the same element (instead of its children), infinitely. – Paul Roub Commented Nov 13, 2014 at 22:14
  • 2 Also, you'll want to do some sort of regex on className, otherwise you'll miss elements that have both myTarget and another class. – Paul Roub Commented Nov 13, 2014 at 22:15
  • @PaulRoub can you suggest something? – SoluableNonagon Commented Nov 13, 2014 at 22:18
 |  Show 5 more ments

5 Answers 5

Reset to default 8

Your recursive call to traverse() passes the same element that was initially passed in, so it's just doing the exact same thing over and over until the stack overflows (hey!). You need to call traverse for each of the children of element rather than passing element back in.

Use document.querySelector. This is not getElementsByClassName(), nor a library. ;)

document.querySelector('.myTarget')

Taking into account elements with multiple classes, and starting with body:

  var targetClass = 'myTarget';
  var elementsWithTargetClass = []; // store in array
  var re = new RegExp("\\b" + targetClass + "\\b");
  
  traverse(document.body);
  
  for ( var j = 0; j < elementsWithTargetClass.length; ++j )
    elementsWithTargetClass[j].style.fontWeight = "bold";
  
  function traverse(element, targetClassName){
      // get class of current element
      var currentClass = element.className;
  
      if (currentClass.match(re))
      // add to array if class matches
    //  if(currentClass.trim() === targetClassName)
          elementsWithTargetClass.push(element);
  
      // recursive call
      if(element.children){
        for ( var i = 0; i < element.children.length; ++i )
           traverse(element.children[i]);
      }
  }
<div>
  <ul>
    <li class="myTarget">this</li>
    <li class="myTarget andAnotherClass">also this</li>
    <li>not this</li>
  </ul>
</div>

You are getting into to many recursions which makes the call stack grow to high. Try chaning your recursive function into a loop. This should not give you any problems.

var root = document.documentElement;
var targetClass = 'myTarget';
var elementsWithTargetClass = []; // store in array
pre_order(root);

function pre_order(node) {
  if(node.className == targetClass) 
    elementsWithTargetClass.push(node);       
  for(var i=0; i < node.childNodes.length; i++)
    pre_order(node.childNodes[i]);
}
console.log(elementsWithTargetClass);

JSFiddle

本文标签: javascriptFind an element with a class without library or getElementsByClassNameStack Overflow