Selectors API

  • Selectors API(参见W3C网站上的Selectors APl Level 1)是W3C推荐标准,规定了浏览器原生支持的CSS查询API。
  • 支持这一特性的所有JavaScript库都会实现一个基本的CSS解析器,然后使用已有的DOM方法搜索文档并匹配目标节点。
  • Selectors APILevel1的核心是两个方法:queryselector()和 querySelectorAll()。在兼容浏览器中,Document类型和Element类型的实例上都会暴露这两个方法
  • Selectors API Level 2规范在Element类型上新增了更多方法,比如 matches ()、find()和findAll()。不过,目前还没有浏览器实现或宣称实现find()和findAll()。

querySelector()

  • 方法接收CSS选择符参数,返回匹配该模式的第一个后代元素,如果没有匹配项则返回null。
1
2
3
4
5
6
7
8
// 取得<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()方法时,则只会从当前元素的后代中查询。
  • 如果选择符有语法错误或碰到不支持的选择符,则querySelector( )方法会抛出错误。

querySelectorAll()

  • querySelectorAll()方法跟querySelector()一样,也接收一个用于查询的参数,但它会返回所有匹配的节点,而不止一个。这个方法返回的是一个NodeList的静态实例。
  • querySelectorAll()返回的 NodeList 实例一个属性和方法都不缺,但它是一个静态的“快照”,而非“实时”的查询。这样的底层实现避免了使用NodeList对象可能造成的性能问题。
    • 返回的NodeList对象可以通过for-of循环、item()方法或中括号语法取得个别元素。
    • 如果没有匹配项,则返回空的NodeList实例
  • querySelectorAll()也可以在Document、DocumentFragment和Element类型上使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 取得 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");
//=======================
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";
}

matches()

  • matches()方法(在规范草案中称为matchesSelector())接收一个CSS选择符参数,如果元素匹配则该选择符返回true,否则返回false。
  • 使用这个方法可以方便地检测某个元素会不会被querySelector()或 querySelectorAll()方法返回。
1
2
3
if (document.body.matches("body.page1")){ 
// true
}

元素遍历

  • IE9之前的版本不会把元素间的空格当成空白节点,而其他浏览器则会。这样就导致了childNodes和firstchild等属性上的差异。为了弥补这个差异,同时不影响DOM规范,W3C通过新的ElementTraversal规范定义了一组新属性。

  • Element Traversal API 为 DOM 元素添加了 5 个属性:

    • childElementCount:返回子元素数量(不包含文本节点和注释);
    • firstElementChild:指向第一个Element类型的子元素(Element版firstChild);
    • lastElementChild:指向最后一个 Element类型的子元素(Element版lastchild);
    • previousElementSibling:指向前一个Element类型的同胞元素(Element版previoussibling);
    • nextElementsibling:指向后一个Element类型的同胞元素(Element 版nextsibling )。
  • 以下是跨浏览器方式遍历特定元素的所有子元素的写法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    let parentElement = document.getElementById('parent'); 
    let currentChildNode = parentElement.firstChild;
    // 没有子元素,firstChild 返回 null,跳过循环
    while (currentChildNode) {
    if (currentChildNode.nodeType === 1) {
    // 如果有元素节点,则做相应处理
    processChild(currentChildNode);
    }
    if (currentChildNode === parentElement.lastChild) {
    break;
    }
    currentChildNode = currentChildNode.nextSibling;
    }
  • 使用Element Traversal属性之后,以上代码可以简化如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    let parentElement = document.getElementById('parent'); 
    let currentChildElement = parentElement.firstElementChild;
    // 没有子元素,firstElementChild 返回 null,跳过循环
    while (currentChildElement) {
    // 这就是元素节点,做相应处理
    processChild(currentChildElement);
    if (currentChildElement === parentElement. lastElementChild) {
    break;
    }
    currentChildElement = currentChildElement.nextElementSibling;
    }