11.1、选择符 API
Selectors API 是由 W3C 发起制定的一个标准,致力于让浏览器原生支持 CSS 查询。
11.1.1、querySelector() 方法
querySelector() 方法接受一个 CSS 选择符,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,返回 null 。
<div id="myDiv" class="bd" title="body text" lang="en" dir="ltr" my_special_attribute="hello!">测试</div>
<div id="myDiv2" class="bd" style="width:400px;height:400px;background-color:red;">测试2</div>
<script>
// 返回第一个匹配的元素
var div = document.querySelector('div');
// 取得 ID 为 "myDiv" 的元素
var myDiv = document.querySelector('#myDiv');
// 取得类为 "bd" 的第一个元素
var bd = document.querySelector('.bd');
console.log(div === myDiv); // true
console.log(div === bd); // true
</script>
注意:通过 document 调用 querySelector() 方法时,会在文档元素的范围内查找匹配的元素。而通过 Element 类型调用 querySelector() 方法时,只会在该元素后代元素的范围内查找匹配的元素。
11.1.2、querySelectorAll() 方法
querySelectorAll() 方法接受一个 CSS 选择符作为参数,返回所有匹配的元素,返回的是 NodeList 的实例。如果没找到则返回的 NodeList 为空。
具体来说,返回的值实际上是带有所有属性和方法的 NodeList,而其底层实现则类似与一组元素的快照,而非不断对文档进行搜索的动态查询。这样实例可以避免使用 NodeList 对象通常会引起的大多数性能问题。
// 取得某 <div> 中的所有 <em> 元素
var ems = document.getElementById("myDiv").querySelectorAll("em");
// 取得类为 "selected" 的所有元素
var selecteds = document.querySelectorAll(".selected");
// 取得所有 <p> 元素中的所有 <strong> 元素
var strong = document.querySelector("p strong");
11.1.3、matchesSelector() 方法
为 Element 类型新增的方法 matchesSelector() 方法,该方法接受一个 CSS 选择符,如果调用元素与该选择符匹配,则返回 true;否则,返回 false。
<div id="myDiv">测试</div>
<script>
// 取得 ID 为 "myDiv" 的元素
var myDiv = document.querySelector('#myDiv');
// 在 chrome 中进行测试
console.log(myDiv.webkitMatchesSelector('#myDiv')); // true
</script>
根据不同浏览器的兼容性考虑,可以编写一个包装函数:
function matchesSelector(element, selector) {
if (element.matchesSelector) {
return element.matchesSelector(selector);
} else if (element.msMatchesSelector) {
return element.msMatchesSelector(selector);
} else if (element.mozMatchesSelector) {
return element.mozMatchesSelector(selector);
} else if (element.webkitMatchesSelector) {
return element.webkitMatchesSelector(selector);
} else {
throw new Error("Not supported.");
}
}
11.2、元素遍历
注意:对于元素间的空格,IE9 及之前版本不会返回文本节点,而其他所有浏览器都会返回文本节点。
为方便元素遍历,Element Traversal API 为 DOM 添加了一下 5 个属性:
- childElementCount:返回子元素(不包括文本节点和注释)的个数
- firstElementChild:指向第一个子元素;
- lastElementChild:指向最后一个子元素;
- previousElementSibling:指向前一个同辈元素;
- nextElementSibling:指向后一个同辈元素
利用这些属性不必担心空白文本节点,从而可以更方便的查找 DOM 元素。
<ul id="item">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>
<script>
var item = document.querySelector('#item');
console.log(item.childNodes.length); // 7
console.log(item.childElementCount); // 3
</script>
11.3、HTML5
11.3.1、与类相关的扩展
1.getElementsByClassName() 方法
getElementsByClassName() 方法接受一个参数,即一个包含一或多个类名的字符串,返回带有指定类的所有元素的 NodeList。传入多个类名时,类名的先后顺序不重要。
// 取得所有类中包含 "username" 和 "current" 的元素,类名的先后顺序无所谓
var allCurrentUsernames = document.getElementsByClassName("username current");
2.classList 属性
在操作类名时,需要通过 className 属性添加、删除和替换类名。对于元素中有多个类名时,使用该方法替换、删除类名时比较麻烦。而 HTML5 为所有元素添加了 classList 属性,该属性是新集合类型 DOMTokenList 的实例,该实例有如下方法:
- add(value):将给定的字符串添加到列表中。如果已存在,则不添加。
- contains(value):表示列表中是否存在给定的值,如果存在则返回 true,否则返回 false。
- remove(value):从列表中删除给定的字符串。
- toggle(value):如果列表中已经存在给定的值,删除它;如果列表中没有给定的值,添加它。
<div id="cbm" class="ceshi cshi bm">测试3</div>
<script>
var cbm = document.getElementById('cbm');
console.log(cbm.classList); // DOMTokenList(3) ["ceshi", "cshi", "bm", value: "ceshi cshi bm"]
console.log(cbm.classList.toggle('bm'));
console.log(cbm.classList); // DOMTokenList(2) ["ceshi", "cshi", value: "ceshi cshi"]
</script>
11.3.2、焦点管理
元素获得焦点的方式:页面加载、用户输入(通常是通过按 Tab 键)和在代码中调用 focus() 方法。HTML5 添加了管理 DOM 焦点的功能,即 document.activeElement 属性,该属性始终会引用 DOM 中当前获得了焦点的元素。
document.hasFocus() 方法用于确定文档是否获得了焦点。
11.3.3、HTMLDocument 的变化
1.readyState 属性
readyState 属性在 HTML5 标准中,Document 的 readyState 属性有两个可能的值:
- loading,正在加载文档;
- complete,已经加载完文档。
使用 document.readyState 的最恰当方式,就是通过它来实现一个指示文档已经加载完成的指示器。
2.兼容模式
document 的 compatMode 属性告诉开发人员浏览器采用哪种渲染模式。
标准模式:document.compatMode == "CSS1Compat"
混杂模式:document.compatMode == "BackCompat"
console.log(document.compatMode); // chrom : "CSS1Compat"
3.head 属性
H5 中新增的 document.head 属性引用文档的 <head> 元素。
11.3.4、字符集属性
H5 中新增加的有关字符集的属性:
- charset:表示文档中实际使用的字符集
- defaultCharset:表示默认浏览器及操作系统的设置,当前文档默认的字符集类型应该是什么。
11.3.5、自定义数据属性
H5 规定可以为元素添加非标准的属性,但要添加前缀 data-,目的是为元素提供与渲染无关的信息,或者提供语义信息。
添加了自定义属性之后,可以通过 dataset 属性来访问自定义属性的值。dataset 属性也就是一个名值对儿映射。在这个映射中,每个 data-name 形式的属性都会有一个对应的属性,只不过属性名没有 data- 前缀(比如,自定义属性是 data-myname,那映射中对应的属性就是 myname)。
<div id="myDiv" data-appId="12345" data-myname="Nicholas">测试</div>
<script>
var myDiv = document.getElementById('myDiv');
console.log(myDiv.dataset.appid); //在 chrome 和 firefox 中小写有效,结果为 '12345'
console.log(myDiv.dataset.myname); // 'Nicholas'
</script>
11.3.6、插入标记
1.innerHTML 属性
在读模式下,innerHTML 属性返回与调用元素的所有子元素(包括元素、注释和文本节点)对应的 HTML 标记。
在写模式下,innerHTML 的值会被解析为 DOM 子树,替换调用元素原来的所有子节点。
<div id="container">
<p>This is a <strong>paragraph</strong> with a list following is.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
<script>
var container = document.getElementById('container');
console.log(container.innerHTML);
container.innerHTML = 'Hello World!';
console.log(container.innerHTML);
</script>
2.outerHTML 属性
在读模式下,outerHTML 返回调用它的元素及所有子节点的 HTML 标签。
在写模式下,outerHTML 会根据指定的 HTML 字符串创建新的 DOM 子树,然后用这个 DOM 子树 完全替换调用元素。
<div id="container">
<p>This is a <strong>paragraph</strong> with a list following is.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
<script>
var container = document.getElementById('container');
console.log(container.outerHTML);
container.outerHTML = "<p>strong boy.</p>"; //直接取代<div id="container"></div>
</script>
3.insertAdjacentHTML() 方法
插入标记方法 insertAdjacentHTML(),该方法接受两个参数:插入位置和要插入的 HTML 文本。第一个参数必须是下列值之一:
- "beforebegin",在当前元素之前插入一个紧邻的同辈元素;
- "afterend",在当前元素之后插入一个紧邻的同辈元素。
- "afterbegin",为当前元素添加一个子元素(位于子元素 DOM 中第一个位置);
- "beforeend",为当前元素添加一个子元素(位于子元素 DOM 中最后一个位置);
<div id="container">
<p>This is a <strong>paragraph</strong> with a list following is.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
<script>
// 作为前一个同辈元素插入
container.insertAdjacentHTML('beforebegin', '<p>this is a strong boy.</p>');
// 作为后一个同辈元素插入
container.insertAdjacentHTML('afterend', '<p>end</p>');
// 作为第一个子元素插入
container.insertAdjacentHTML('afterbegin', '<p>afterbegin</p>');
// 作为最后一个子元素插入
container.insertAdjacentHTML('beforeend', '<p>beforeend</p>');
</script>
4.内存与性能问题
在删除带有事件处理程序或引用了其他 JavaScript 对象子树时,就可能导致内存占用问题。
最好手工删除要被替换的元素的所有事件处理程序和的 JavaScript 对象属性。
11.3.7、scrollIntoView() 方法
scrollIntroView() 可以在所有 HTML 元素上调用,通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视口中。如果给这个方法传入 true 作为参数,或者不传入任何参数,那么窗口滚动之后会让调用元素的顶部与视口顶部尽可能平齐。如果传入 false 作为参数,调用元素会尽可能全部出现在视口中,(可能的话,调用元素的底部会与视口底部平齐。)
11.4、专有扩展
11.4.1、文档模式
略
11.4.2、children 属性
children 属性,该属性是 HTMLCollection 的实例,只包含元素中同样还是元素的子节点。除此之外,children 属性与 childNodes 没有什么区别,即在元素只包含元素子节点时,这两个属性的值相同。
11.4.3、contains() 方法
测试某个节点是不是另一个节点的后代,可以使用 contains() 方法。
调用 contains() 方法的应该是祖先节点,也就是搜索开始的节点,这个方法接受一个参数,即要检测的后代节点。如果被检测的节点是后代节点,该方法返回 true;否则,返回 false。
DOM Level 3 中的 compareDocumentPosition() 用于确定两个节点间的关系,返回一个表示该关系的位掩码。
console.log(document.documentElement.compareDocumentPosition(document.body)); // 4+16=20
11.4.4、插入文本
1.innerText 属性
innerText 属性可以操作元素中包含的所有文本内容,包括子文档树中的文本。
注意:设置 innerText 永远只会生成当前节点的一个子文本节点,而为了确保只生成一个子文本节点,就必须要对文本进行 HTML 编码。利用这一点,可以通过 innerText 属性过滤掉 HTML 标签。
2.outerText 属性
outerText 属性不只是替换调用它的元素的子节点,而是会替换整个元素(包括子节点)。
11.4.5、滚动
略
参考文献
[1]《JavaScript高级程序设计(第3版)》