这是我参与8月更文挑战的第6天,活动详情查看:8月更文挑战
JavaScript
库中最流行的一种能力就是根据 CSS
选择符的模式匹配 DOM
元素。Selectors API
是 W3C
推荐标准,规定了浏览器原生支持的 CSS
查询 API
。
支持这一特性的所有 JavaScript
库都会实现一个基本的 CSS
解析器,然后使用已有的 DOM
方法搜索文档并匹配目标节点。
先来看一下 W3C
定义的 Selectors API Level 2
:
partial interface Document {
Element? querySelector(DOMString selectors);
NodeList querySelectorAll(DOMString selectors);
Element? find(DOMString selectors, optional (Element or sequence<Node>)? refNodes);
NodeList findAll(DOMString selectors, optional (Element or sequence<Node>)? refNodes);
};
partial interface DocumentFragment {
Element? querySelector(DOMString selectors);
NodeList querySelectorAll(DOMString selectors);
Element? find(DOMString selectors, optional (Element or sequence<Node>)? refNodes);
NodeList findAll(DOMString selectors, optional (Element or sequence<Node>)? refNodes);
};
partial interface Element {
Element? querySelector(DOMString selectors);
NodeList querySelectorAll(DOMString selectors);
Element? find(DOMString selectors);
NodeList findAll(DOMString selectors);
boolean matches(DOMString selectors, optional (Element or sequence<Node>)? refNodes);
};
Selectors API Level 1
的核心是两个方法:querySelector()
和 querySelectorAll()
。在兼容浏览器中,Document
类型和 Element
类型的实例上都会暴露这两个方法。
Selectors API Level 2
规范在 Element
类型上新增了更多方法,比如 matches()
、 find()
和 findAll()
。不过,目前还没有浏览器实现或宣称实现 find()
和 findAll()
。
1. querySelector()
querySelector()
方法接收 CSS
选择符参数,返回匹配该模式的第一个后代元素,如果没有匹配项则返回 null
。下面是一些例子:
// 取得<body>元素
let body = document.querySelector("body");
// 取得 ID 为"myDiv"的元素
let myDiv = document.querySelector("#myDiv");
// 取得类名为"selected"的第一个元素
let selected = document.querySelector(".selected");
// 取得类名为"button"的图片
let img = document.body.querySelector("img.button");
在 Document
上使用 querySelector()
方法时,会从文档元素开始搜索;在 Element
上使用querySelector()
方法时,则只会从当前元素的后代
中查询。
用于查询模式的 CSS
选择符可繁可简,依需求而定。如果选择符有语法错误或碰到不支持的选择符,则 querySelector()
方法会抛出错误。
2. querySelectorAll()
querySelectorAll()
方法跟 querySelector()
一样,也接收一个用于查询的参数,但它会返回所有匹配的节点,而不止一个。这个方法返回的是一个 NodeList
的静态实例。
与 querySelector()
一样,querySelectorAll()
也可以在 Document
、DocumentFragment
和 Element
类型上使用。下面是几个例子:
// 取得 ID 为"myDiv"的<div>元素中的所有<em>元素
let ems = document.getElementById("myDiv").querySelectorAll("em");
// 取得所有类名中包含"selected"的元素
let selecteds = document.querySelectorAll(".selected");
// 取得所有是<p>元素子元素的<strong>元素
let strongs = document.querySelectorAll("p strong");
返回的 NodeList
对象可以通过 for-of
循环、item()
方法或中括号语法取得个别元素。比如:
let strongElements = document.querySelectorAll("p strong");
// 以下 3 个循环的效果一样
for (let strong of strongElements) {
strong.className = "important";
}
for (let i = 0; i < strongElements.length; ++i) {
strongElements.item(i).className = "important";
}
for (let i = 0; i < strongElements.length; ++i) {
strongElements[i].className = "important";
}
与 querySelector()
方法一样,如果选择符有语法错误或碰到不支持的选择符,则 querySelectorAll()
方法会抛出错误。
如果需要 querySelectorAll()
只是搜索 element 的后代中的元素,我们需要用到 :scope
伪类来符合预期。下面是一个例子
<div class="outer">
<div class="select">
<div class="inner">
</div>
</div>
</div>
var select = document.querySelector('.select');
var inner = select.querySelectorAll('.outer .inner');
var scope_inner = select.querySelectorAll(':scope .outer .inner');
inner.length; // 1
scope_inner.length; // 0
3. matches()
matches()
方法接收一个 CSS
选择符参数,如果元素匹配则该选择符返回 true
,否则返回 false
。例如:
if (document.body.matches("body.page1")){
// true
}
使用这个方法可以方便地检测某个元素会不会被 querySelector()
或 querySelectorAll()
方法返回。所有主流浏览器都支持 matches()
。Edge
、Chrome
、Firefox
、Safari
和 Opera
完全支持,IE9~11
及一些移动浏览器支持带前缀的方法。
引用
- [1] JavaScript高级程序设计(第4版)
- [2] MDN Web Docs