proposal for a new acceptNode() return value for TreeWalker.

Hello,

According to DOM4, when an author creates a TreeWalker object, the
return value of the acceptNode() method would be one of the following
three:

  FILTER_ACCEPT   the current node passes the filter (and the
                  descendant would still be traversed).
  FILTER_REJECT   the current node doesn't pass the filter, and the
                  descendant would not be traversed.
  FILTER_SKIP     the current node doesn't pass the filter, but
                  continue traversing the decendants.

All three values can't describe the "the current node passes the filter,
but the descendants would not be traversed" state. In other words,
there's a missing value:

              FILTER_ACCEPT   FILTER_REJECT   FILTER_SKIP   FILTER_?

passes the         YES             NO             NO           YES
  filter

descendants        YES             NO             YES          NO
  traversed


Consider a use case when an author wants to select any <div> element in
an element with id "myid" such that it's not a descendant of an element
matching ".any". With TreeWalker, the following JavaScript code can
achieve this.

  var selected = [];
  var domWalker = document.createTreeWalker(
    document.getElementById('myid'),
    NodeFilter.SHOW_ELEMENT,
    { acceptNode: function(node) {
        if((node.parentNode.classList.contains('any')) {
          return NodeFilter.FILTER_REJECT;
        } else if(node.tagName == 'DIV') {
          return NodeFilter.FILTER_ACCEPT;
        } else {
          return NodeFilter.FILTER_SKIP;
        }
    } },
    false
  );

  while(domWalker.nextNode()) {
    selected.push(domWalker.currentNode);
  }

For a node matching "div.any", when UA traverses it, among all three
possible return value of acceptNode(), only FILTER_ACCEPT indicates that
this node passes the filter. However, FILTER_ACCEPT couldn't stop  UA
from traversing its children, so when its children is being traversed,
the code has to determine if the parent node is matching ".any" to
determine if acceptNode() can return a FILTER_REJECT here.

If acceptNode() can return a value representing "the current node
passeds the filter, but the descendants would not be traversed", say,
FILTER_FINISH, then when UA traverses a node that passes the filter, it
may still decide not to traverse the children of the node. Therefore,
there's no longer a need to use *the characteristics of the parent node*
to decide what the return value of acceptNode() for the current node is
(which is also prone to error. For example, the above code throws when
the element with id "myid" doesn't have a parent):

  var selected = [];
  var domWalker = document.createTreeWalker(
    document.getElementById('myid'),
    NodeFilter.SHOW_ELEMENT,
    { acceptNode: function(node) {
        if((node.parentNode.classList.contains('any')) {
          return node.tagName == 'DIV' ? NodeFilter.FILTER_FINISH :
                                         NodeFilter.FILTER_REJECT;
        } else {
          return node.tagName == 'DIV' ? NodeFilter.FILTER_ACCEPT :
                                         NodeFilter.FILTER_SKIP;
        }
    } },
    false
  );

  while(domWalker.nextNode()) {
    selected.push(domWalker.currentNode);
  }

FILTER_FINISH would be equivalent to FILTER_ACCEPT when used
for a filter of a NodeIterator.


Cheer,
Leo

Received on Sunday, 12 August 2012 16:24:37 UTC