Weird behaviour when using querySelectorWhen are you supposed to use escape instead of encodeURI / encodeURIComponent?When to use double or single quotes in JavaScript?How to execute a JavaScript function when I have its name as a stringChange the selected value of a drop-down list with jQueryWhat is the purpose of the var keyword and when should I use it (or omit it)?How to decide when to use Node.js?What is the explanation for these bizarre JavaScript behaviours mentioned in the 'Wat' talk for CodeMash 2012?Get current applied style with JSRunning JavaScript function to gives “Uncaught SyntaxError: Unexpected token }”Accessing elements from HTML in JavaScript
How to create a consistent feel for character names in a fantasy setting?
How can I add custom success page
Email Account under attack (really) - anything I can do?
What to wear for invited talk in Canada
Manga about a female worker who got dragged into another world together with this high school girl and she was just told she's not needed anymore
Does bootstrapped regression allow for inference?
Shall I use personal or official e-mail account when registering to external websites for work purpose?
"My colleague's body is amazing"
Can I legally use front facing blue light in the UK?
Why was the "bread communication" in the arena of Catching Fire left out in the movie?
aging parents with no investments
What does "enim et" mean?
Extreme, but not acceptable situation and I can't start the work tomorrow morning
Calculate Levenshtein distance between two strings in Python
What are the advantages and disadvantages of running one shots compared to campaigns?
Domain expired, GoDaddy holds it and is asking more money
Why do UK politicians seemingly ignore opinion polls on Brexit?
Are white and non-white police officers equally likely to kill black suspects?
Piano - What is the notation for a double stop where both notes in the double stop are different lengths?
LWC and complex parameters
When blogging recipes, how can I support both readers who want the narrative/journey and ones who want the printer-friendly recipe?
Re-submission of rejected manuscript without informing co-authors
Why is the design of haulage companies so “special”?
Ideas for 3rd eye abilities
Weird behaviour when using querySelector
When are you supposed to use escape instead of encodeURI / encodeURIComponent?When to use double or single quotes in JavaScript?How to execute a JavaScript function when I have its name as a stringChange the selected value of a drop-down list with jQueryWhat is the purpose of the var keyword and when should I use it (or omit it)?How to decide when to use Node.js?What is the explanation for these bizarre JavaScript behaviours mentioned in the 'Wat' talk for CodeMash 2012?Get current applied style with JSRunning JavaScript function to gives “Uncaught SyntaxError: Unexpected token }”Accessing elements from HTML in JavaScript
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty height:90px;width:728px;box-sizing:border-box;
As my understanding, when using element.querySelector()
, the query should be start on particular element.
However, when I run using code below, it keep selected the first DIV
tag in particular element.
const rootDiv = document.getElementById('test');
console.log(rootDiv.querySelector('div').innerHTML);
console.log(rootDiv.querySelector('div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div > div > div').innerHTML);
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
As you can see, the first few results is the same.
This this a bug? Or it will query from start of the document?
javascript
add a comment |
As my understanding, when using element.querySelector()
, the query should be start on particular element.
However, when I run using code below, it keep selected the first DIV
tag in particular element.
const rootDiv = document.getElementById('test');
console.log(rootDiv.querySelector('div').innerHTML);
console.log(rootDiv.querySelector('div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div > div > div').innerHTML);
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
As you can see, the first few results is the same.
This this a bug? Or it will query from start of the document?
javascript
add a comment |
As my understanding, when using element.querySelector()
, the query should be start on particular element.
However, when I run using code below, it keep selected the first DIV
tag in particular element.
const rootDiv = document.getElementById('test');
console.log(rootDiv.querySelector('div').innerHTML);
console.log(rootDiv.querySelector('div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div > div > div').innerHTML);
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
As you can see, the first few results is the same.
This this a bug? Or it will query from start of the document?
javascript
As my understanding, when using element.querySelector()
, the query should be start on particular element.
However, when I run using code below, it keep selected the first DIV
tag in particular element.
const rootDiv = document.getElementById('test');
console.log(rootDiv.querySelector('div').innerHTML);
console.log(rootDiv.querySelector('div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div > div > div').innerHTML);
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
As you can see, the first few results is the same.
This this a bug? Or it will query from start of the document?
const rootDiv = document.getElementById('test');
console.log(rootDiv.querySelector('div').innerHTML);
console.log(rootDiv.querySelector('div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div > div > div').innerHTML);
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
const rootDiv = document.getElementById('test');
console.log(rootDiv.querySelector('div').innerHTML);
console.log(rootDiv.querySelector('div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div > div').innerHTML);
console.log(rootDiv.querySelector('div > div > div > div > div').innerHTML);
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
javascript
javascript
edited 16 hours ago
Abana Clara
1,6651020
1,6651020
asked 17 hours ago
Joey ChongJoey Chong
1,0371016
1,0371016
add a comment |
add a comment |
3 Answers
3
active
oldest
votes
What querySelector
does is it finds an element somewhere in the document that matches the CSS selector passed, and then checks that the found element is a descendant of the element you called querySelector
on. It doesn't start at the element it was called on and search downwards - rather, it always starts at the document level, looks for elements that match the selector, and checks that the element is also a descendant of the calling context element. It's a bit unintuitive.
So:
someElement.querySelector(selectorStr)
is like
[...document.querySelectorAll(selectorStr)]
.find(elm => someElement.contains(elm));
A possible solution is to use :scope
to indicate that you want the selection to start at the rootDiv
, rather than at document
:
const rootDiv = document.getElementById('test');
alert(rootDiv.querySelector(':scope > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div > div').innerHTML);
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
:scope
is supported in all modern browsers but Edge.
Neat trick! Thanks!
– Tico
17 hours ago
:scope
is not supported by all but most of the major browsers. Anyways, thank for explanation and this should be the answer. Thanks.
– Joey Chong
17 hours ago
:scope
doesn't work with IE and Edge, I think it's important to point this out
– Cristian Traìna
17 hours ago
Here is the browser compatibility table - we have to resist just few months more since Edge is implementing Chromium engine :P
– Cristian Traìna
17 hours ago
Do you have any sources for the browser engine actually working this way ? - The query would have the same result, if the engine would start with the first div and check if it matches the query selector...
– Falco
14 hours ago
add a comment |
The currently accepted answer somehow provides a valid logical explanation as to what happens, but they are factually wrong.
Element.querySelector
triggers the match a selector against tree algorithm, which goes from the root element and checks if its descendants do match the selector.
The selector itself is absolute, it doesn't have any knowledge of a Document and doesn't even require that your Element be appended to any. And apart from the :scope
attribute, it doesn't either care with which root you called the querySelector
method.
If we wanted to rewrite it ourselves, it would be more like
const walker = document.createTreeWalker(element,
NodeFilter.SHOW_ELEMENT,
acceptNode: (node) => return node.matches(selector) && NodeFilter.FILTER_ACCEPT
);
return walker.nextNode();
const rootDiv = document.getElementById('test');
console.log(querySelector(rootDiv, 'div>div').innerHTML);
function querySelector(element, selector)
const walker = document.createTreeWalker(element,
NodeFilter.SHOW_ELEMENT,
acceptNode: (node) => node.matches(selector) && NodeFilter.FILTER_ACCEPT
);
return walker.nextNode();
;
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
With the big difference that this implementation doesn't support the special :scope
selector.
You may think it's the same going from the document or going from the root element, but not only will it make a difference in terms of performances, it will also allow for using this method while the element is not appended to any document.
const div = document.createElement('div');
div.insertAdjacentHTML('beforeend', '<div id="test"><div class="bar"></div></div>')
console.log(div.querySelector('div>.bar')); // found
console.log(document.querySelector('div>.bar')); // null
In the same way, matching elements in the Shadow-DOM would not be possible if we only had Document.querySelector.
Goood explanaition - especially the bit about Nodes not part of the document. This represents more clearly what the browser engine actually does.
– Falco
11 hours ago
add a comment |
The query selector div > div > div
only means:
Find a div which has a parent and a granparent which are both also a div.
And if you start with the first child of test and check the selector, it is true. And this is the reason why only your last query selects the innermost div, since it has the first predicate (find a div with a great-great-grandparent-div) which is not fulfilled by the first child of test.
The query-selector will only test descendants, but it will evaluate the expression in scope of the whole document. Just imagine a selector like checking properties of an element - even if you only view the child element, it is still the child of its parent.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55568211%2fweird-behaviour-when-using-queryselector%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
3 Answers
3
active
oldest
votes
3 Answers
3
active
oldest
votes
active
oldest
votes
active
oldest
votes
What querySelector
does is it finds an element somewhere in the document that matches the CSS selector passed, and then checks that the found element is a descendant of the element you called querySelector
on. It doesn't start at the element it was called on and search downwards - rather, it always starts at the document level, looks for elements that match the selector, and checks that the element is also a descendant of the calling context element. It's a bit unintuitive.
So:
someElement.querySelector(selectorStr)
is like
[...document.querySelectorAll(selectorStr)]
.find(elm => someElement.contains(elm));
A possible solution is to use :scope
to indicate that you want the selection to start at the rootDiv
, rather than at document
:
const rootDiv = document.getElementById('test');
alert(rootDiv.querySelector(':scope > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div > div').innerHTML);
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
:scope
is supported in all modern browsers but Edge.
Neat trick! Thanks!
– Tico
17 hours ago
:scope
is not supported by all but most of the major browsers. Anyways, thank for explanation and this should be the answer. Thanks.
– Joey Chong
17 hours ago
:scope
doesn't work with IE and Edge, I think it's important to point this out
– Cristian Traìna
17 hours ago
Here is the browser compatibility table - we have to resist just few months more since Edge is implementing Chromium engine :P
– Cristian Traìna
17 hours ago
Do you have any sources for the browser engine actually working this way ? - The query would have the same result, if the engine would start with the first div and check if it matches the query selector...
– Falco
14 hours ago
add a comment |
What querySelector
does is it finds an element somewhere in the document that matches the CSS selector passed, and then checks that the found element is a descendant of the element you called querySelector
on. It doesn't start at the element it was called on and search downwards - rather, it always starts at the document level, looks for elements that match the selector, and checks that the element is also a descendant of the calling context element. It's a bit unintuitive.
So:
someElement.querySelector(selectorStr)
is like
[...document.querySelectorAll(selectorStr)]
.find(elm => someElement.contains(elm));
A possible solution is to use :scope
to indicate that you want the selection to start at the rootDiv
, rather than at document
:
const rootDiv = document.getElementById('test');
alert(rootDiv.querySelector(':scope > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div > div').innerHTML);
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
:scope
is supported in all modern browsers but Edge.
Neat trick! Thanks!
– Tico
17 hours ago
:scope
is not supported by all but most of the major browsers. Anyways, thank for explanation and this should be the answer. Thanks.
– Joey Chong
17 hours ago
:scope
doesn't work with IE and Edge, I think it's important to point this out
– Cristian Traìna
17 hours ago
Here is the browser compatibility table - we have to resist just few months more since Edge is implementing Chromium engine :P
– Cristian Traìna
17 hours ago
Do you have any sources for the browser engine actually working this way ? - The query would have the same result, if the engine would start with the first div and check if it matches the query selector...
– Falco
14 hours ago
add a comment |
What querySelector
does is it finds an element somewhere in the document that matches the CSS selector passed, and then checks that the found element is a descendant of the element you called querySelector
on. It doesn't start at the element it was called on and search downwards - rather, it always starts at the document level, looks for elements that match the selector, and checks that the element is also a descendant of the calling context element. It's a bit unintuitive.
So:
someElement.querySelector(selectorStr)
is like
[...document.querySelectorAll(selectorStr)]
.find(elm => someElement.contains(elm));
A possible solution is to use :scope
to indicate that you want the selection to start at the rootDiv
, rather than at document
:
const rootDiv = document.getElementById('test');
alert(rootDiv.querySelector(':scope > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div > div').innerHTML);
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
:scope
is supported in all modern browsers but Edge.
What querySelector
does is it finds an element somewhere in the document that matches the CSS selector passed, and then checks that the found element is a descendant of the element you called querySelector
on. It doesn't start at the element it was called on and search downwards - rather, it always starts at the document level, looks for elements that match the selector, and checks that the element is also a descendant of the calling context element. It's a bit unintuitive.
So:
someElement.querySelector(selectorStr)
is like
[...document.querySelectorAll(selectorStr)]
.find(elm => someElement.contains(elm));
A possible solution is to use :scope
to indicate that you want the selection to start at the rootDiv
, rather than at document
:
const rootDiv = document.getElementById('test');
alert(rootDiv.querySelector(':scope > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div > div').innerHTML);
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
:scope
is supported in all modern browsers but Edge.
const rootDiv = document.getElementById('test');
alert(rootDiv.querySelector(':scope > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div > div').innerHTML);
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
const rootDiv = document.getElementById('test');
alert(rootDiv.querySelector(':scope > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div').innerHTML);
alert(rootDiv.querySelector(':scope > div > div > div').innerHTML);
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
edited 17 hours ago
answered 17 hours ago
CertainPerformanceCertainPerformance
98.3k165989
98.3k165989
Neat trick! Thanks!
– Tico
17 hours ago
:scope
is not supported by all but most of the major browsers. Anyways, thank for explanation and this should be the answer. Thanks.
– Joey Chong
17 hours ago
:scope
doesn't work with IE and Edge, I think it's important to point this out
– Cristian Traìna
17 hours ago
Here is the browser compatibility table - we have to resist just few months more since Edge is implementing Chromium engine :P
– Cristian Traìna
17 hours ago
Do you have any sources for the browser engine actually working this way ? - The query would have the same result, if the engine would start with the first div and check if it matches the query selector...
– Falco
14 hours ago
add a comment |
Neat trick! Thanks!
– Tico
17 hours ago
:scope
is not supported by all but most of the major browsers. Anyways, thank for explanation and this should be the answer. Thanks.
– Joey Chong
17 hours ago
:scope
doesn't work with IE and Edge, I think it's important to point this out
– Cristian Traìna
17 hours ago
Here is the browser compatibility table - we have to resist just few months more since Edge is implementing Chromium engine :P
– Cristian Traìna
17 hours ago
Do you have any sources for the browser engine actually working this way ? - The query would have the same result, if the engine would start with the first div and check if it matches the query selector...
– Falco
14 hours ago
Neat trick! Thanks!
– Tico
17 hours ago
Neat trick! Thanks!
– Tico
17 hours ago
:scope
is not supported by all but most of the major browsers. Anyways, thank for explanation and this should be the answer. Thanks.– Joey Chong
17 hours ago
:scope
is not supported by all but most of the major browsers. Anyways, thank for explanation and this should be the answer. Thanks.– Joey Chong
17 hours ago
:scope
doesn't work with IE and Edge, I think it's important to point this out– Cristian Traìna
17 hours ago
:scope
doesn't work with IE and Edge, I think it's important to point this out– Cristian Traìna
17 hours ago
Here is the browser compatibility table - we have to resist just few months more since Edge is implementing Chromium engine :P
– Cristian Traìna
17 hours ago
Here is the browser compatibility table - we have to resist just few months more since Edge is implementing Chromium engine :P
– Cristian Traìna
17 hours ago
Do you have any sources for the browser engine actually working this way ? - The query would have the same result, if the engine would start with the first div and check if it matches the query selector...
– Falco
14 hours ago
Do you have any sources for the browser engine actually working this way ? - The query would have the same result, if the engine would start with the first div and check if it matches the query selector...
– Falco
14 hours ago
add a comment |
The currently accepted answer somehow provides a valid logical explanation as to what happens, but they are factually wrong.
Element.querySelector
triggers the match a selector against tree algorithm, which goes from the root element and checks if its descendants do match the selector.
The selector itself is absolute, it doesn't have any knowledge of a Document and doesn't even require that your Element be appended to any. And apart from the :scope
attribute, it doesn't either care with which root you called the querySelector
method.
If we wanted to rewrite it ourselves, it would be more like
const walker = document.createTreeWalker(element,
NodeFilter.SHOW_ELEMENT,
acceptNode: (node) => return node.matches(selector) && NodeFilter.FILTER_ACCEPT
);
return walker.nextNode();
const rootDiv = document.getElementById('test');
console.log(querySelector(rootDiv, 'div>div').innerHTML);
function querySelector(element, selector)
const walker = document.createTreeWalker(element,
NodeFilter.SHOW_ELEMENT,
acceptNode: (node) => node.matches(selector) && NodeFilter.FILTER_ACCEPT
);
return walker.nextNode();
;
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
With the big difference that this implementation doesn't support the special :scope
selector.
You may think it's the same going from the document or going from the root element, but not only will it make a difference in terms of performances, it will also allow for using this method while the element is not appended to any document.
const div = document.createElement('div');
div.insertAdjacentHTML('beforeend', '<div id="test"><div class="bar"></div></div>')
console.log(div.querySelector('div>.bar')); // found
console.log(document.querySelector('div>.bar')); // null
In the same way, matching elements in the Shadow-DOM would not be possible if we only had Document.querySelector.
Goood explanaition - especially the bit about Nodes not part of the document. This represents more clearly what the browser engine actually does.
– Falco
11 hours ago
add a comment |
The currently accepted answer somehow provides a valid logical explanation as to what happens, but they are factually wrong.
Element.querySelector
triggers the match a selector against tree algorithm, which goes from the root element and checks if its descendants do match the selector.
The selector itself is absolute, it doesn't have any knowledge of a Document and doesn't even require that your Element be appended to any. And apart from the :scope
attribute, it doesn't either care with which root you called the querySelector
method.
If we wanted to rewrite it ourselves, it would be more like
const walker = document.createTreeWalker(element,
NodeFilter.SHOW_ELEMENT,
acceptNode: (node) => return node.matches(selector) && NodeFilter.FILTER_ACCEPT
);
return walker.nextNode();
const rootDiv = document.getElementById('test');
console.log(querySelector(rootDiv, 'div>div').innerHTML);
function querySelector(element, selector)
const walker = document.createTreeWalker(element,
NodeFilter.SHOW_ELEMENT,
acceptNode: (node) => node.matches(selector) && NodeFilter.FILTER_ACCEPT
);
return walker.nextNode();
;
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
With the big difference that this implementation doesn't support the special :scope
selector.
You may think it's the same going from the document or going from the root element, but not only will it make a difference in terms of performances, it will also allow for using this method while the element is not appended to any document.
const div = document.createElement('div');
div.insertAdjacentHTML('beforeend', '<div id="test"><div class="bar"></div></div>')
console.log(div.querySelector('div>.bar')); // found
console.log(document.querySelector('div>.bar')); // null
In the same way, matching elements in the Shadow-DOM would not be possible if we only had Document.querySelector.
Goood explanaition - especially the bit about Nodes not part of the document. This represents more clearly what the browser engine actually does.
– Falco
11 hours ago
add a comment |
The currently accepted answer somehow provides a valid logical explanation as to what happens, but they are factually wrong.
Element.querySelector
triggers the match a selector against tree algorithm, which goes from the root element and checks if its descendants do match the selector.
The selector itself is absolute, it doesn't have any knowledge of a Document and doesn't even require that your Element be appended to any. And apart from the :scope
attribute, it doesn't either care with which root you called the querySelector
method.
If we wanted to rewrite it ourselves, it would be more like
const walker = document.createTreeWalker(element,
NodeFilter.SHOW_ELEMENT,
acceptNode: (node) => return node.matches(selector) && NodeFilter.FILTER_ACCEPT
);
return walker.nextNode();
const rootDiv = document.getElementById('test');
console.log(querySelector(rootDiv, 'div>div').innerHTML);
function querySelector(element, selector)
const walker = document.createTreeWalker(element,
NodeFilter.SHOW_ELEMENT,
acceptNode: (node) => node.matches(selector) && NodeFilter.FILTER_ACCEPT
);
return walker.nextNode();
;
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
With the big difference that this implementation doesn't support the special :scope
selector.
You may think it's the same going from the document or going from the root element, but not only will it make a difference in terms of performances, it will also allow for using this method while the element is not appended to any document.
const div = document.createElement('div');
div.insertAdjacentHTML('beforeend', '<div id="test"><div class="bar"></div></div>')
console.log(div.querySelector('div>.bar')); // found
console.log(document.querySelector('div>.bar')); // null
In the same way, matching elements in the Shadow-DOM would not be possible if we only had Document.querySelector.
The currently accepted answer somehow provides a valid logical explanation as to what happens, but they are factually wrong.
Element.querySelector
triggers the match a selector against tree algorithm, which goes from the root element and checks if its descendants do match the selector.
The selector itself is absolute, it doesn't have any knowledge of a Document and doesn't even require that your Element be appended to any. And apart from the :scope
attribute, it doesn't either care with which root you called the querySelector
method.
If we wanted to rewrite it ourselves, it would be more like
const walker = document.createTreeWalker(element,
NodeFilter.SHOW_ELEMENT,
acceptNode: (node) => return node.matches(selector) && NodeFilter.FILTER_ACCEPT
);
return walker.nextNode();
const rootDiv = document.getElementById('test');
console.log(querySelector(rootDiv, 'div>div').innerHTML);
function querySelector(element, selector)
const walker = document.createTreeWalker(element,
NodeFilter.SHOW_ELEMENT,
acceptNode: (node) => node.matches(selector) && NodeFilter.FILTER_ACCEPT
);
return walker.nextNode();
;
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
With the big difference that this implementation doesn't support the special :scope
selector.
You may think it's the same going from the document or going from the root element, but not only will it make a difference in terms of performances, it will also allow for using this method while the element is not appended to any document.
const div = document.createElement('div');
div.insertAdjacentHTML('beforeend', '<div id="test"><div class="bar"></div></div>')
console.log(div.querySelector('div>.bar')); // found
console.log(document.querySelector('div>.bar')); // null
In the same way, matching elements in the Shadow-DOM would not be possible if we only had Document.querySelector.
const rootDiv = document.getElementById('test');
console.log(querySelector(rootDiv, 'div>div').innerHTML);
function querySelector(element, selector)
const walker = document.createTreeWalker(element,
NodeFilter.SHOW_ELEMENT,
acceptNode: (node) => node.matches(selector) && NodeFilter.FILTER_ACCEPT
);
return walker.nextNode();
;
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
const rootDiv = document.getElementById('test');
console.log(querySelector(rootDiv, 'div>div').innerHTML);
function querySelector(element, selector)
const walker = document.createTreeWalker(element,
NodeFilter.SHOW_ELEMENT,
acceptNode: (node) => node.matches(selector) && NodeFilter.FILTER_ACCEPT
);
return walker.nextNode();
;
<div>
<div>
<div id="test">
<div>
<div>
This is content
</div>
</div>
</div>
</div>
</div>
const div = document.createElement('div');
div.insertAdjacentHTML('beforeend', '<div id="test"><div class="bar"></div></div>')
console.log(div.querySelector('div>.bar')); // found
console.log(document.querySelector('div>.bar')); // null
const div = document.createElement('div');
div.insertAdjacentHTML('beforeend', '<div id="test"><div class="bar"></div></div>')
console.log(div.querySelector('div>.bar')); // found
console.log(document.querySelector('div>.bar')); // null
answered 12 hours ago
KaiidoKaiido
45.5k467108
45.5k467108
Goood explanaition - especially the bit about Nodes not part of the document. This represents more clearly what the browser engine actually does.
– Falco
11 hours ago
add a comment |
Goood explanaition - especially the bit about Nodes not part of the document. This represents more clearly what the browser engine actually does.
– Falco
11 hours ago
Goood explanaition - especially the bit about Nodes not part of the document. This represents more clearly what the browser engine actually does.
– Falco
11 hours ago
Goood explanaition - especially the bit about Nodes not part of the document. This represents more clearly what the browser engine actually does.
– Falco
11 hours ago
add a comment |
The query selector div > div > div
only means:
Find a div which has a parent and a granparent which are both also a div.
And if you start with the first child of test and check the selector, it is true. And this is the reason why only your last query selects the innermost div, since it has the first predicate (find a div with a great-great-grandparent-div) which is not fulfilled by the first child of test.
The query-selector will only test descendants, but it will evaluate the expression in scope of the whole document. Just imagine a selector like checking properties of an element - even if you only view the child element, it is still the child of its parent.
add a comment |
The query selector div > div > div
only means:
Find a div which has a parent and a granparent which are both also a div.
And if you start with the first child of test and check the selector, it is true. And this is the reason why only your last query selects the innermost div, since it has the first predicate (find a div with a great-great-grandparent-div) which is not fulfilled by the first child of test.
The query-selector will only test descendants, but it will evaluate the expression in scope of the whole document. Just imagine a selector like checking properties of an element - even if you only view the child element, it is still the child of its parent.
add a comment |
The query selector div > div > div
only means:
Find a div which has a parent and a granparent which are both also a div.
And if you start with the first child of test and check the selector, it is true. And this is the reason why only your last query selects the innermost div, since it has the first predicate (find a div with a great-great-grandparent-div) which is not fulfilled by the first child of test.
The query-selector will only test descendants, but it will evaluate the expression in scope of the whole document. Just imagine a selector like checking properties of an element - even if you only view the child element, it is still the child of its parent.
The query selector div > div > div
only means:
Find a div which has a parent and a granparent which are both also a div.
And if you start with the first child of test and check the selector, it is true. And this is the reason why only your last query selects the innermost div, since it has the first predicate (find a div with a great-great-grandparent-div) which is not fulfilled by the first child of test.
The query-selector will only test descendants, but it will evaluate the expression in scope of the whole document. Just imagine a selector like checking properties of an element - even if you only view the child element, it is still the child of its parent.
answered 13 hours ago
FalcoFalco
2,8521620
2,8521620
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f55568211%2fweird-behaviour-when-using-queryselector%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown