这篇文章发布于 2015年11月5日,星期四,23:31,归类于 JS实例。 阅读 75102 次, 今日 4 次 32 条评论
by zhangxinxu from http://www.zhangxinxu.com
本文地址:http://www.zhangxinxu.com/wordpress/?p=5036
一、时间紧急,废话少说
本文所在的页面藏匿了下面这些代码:
<img id="outside"> <div id="my-id"> <img id="inside"> <div class="lonely"></div> <div class="outer"> <div class="inner"></div> </div> </div>
就是下面这样的表现(为了便于观察,我加了边框背景色和文字):
首先说点大家都知道的热热身。
querySelector
和querySelectorAll
IE8+浏览器支持。querySelector
返回的是单个DOM元素;querySelectorAll
返回的是NodeList
.- 我们一般用的多的是
document.querySelectorAll
, 实际上,也支持dom.querySelectorAll
.例如:document.querySelector("#my-id").querySelectorAll("img")
选择的就是里面这个妹子。例如,我在控制台输出该选择
NodeList
的长度和id
,如下截图:
好了,上面都是众所周知的,好,下面开始展示点有意思的。
大家看下下面2行简单的查询语句:
document.querySelectorAll("#my-id div div");
document.querySelector("#my-id").querySelectorAll("div div");
提问:上面两个语句返回的NodeList
的内容是否是一样的?
给大家1分钟的时间思考下。
//zxx: 假设1分钟已经过去了
好了,答案是:不一样的。估计不少人跟我一样,会认为是一样的。
实际上:
document.querySelectorAll("#my-id div div").length === 1;
document.querySelector("#my-id").querySelectorAll("div div").length === 3;
大家如果有疑问,可以在控制台测试下,下图就是我自己测试的结果:
为啥会这样?
第一个符合我们的理解,不解释。那下一个语句,为何返回的NodeList
长度是3
呢?
首先,遍历该NodeList
会发现,查询的三个dom元素为:div.lonely
, div.outer
, div.inner
.
奇怪,奇怪,怎么会是3个呢?
jQuery中有个find()
方法,大家很可能受到这个方法影响,导致出现了一些认知的问题:
$("#my-id").find("div div").length === 1;
如果使用find
方法,则是1
个匹配;由于结构和作用类似,我们很自然疑问原生的querySelectorAll
也是这个套路。真是太错特错!!
要解释,为何NodeList
长度是3
,只要一句话就可以了,我特意加粗标红:
CSS选择器是独立于整个页面的!
什么意思呢?比如说你在页面很深的一个DOM里面写上:
<style> div div { } </style>
整个网页,包括父级,只要是满足div div
父子关系的元素,全部会被选中,对吧,这个大家应该都清楚的。
这里的querySelectorAll
里面的选择器也同样是这也全局特性。document.querySelector("#my-id").querySelectorAll("div div")
翻译成白话文就是:查询#my-id
的子元素,同时满足整个页面下div div
选择器条件的DOM元素们。
我们页面往上滚动看看原始的HTML结构,会发现,在全局视野下,div.lonely
, div.outer
, div.inner
全部都满足div div
这个选择器条件,于是,最终返回的长度为3
.
二、:scope与区域选择限制
其实,要想querySelectorAll
后面选择器不受全局影响,也是有办法的,就是使用目前还处于实验阶段的:scope
伪类,其作用就是让CSS是在某一范围内使用。此伪类在CSS中使用是大头,但是也可以在querySelectorAll
语句中使用:
document.querySelector("#my-id").querySelectorAll(":scope div div");
兼容性如下:
我写此文时候是15年11月初,目前基本上就FireFox浏览器支持,我估计,以后,会支持越来越多的。为什么呢?
因为Web Components需要它,可以实现真正独立封装,不会受外界影响的HTML组件。
关于:scope
目前支持尚浅,时机未到,我就没必要乱展开了,点到为止。
更新于2018-01-09
现在在看我2, 3年前说的上面这段话,发现脸打得啪啪响。:scope
伪类看来是支持越来越少了。
更新于2019-02-09
又反转了,:scope
伪类在CSS中虽然不支持,但是在querySelectorAll中是有效的。
三、结语还是要的
参考文章:querySelectorAll from an element probably doesn’t do what you think it does
感谢阅读,欢迎纠错,欢迎交流!
//众人: 你丫逗我呢,这也能叫结语……
本文为原创文章,包含脚本行为,会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,避免陈旧错误知识的误导,同时有更好的阅读体验。
本文地址:http://www.zhangxinxu.com/wordpress/?p=5036
(本篇完)
- 几个常见功能重合DOM API的细节差异 (0.457)
- jQuery诞生记-原理与机制 (0.244)
- 遐想:如果没有IE6和IE7浏览器... (0.236)
- 巧用:is()或:where()伪类让scoped的style依然全局匹配 (0.236)
- CSS @scope他来了 (0.236)
- CSS Nesting嵌套与@scope规则也太雷同了吧? (0.236)
- 近期手机网页项目一些杂碎心得分享 (0.213)
- 使用::part伪元素改变Shadow DOM的CSS样式 (0.118)
- 伪元素表单控件默认样式重置与自定义大全 (0.095)
- HTMLUnknownElement与HTML5自定义元素的故事 (0.095)
- 如何继承自定义元素及其他JS中扩展新方法 (RANDOM - 0.095)
又是一个知识点 谢谢大佬 虽然好像目前来说感觉好像用不上
:scope 特性已经被移除了
https://caniuse.com/#search=scope
感觉这个功能没有实际作用呀
没有谈到querySelector()的性能问题
我试了下dom.querySelectorAll 控制台报错呢,说dom undefined
试下querySelsct可以选中
Element.querySelectorAll(‘#/reg’),为什么这条语句会报错呢?
以前测试过这个问题,是在#my-id范围下的,包含#my-id本身。
document.querySelectorAll(“#my-id div div”) 这个没啥好解释的
document.querySelector(“#my-id”).querySelectorAll(“div div”)
1. 找出 #my-id 节点
2. 在#my-id节点上(包含#my-id 节点),找符合 div div 选择器的节点
$(‘#my-id’).find(‘div’).filter(‘div div’)
对~这个说法比较准确= = 我之前还在往外面包裹了一层div想看是不是真的会在全局下匹配div div结构的 = =
打卡3
Element.querySelectorAll() 不是在整个页面查找的。
https://developer.mozilla.org/zh-CN/docs/Web/API/Element/querySelectorAll
返回一个non-live的NodeList对象,这个对象将会包含调用querySelectorAll()方法的那个DOM对象的所有后代元素中匹配指定css选择器的元素们.
我也亲自做了实验,确实是这样的,你说的整个网页,包括父级,显示是错误的。
验证页面 http://139.129.27.90/demo/o1.html
博主的解释没错,指的是“查询#my-id的子元素,同时满足整个页面下div div选择器条件的DOM元素们。”
服了
定义document.querySelector(cssQuery) 范围为A
jquery中使用find()查找的范围 是<A的,这个符合认知
querySelectorAll()查找的范围是 <=A的,这个比较奇葩
console.log(document.querySelector(“#my-id”).querySelectorAll(“.m div”));
//[div#n, div.lonely, div.outer, div.inner]
由此证明不是全局范围下查找,而是相对于调用querySelectorAll的dom元素
但是:
console.log(document.querySelector(“#my-id”).querySelectorAll(“.m”));
// []
求解这是为什么,按照第一个console的逻辑来说,第二个console应该打印[div.m]啊
document.querySelector(“#my-id”).querySelectorAll(“div div”)翻译成白话文就是:查询#my-id的子元素,同时满足整个页面下div div选择器条件的DOM元素们
结果是3说明是基于整个页面的说法有误吧?
如果my-id外面套一层div那么结果应该是几呢?
像这样:
http://jsbin.com/meyiya/4/edit?html,js,console
结果是不是四呢?非也非也,是3.
结论是:通过DOM非document使用querySelectorAll 不是基于整个页面(可以理解为全局)的查找,而是基于当前的元素(从当前元素开始的向里面的局部)查找。
原文:“document.querySelector(“#my-id”).querySelectorAll(“div div”)翻译成白话文就是:查询#my-id的子元素,同时满足整个页面下div div选择器条件的DOM元素们。”
querySelectorAll是基于整个页面的,但是前面的“querySelector(“#my-id”)”相当于把基于页面的选择结果过滤出来,限定在了自己的子元素下。博主的解读没有问题。
我覺得应该是同级的,不知这样理解对不对
没有问题的,有句“同时满足”,就是说querySelectorAll是基于全局查找,但是前面的querySelector限定了范围,相当于求二者的交集
:scope选择器与scope属性不同,前者的支持度是很高的
https://developer.mozilla.org/en-US/docs/Web/CSS/%3Ascope
document.querySelector(‘selector0’).querySelector(‘selector1’)
document=document.querySelector(‘selector0’)//这个document的根是body
document.querySelector(‘selector1’)//这个document的根是selector0
我服了,html标签就是出不来,楼主能让人插插代码否。
作者说:document.querySelector(“#my-id”).querySelectorAll(“div div”)翻译成白话文就是:查询#my-id的子元素,同时满足整个页面下div div选择器条件的DOM元素们。
那么问题出来了,是这样吗?
看如下代码:
body下为如下结构:
父00
子001
父div的id为my-id
子“图片” id为inside
子div的 class为lonely
子div的 class为outer
孙divclass为inner
var km=document.querySelectorAll(“#my-id div div”);
var km2=document.querySelector(“#my-id”).querySelectorAll(“div div”);
var km3=document.getElementById(“my-id”).querySelectorAll(“div div”);
var km4=document.querySelectorAll(“div div”);
console.log(km,km2,km3);
console.log(km4)
结果是:
[div.inner] [div.lonely, div.outer, div.inner] [div.lonely, div.outer, div.inner]
[div, div.lonely, div.outer, div.inner]
也就是说#my-id下的div div关系,而不是整个页面的。否则km3和km4为什么不一样呢..00和001也是div div关系
请把我发的其余评论删除。。
涨姿势了,一直没留意,大概都是一直用精确的 class 名来选择子元素的缘故吧。
> 翻译成白话文就是:查询#my-id的子元素,同时满足整个页面下div div选择器条件的DOM元素们。
因为这两个选择器都挨在同一行,如果隔开很多,这样说的话就很纠结了,例如
var node = document.querySelector(“#my-id”);
// 隔了很多行代码
node.querySelectorAll(“div div”);
因为 css 匹配是自下而上(或者说自右而左),所以单独拿 node.querySelectorAll(“div div”); 这行来解释就好了。
1. 拿出 node 元素下所有子元素,处理对象是所有子元素,这时候就没有 node 这个元素本身的事了。
2. 拿出一个 div(处理选择器右边那个 div),再看看它的父元素是不是也是一个 div(处理选择器右边那个 div),如果是,匹配起来。
并非整个页面的全局查找
Web Components 默认 就是不影响 外界 样式的。
因为 组件里面的 style是在 shadow DOM 中的。
只有 在组件中写 特殊 选择器 :host 才会选到外面的。
同样 外面的不能选里面的 ,可以使用 /deep/ ^ ^^ 深选择器选择 shadow中的元素。
http://mantou132.github.io/test/shadow%20DOM.html
/deep/ :shadow 选择器 chrome提示 不标准,不知改成什么样了 还是取消了
改成>>>了……
见:http://www.w3.org/TR/2015/WD-shadow-dom-20151006/#h-composed-trees
/deep/ combinator, which was replaced with a >>> combinator (or shadow piercing descendant combinator)
太细节了,长姿势了。
期待更多关于scope的知识
https://dom.spec.whatwg.org/#element-collections
新规范有两个函数, element.query 和 element.queryAll, 就是基于 :scope 的.