getByClassnameHierarchy实现的问题

时间:2019-03-24 16:00:17

标签: javascript dom

我正在尝试实现getByClassnameHierarchy,但是实现方面存在问题。 我应该使用BFS而不是DFS遍历节点吗?

有人可以向我解释使用这些方法的方法以及Pro和Con的方法吗?

    // Helper function to make output easier to read
const getIds = (elements=[]) => Array.from(elements).map(x => x.id);
    
    /**
     * Return all DOM elements who are _leaf_nodes_ that satisfy the hierarchy. 
     * Hierarchy is a string of class names separated by `>`, akin to 
     * CSS CHILD SELECTORS.
     * 
     * ex. getByClassnameHierarchy(#root, 'a>b>c') -> [<div class="c" id="c-1"></div>,<div class="c" id="c-2"></div> ]
     * "give me all the elements with class name 'c', who have a strict parent with 
     * class name 'b', who have a strict parent with class name 'a'"
     * 
     * @param root DOMElement: start the search from this DOM element
     * @param hierarchy string: `>`-delimited string of classnames
     * @return Array<DOMElement>: all DOM elements that satisfy the target hierarchy
     */
    function getByClassnameHierarchy(root, hierarchy) {
      // parentNode
      const res = [];
      const level = hierarchy.split('>');
      
      helper(root, level, 0);
      return res;
      
      function helper(root, level, cur) {
        
        if(!root) return
        
        if(root.classList && root.classList.contains(level[cur-1])){ // b
          
           if(root.parentNode.classList.contains(level[cur-2])) { // a
              if(root.classList.contains(level[cur-1])) {
                 res.push(root);
                }
            }
           
        } //c
      
        root.childNodes.forEach(child => {
          helper(child, level, cur + 1);
        });
      
      }
    }

    
    const root2 = document.getElementById('root2');

    // // basic case:
    console.log('actual: ', getIds(getByClassnameHierarchy(root2, 'a>b>c')));
    console.log(`a>b>c expected:` , `['c-1', 'c-2']`, '\n');
    <div id="root2">
      <div class="a" id="a-1">
        <div class="b" id="b-1">
          <div class="c" id="c-1"></div>
          <div class="c" id="c-2"></div>
        </div>
      </div>
    </div>

问题:

预期返回:[ 'b-1', 'c-1', 'c-2' ]而不是['c-1', 'c-2']

不确定我要去哪里。

1 个答案:

答案 0 :(得分:1)

您可以执行以下操作(查看评论以获取详细信息):

const getIds = (elements = []) => Array.from(elements).map(x => x.id);

function getByClassnameHierarchy(root, hierarchy, level = 0) {
  let result = [];
  
  // Grab the class names
  const classNames = hierarchy.split('>');
  
  // Retrieve the current (first) one
  const currentClassName = classNames[0];
  
  // For each child
  root.childNodes.forEach(child => {
    // If it contains the given class
    if (child.classList && child.classList.contains(currentClassName)) {
      // Append the result of the following selector on this child, 
      // or the child itself if we're at the last part of the selector
      result = result.concat(classNames.length > 1 
        ? getByClassnameHierarchy(child, classNames.slice(1).join('>'), level + 1)
        : child);
    }
    // Otherwise, if we're still on the first part of the selector, 
    // append the result of the same selector on this child
    else if (level === 0) {
      result = result.concat(getByClassnameHierarchy(child, hierarchy, level));
    }
  });
  
  return result;
}

// Test

const root2 = document.getElementById('root2');

console.log('a>b>c actual:', getIds(getByClassnameHierarchy(root2, 'a>b>c')));
console.log('a>b>c expected:', ['c-1', 'c-2']);

console.log('b>c actual: ', getIds(getByClassnameHierarchy(root2, 'b>c')));
console.log('b>c expected:', ['c-1', 'c-2', 'c-3', 'c-4']);

console.log('b actual: ', getIds(getByClassnameHierarchy(root2, 'b')));
console.log('b expected:', ['b-1', 'b-2']);

console.log('a>c actual: ', getIds(getByClassnameHierarchy(root2, 'a>c')));
console.log('a>c expected:', []);
<div id="root2">
  <div class="a" id="a-1">
    <div class="b" id="b-1">
      <div class="c" id="c-1"></div>
      <div class="c" id="c-2"></div>
    </div>
  </div>
  <div class="b" id="b-2">
    <div class="c" id="c-3"></div>
    <div class="c" id="c-4"></div>
  </div>
</div>