不使用 :scope 的 querySelectorAll 根元素

querySelectorAll root elements without using :scope

假设我们有这个 HTML 结构:

<div id='test-div'>
    <div class='random-class-1'>
        <div class='useless-element-1'>
        </div>
        <div class='useless-element-2'>
        </div>
        <p>Just a paragraph, and useless</p>
    </div>
    <div class='random-class-2'>
        <div class='useless-element-3'>
        </div>
        <div class='useless-element-4'>
        </div>
        <div class='useless-element-5'>
        </div>
    </div>
</div>

我需要 select 所有 children "DIV elements"(不是 grandchildren)在第一个 DIV 中(在这个例子中 id='test-div'),不是来自文档,而是来自元素 (div) 本身。

所以,我不想使用下面的 "query",因为我已经 select 编辑了元素 DIV [object HTMLDivElement ]:

// I don't want to use this query
var children = document.querySelectorAll("div > div");

只是实现此目的的示例 (https://jsfiddle.net/t4gxt65k/):

// I already have selected DIV element 
var el = document.getElementById("test-div")
// OR var el = document.querySelectorAll("#test-div");

var children = el.querySelectorAll(":scope > div");

但是因为浏览器不兼容我不想使用":scope"

真正的问题是:

如何使用纯 JavaScript 获取 [object HTMLDivElement] 的 children(仅 DIV 元素)?

要获得元素的直接子元素,请使用 parentNode.children or parentNode.childNodes, and Array.prototype.reduce 的组合,如下所示:

var children = Array.prototype.reduce.call(el.children, function(acc, e) {
    if(e.tagName == "DIV")
        acc.push(e);
    return acc;
}, []);

作为一个选项,您可以为您的范围元素设置一个临时的 唯一属性 ,然后将 querySelectorAll() 用于其父元素,并将属性选择器添加到您想要的内容之前将放在 :scope 选择器之后:

// The `scope` variable stores a reference to the scope element.
var attrName = '_scope'  + Date.now();
scope.setAttribute(attrName, '');
var children = scope.parentNode.querySelector('[' + attrName + '] > DIV');

虽然我不确定它有多快。

Fwiw,专门用于获取 child 元素,有 children DOM 属性:

var children = scope.children;

类似于 in selecting children but less complicated since it does not address prototype and reduce. Instead it uses Array.from, Array.filter and Element.matches。 Element.matches 使它更加通用,因为它使用普通的选择器字符串(就像在 querySelectorAll 中一样)。

Array.from(rootElement.children)
  .filter(elm => elm.matches('div'))

const rootElement = document.getElementById('test-div')

Array.from(rootElement.children)
  .filter(elm=>elm.matches('div'))
  .forEach(elm=>elm.classList.add('matched'))
div {
  padding: 1rem;
  box-shadow: 0 0 0 1px gray inset;
}
.matched {
  box-shadow: 0 0 0 1px red inset;
}
<div id='test-div'>
    <div class='random-class-1'>
        <div class='useless-element-1'></div>
        <div class='useless-element-2'></div>
        <p>Just a paragraph, and useless</p>
    </div>
    <div class='random-class-2'>
        <div class='useless-element-3'></div>
        <div class='useless-element-4'></div>
        <div class='useless-element-5'></div>
    </div>
</div>