javascript - getByClassnameHierarchy 实现的问题

标签 javascript dom

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

有人可以向我解释一下使用这些方法的方法以及优点和缺点吗?

    // 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' ]

不知道我错在哪里。

最佳答案

2020 年编辑(感谢 @techguy2000 的评论):

此版本应该正确处理根元素本身具有某些类的情况。它还修复了先前版本的一些问题:

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

function getByClassnameHierarchy(element, hierarchy, level = 0) {
  const result = [];
  
  const classNames = hierarchy.split('>');
  const currentClassName = classNames[0];
  
  const isClassFound = element.classList && element.classList.contains(currentClassName);
  const isLastClass = classNames.length === 1;
  
  if (isClassFound) {
    if (isLastClass) {
      result.push(element);
    } else {
      element.childNodes.forEach(child => {
        result.push(...getByClassnameHierarchy(child, classNames.slice(1).join('>'), level + 1));
      });
    }
  } 
  
  if (level === 0) {
    element.childNodes.forEach(child => {
      result.push(...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', 'c-3', 'c-4']);

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 class="a" 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>

初始答案:

你可以这样做(查看评论以了解详细信息):

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>

关于javascript - getByClassnameHierarchy 实现的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55325681/

相关文章:

javascript - 如何检查日期距当前年份是否超过一年

javascript - div调整大小后如何检测html div的宽度和高度

javascript - chrome 扩展未捕获 ReferenceError : $ is not defined

python - 如何在 Python 的 xml.dom.minidom 中设置元素的 id?

javascript - 用javascript将一个HTML节点分割为多个节点

javascript - 如何创建将覆盖 ng-disabled 的自定义 Angular Directive(指令)?

javascript - Angular js-值更改时突出显示dom

html - 浏览器如何解决冲突的类?

javascript - 如何获取每个角色的位置

javascript - IE9 是否按文档顺序加载 SVG dom?