admin管理员组文章数量:1129003
I am able to do this:
<div id="myDiv">
<div class="foo"></div>
</div>
myDiv = getElementById("myDiv");
myDiv.querySelectorAll("#myDiv > .foo");
That is, I can successfully retrieve all the direct children of the myDiv
element that have class .foo
.
The problem is, it bothers me that I must include the #myDiv
in the selector, because I am running the query on the myDiv
element (so it is obviously redundant).
I ought to be able to leave the #myDiv
off, but then the selector is not legal syntax since it starts with a >
.
Does anyone know how to write a selector which gets just the direct children of the element that the selector is running on?
I am able to do this:
<div id="myDiv">
<div class="foo"></div>
</div>
myDiv = getElementById("myDiv");
myDiv.querySelectorAll("#myDiv > .foo");
That is, I can successfully retrieve all the direct children of the myDiv
element that have class .foo
.
The problem is, it bothers me that I must include the #myDiv
in the selector, because I am running the query on the myDiv
element (so it is obviously redundant).
I ought to be able to leave the #myDiv
off, but then the selector is not legal syntax since it starts with a >
.
Does anyone know how to write a selector which gets just the direct children of the element that the selector is running on?
Share Improve this question edited Jul 4, 2017 at 16:40 Bugs 4,4899 gold badges33 silver badges41 bronze badges asked Sep 9, 2010 at 21:57 mattshmattsh 6,3036 gold badges25 silver badges21 bronze badges 2- 7 checkout stackoverflow.com/questions/6481612/… – Anurag Commented Aug 30, 2012 at 15:34
- Did none of the answers accomplish what you needed? Please provide feedback or select an answer. – Randy Hall Commented Dec 7, 2012 at 20:50
15 Answers
Reset to default 370Good question. At the time it was asked, a universally-implemented way to do "combinator rooted queries" (as John Resig called them) did not exist.
Now the :scope pseudo-class has been introduced. It is not supported on [pre-Chrominum] versions of Edge or IE, but has been supported by Safari for a few years already. Using that, your code could become:
let myDiv = getElementById("myDiv");
myDiv.querySelectorAll(":scope > .foo");
Note that in some cases you can also skip .querySelectorAll
and use other good old-fashioned DOM API features. For example, instead of myDiv.querySelectorAll(":scope > *")
you could just write myDiv.children
, for example.
Otherwise if you can't yet rely on :scope
, I can't think of another way to handle your situation without adding more custom filter logic (e.g. find myDiv.getElementsByClassName("foo")
whose .parentNode === myDiv
), and obviously not ideal if you're trying to support one code path that really just wants to take an arbitrary selector string as input and a list of matches as output! But if like me you ended up asking this question simply because you got stuck thinking "all you had was a hammer" don't forget there are a variety of other tools the DOM offers too.
Does anyone know how to write a selector which gets just the direct children of the element that the selector is running on?
The correct way to write a selector that is "rooted" to the current element is to use :scope
.
var myDiv = getElementById("myDiv");
var fooEls = myDiv.querySelectorAll(":scope > .foo");
However, browser support is limited and you'll need a shim if you want to use it. I built scopedQuerySelectorShim for this purpose.
I Use This:
You can avoid typing "myDiv" twice AND using the arrow.
There are of course always more possibilities.
A modern browser is probably required.
<!-- Sample Code -->
<div id="myDiv">
<div class="foo">foo 1</div>
<div class="foo">foo 2
<div class="bar">bar</div>
</div>
<div class="foo">foo 3</div>
</div>
// Return HTMLCollection (Matches 3 Elements)
var allMyChildren = document.querySelector("#myDiv").children;
// Return NodeList (Matches 7 Nodes)
var allMyChildren = document.querySelector("#myDiv").childNodes;
// Match All Children With Class Of Foo (Matches 3 Elements)
var myFooChildren = document.querySelector("#myDiv").querySelectorAll(".foo");
// Match Second Child With Class Of Foo (Matches 1 Element)
var mySecondChild = document.querySelector("#myDiv").querySelectorAll(".foo")[1];
// Match All Children With Class Of Bar (Matches 1 Element)
var myBarChild = document.querySelector("#myDiv").querySelector(".bar");
// Match All Elements In "myDiv" (Matches 4 Elements)
var myDescendants = document.querySelector("#myDiv").querySelectorAll("*");
if you know for sure the element is unique (such as your case with the ID):
myDiv.parentElement.querySelectorAll("#myDiv > .foo");
For a more "global" solution: (use a matchesSelector shim)
function getDirectChildren(elm, sel){
var ret = [], i = 0, l = elm.childNodes.length;
for (var i; i < l; ++i){
if (elm.childNodes[i].matchesSelector(sel)){
ret.push(elm.childNodes[i]);
}
}
return ret;
}
where elm
is your parent element, and sel
is your selector. Could totally be used as a prototype as well.
Here's a flexible method, written in vanilla JS, that allows you to run a CSS selector query over only the direct children of an element:
var count = 0;
function queryChildren(element, selector) {
var id = element.id,
guid = element.id = id || 'query_children_' + count++,
attr = '#' + guid + ' > ',
selector = attr + (selector + '').replace(',', ',' + attr, 'g');
var result = element.parentNode.querySelectorAll(selector);
if (!id) element.removeAttribute('id');
return result;
}
function siblings(el) {
return el.closest('*:not(:scope)').querySelectorAll(':scope > *');
}
Pure JS Code
el
- is your element
here we have function which works in next steps:
- take any direct parent, but not el which we are passed (closest can take el, so we are ensure el will not be as a result)
- take all direct children
The following solution is different to the ones proposed so far, and works for me.
The rationale is that you select all matching children first, and then filter out the ones which are not direct children. A child is a direct child if it does not have a matching parent with the same selector.
function queryDirectChildren(parent, selector) {
const nodes = parent.querySelectorAll(selector);
const filteredNodes = [].slice.call(nodes).filter(n =>
n.parentNode.closest(selector) === parent.closest(selector)
);
return filteredNodes;
}
HTH!
I created a function to handle this situation, thought I would share it.
getDirectDecendent(elem, selector, all){
const tempID = randomString(10) //use your randomString function here.
elem.dataset.tempid = tempID;
let returnObj;
if(all)
returnObj = elem.parentElement.querySelectorAll(`[data-tempid="${tempID}"] > ${selector}`);
else
returnObj = elem.parentElement.querySelector(`[data-tempid="${tempID}"] > ${selector}`);
elem.dataset.tempid = '';
return returnObj;
}
In essence what you are doing is generating a random-string (randomString function here is an imported npm module, but you can make your own.) then using that random string to guarantee that you get the element you are expecting in the selector. Then you are free to use the >
after that.
The reason I am not using the id attribute is that the id attribute may already be used and I don't want to override that.
You could extend Element to include a method getDirectDesc()
like this:
Element.prototype.getDirectDesc = function() {
const descendants = Array.from(this.querySelectorAll('*'));
const directDescendants = descendants.filter(ele => ele.parentElement === this)
return directDescendants
}
const parent = document.querySelector('.parent')
const directDescendants = parent.getDirectDesc();
document.querySelector('h1').innerHTML = `Found ${directDescendants.length} direct descendants`
<ol class="parent">
<li class="b">child 01</li>
<li class="b">child 02</li>
<li class="b">child 03 <ol>
<li class="c">Not directDescendants 01</li>
<li class="c">Not directDescendants 02</li>
</ol>
</li>
<li class="b">child 04</li>
<li class="b">child 05</li>
</ol>
<h1></h1>
Well we can easily get all the direct children of an element using childNodes
and we can select ancestors with a specific class with querySelectorAll
, so it's not hard to imagine we could create a new function that gets both and compares the two.
HTMLElement.prototype.queryDirectChildren = function(selector){
var direct = [].slice.call(this.directNodes || []); // Cast to Array
var queried = [].slice.call(this.querySelectorAll(selector) || []); // Cast to Array
var both = [];
// I choose to loop through the direct children because it is guaranteed to be smaller
for(var i=0; i<direct.length; i++){
if(queried.indexOf(direct[i])){
both.push(direct[i]);
}
}
return both;
}
Note: This will return an Array of Nodes, not a NodeList.
Usage
document.getElementById("myDiv").queryDirectChildren(".foo");
I would like to add that you can extend the compatibility of :scope by just assigning a temporary attribute to the current node.
let node = [...];
let result;
node.setAttribute("foo", "");
result = window.document.querySelectorAll("[foo] > .bar");
// And, of course, you can also use other combinators.
result = window.document.querySelectorAll("[foo] + .bar");
result = window.document.querySelectorAll("[foo] ~ .bar");
node.removeAttribute("foo");
Single line Version
var direct_children=Array.from(parent_el.querySelectorAll('span')).filter(function(a){return a.parentNode===parent_el;});
I found this is very handy in case parent element is given. I tested it and it worked 100%.
I was working on Bootstrap and I had the same question and I used the below js to help me
let items = document.querySelectorAll(
".multi-item-carousel .carousel-inner .carousel-item"
);
My HTML looks like
<div
id="recipeCarousel"
className="carousel slide multi-item-carousel"
data-bs-ride="carousel"
>
<div className="carousel-inner" role="listbox">
<div className="carousel-item active">
I'd have gone with
var myFoo = document.querySelectorAll("#myDiv > .foo");
var myDiv = myFoo.parentNode;
I am just doing this without even trying it. Would this work?
myDiv = getElementById("myDiv");
myDiv.querySelectorAll(this.id + " > .foo");
Give it a try, maybe it works maybe not. Apolovies, but I am not on a computer now to try it (responding from my iPhone).
本文标签: javascriptUsing querySelectorAll to retrieve direct childrenStack Overflow
版权声明:本文标题:javascript - Using querySelectorAll to retrieve direct children - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736706794a1948720.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论