0

I'm stucked with the following function, trying to get back a value (a part of a dom tree).

Instead of receiving a useful value I just obtain a 0/undefined.

var findNodeForAttributeValue = function (node, innerXmlAttributeName, innerXmlAttributeValue) {
var indirectReturnVar='0';
if ((node.nodeType === 1)&&(node.hasAttribute(innerXmlAttributeName))) {
    if (node.getAttribute(innerXmlAttributeName)==innerXmlAttributeValue) {
        indirectReturnVar=node;
        return indirectReturnVar;
    }
}
if((node.hasChildNodes())&&(node.firstChild!=null)) {
    Array.forEach ( node.childNodes, function (children) {
        findNodeForAttributeValue(children, innerXmlAttributeName, innerXmlAttributeValue);
    } );
    return indirectReturnVar;
}

Edit

Updated code:

var findNodeForAttributeValue = function (node, innerXmlAttributeName, innerXmlAttributeValue) {
  var indirectReturnVar='0';
  if ((node.nodeType === 1) && (node.hasAttribute(innerXmlAttributeName))) {
    if (node.getAttribute(innerXmlAttributeName) == innerXmlAttributeValue) {
      indirectReturnVar = node;
      return indirectReturnVar;
    }
  }

  if ((node.hasChildNodes()) && (node.firstChild != null)) {
    for (var fi=0, fiLen=node.childNodes.length; fi<fiLen; fi++) {
      findNodeForAttributeValue(node.childNodes[fi], innerXmlAttributeName, innerXmlAttributeValue);
    }
    return indirectReturnVar;
  }
}
3
  • Welcome to StackOverflow! Thanks for posting your code, but please put a little more description in your question: what problem are you having, what is the result you expect, and what have you tried so far? Commented Oct 25, 2013 at 0:20
  • childNodes is a NodeList, not an array. Commented Oct 25, 2013 at 0:49
  • RobG - seems to work anyway, i use this construct in other places with nodes... the problem is that I could not return a value... . Commented Oct 25, 2013 at 8:14

2 Answers 2

1

When you do:

> Array.forEach ( node.childNodes .. )

forEach is a method of Array instances that is on Array.prototype. the childNodes property is a NodeList, which is not an Array.

In some browsers that support ES5 you can do:

Array.prototype.forEach.call(childNodes, ...)

but that isn't guaranteed to work (and will fail in IE 8 and lower). So just use a for loop:

for (var i=0, iLen=node.childNodes.length; i<iLen; i++) {
  // do stuff with node.childNodes[i];
}

Edit

To fix your updated code:

function findNodeForAttributeValue (node, innerXmlAttributeName, innerXmlAttributeValue) {

Use a function declaration, I don't understand why you are using expressions with assignment. Also, shorter variable names will make life a lot easier, I'd probably do something like:

function getNodeByAttributeValue (node, att, value)

If you want a variable to have a truthy value, just set it to true. In this case, you want it falsey so either leave it undefined or set it to null (since most DOM methods return null if they don't get a matching element):

  var indirectReturnVar = null;

This for block is fine.

  if ((node.nodeType === 1) && (node.hasAttribute(innerXmlAttributeName))) {

    if (node.getAttribute(innerXmlAttributeName) == innerXmlAttributeValue) {
      indirectReturnVar = node;
      return indirectReturnVar;
    }
  }

  if ((node.hasChildNodes()) && (node.firstChild != null)) {

This bit needs modifying. Only keep looping while indirectReturnVar is falsey:

    for (var fi=0, fiLen=node.childNodes.length; fi<fiLen && !indirectReturnVar; fi++) {

Assign the returned value of the recursive function to indirectReturnVar, otherwise it gets lost in the ether.

      indirectReturnVar = findNodeForAttributeValue(node.childNodes[fi], innerXmlAttributeName, innerXmlAttributeValue);


    }
  }

Return the value outside the recursive loop. It will only loop until it either finds a matching node or runs out of nodes.

  return indirectReturnVar;
}
Sign up to request clarification or add additional context in comments.

3 Comments

What's wrong with this code?---var findNodeForAttributeValue = function (node, innerXmlAttributeName, innerXmlAttributeValue) { var indirectReturnVar='0'; if ((node.nodeType === 1)&&(node.hasAttribute(innerXmlAttributeName))) { if (node.getAttribute(innerXmlAttributeName)==innerXmlAttributeValue) { indirectReturnVar=node; return indirectReturnVar; } } if((node.hasChildNodes())&&(node.firstChild!=null)) { for(var fi=0, fiLen=node.childNodes.length; fi<fiLen; fi++) { findNodeForAttributeValue(node.childNodes[fi], innerXmlAttributeName, innerXmlAttributeValue); } return indirectReturnVar; } }
I think that the code with the array.foreach construct in the main question should be equivalent to the one I inserted into the comment, which uses the for cycle: the difference is that the first one executes quickly and exits correctly, the second hangs in an endless loop... What am I doing wrong? Uff
If you have updated code, you should post it in your OP. The issue with the code posted as a comment (apart from expecting others to copy and pate it, then format it to be readable) is that the recursive loop doesn't do anything with the returned value.
0

Below find1 takes a searching function f that will be called for the supplied node and once for each of the node's childNodes. When f returns true, the node is returned. Otherwise undefined is given which signals no result was found.

const find1 = (f, node, cursor = 0) =>
  node.nodeType === 1 && f (node)
    ? node
  : cursor === node.childNodes.length
    ? undefined
  : find1 (f, node.childNodes[cursor]) || find1 (f, node, cursor + 1)

console.log
  ( find1
      ( node => node.tagName === 'P'
      , document
      )
      // <p>first paragraph</p>
      
  , find1
      ( node => node.textContent === 'and a span'
      , document
      )
      // <span>and a span</span>
      
  , find1
      ( node => node.getAttribute('class') === 'last'
      , document
      )
      // <p class="last">last paragraph</p>
  )
<div id="main">
  <!-- comment -->
  <h1>title</h1>
  <p>first paragraph</p>
  <p>second paragraph <span>and a span</span></p>
  <p class="last">last paragraph</p>
<div>

Above find1 is not limited to searching for nodes by a specific attribute and value. Instead the user-supplied lambda allows the programmer to direct find1 toward its goal.


What if we want all the results of find? Below findAll returns an Array of all matched results

const findAll = (f, node) =>
{ const loop = function* (node)
  { if (node.nodeType === 1 && f (node))
      yield node
    for (const child of node.childNodes)
      yield* loop (child)
  }
  return Array.from (loop (node))
}

console.log
  ( findAll
      ( node => node.tagName === 'P'
      , document
      )
      // [ <p>first paragraph</p>
      // , <p>second paragraph<span>...</span></p>
      // , <p class="last">last paragraph</p>
      // ]
      
  , findAll
      ( node => node.getAttribute('class') === 'last'
      , document
      )
      // [ <p class="last">last paragraph</p> ]
  )
<div id="main">
  <!-- comment -->
  <h1>title</h1>
  <p>first paragraph</p>
  <p>second paragraph <span>and a span</span></p>
  <p class="last">last paragraph</p>
<div>


Higher-order functions like find1 and findAll are great because they can be specialized in all sorts of useful ways.

const findByTag = (tagName, node) =>
  findAll
    ( node => node.tagName === tagName.toUpperCase()
    , node
    )

const findByAttrValue = (attr, value, node) =>
  findAll
    ( node => node.getAttribute (attr) === value
    , node
    )

console.log
  ( findByTag ('p', document)
    // [ '<p>...</p>', '<p>...</p>', '<p>...</p>' ]

  , findByTag ('h1', document)
    // [ '<h1>title</h1>' ]

  , findByTag ('strong', document)
    // []

  , findByAttrValue ('class', 'last', document)
    // [ <p class="last">last paragraph</p> ]

  , findByAttrValue ('id', 'main', document)
    // [ <div id="main">...</div> ]

  , findByAttrValue ('class', 'first', document)
    // []
  )

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.