title: JavaScript高级程序设计第四版学习–第十五章
date:
author: Xilong88
tags: JavaScript
**本章内容:
理解Selectors API
使用HTML5 DOM扩展
可能出现的面试题:
1.如何通过CSS选择符参数获取节点?
2.关于元素遍历的题目(5种新增的方法)
3.如何获取CSS类名的值中的某一个
4.innerHTML和outerHTML有啥区别?
5.innerText和outerText有啥区别?
6.性能关于innerHTML和outerHTML
**
总结:
这一章也是讲一些DOM的属性和方法。
知识点:
1.querySelector() 和querySelectorAll()
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() 方法时,则只会从当前元素的后代中查询。
querySelectorAll()
querySelectorAll() 方法跟querySelector() 一样,也接收一个用于查询的参数,但它会返回所有匹配的节点,而不止一个。这个方法返回的是一个NodeList 的静态实例。
matches()
如果元素匹配则该选择符返回true ,否则返回false
if (document.body.matches("body.page1")){
// true
}
2.元素遍历
Element Traversal API为DOM元素添加了5个属性:
childElementCount ,返回子元素数量(不包含文本节点和注
释);
firstElementChild ,指向第一个Element 类型的子元素
(Element 版firstChild );
lastElementChild ,指向最后一个Element 类型的子元素
(Element 版lastChild );
previousElementSibling ,指向前一个Element 类型的同胞元
素(Element 版previousSibling );
nextElementSibling ,指向后一个Element 类型的同胞元素
(Element 版nextSibling )
过去要以跨浏览器方式遍历特定元素的所有子元素,代码大致是这样写的:
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属性之后,以上代码可以简化如下:
let parentElement = document.getElementById('parent');
let currentChildElement = parentElement.firstElementChild;
// 没有子元素,firstElementChild返回null,跳过循环
while (currentChildElement) {
// 这就是元素节点,做相应处理
processChild(currentChildElement);
if (currentChildElement === parentElement.lastElementChild) {
break;
}
currentChildElement = currentChildElement.nextElementSibling;
}
3.CSS类扩展
getElementsByClassName()
接收一个参数,即包含一个或多个类名的字符串,返回类名中包含相应类的元素的NodeList
// 取得所有类名中包含"username"和"current"元素
// 这两个类名的顺序无关紧要
let allCurrentUsernames = document.getElementsByClassName("username current");
// 取得ID为"myDiv"的元素子树中所有包含"selected"类的元素
let selected = document.getElementById("myDiv").getElementsByClassName("selected");
这个方法只会返回以调用它的对象为根元素的子树中所有匹配的元素
classList 属性
相当于把class里面的每一项拆开成为了一个集合类型
add(value ) ,向类名列表中添加指定的字符串值value
如果这个值已经存在,则什么也不做。
contains(value ) ,返回布尔值,表示给定的value 是否存在
remove(value ) ,从类名列表中删除指定的字符串值value
toggle(value ) ,如果类名列表中已经存在指定的value
则删除;如果不存在,则添加
// 删除"disabled"类
div.classList.remove("disabled");
// 添加"current"类
div.classList.add("current");
// 切换"user"类
div.classList.toggle("user");
// 检测类名
if (div.classList.contains("bd") && !div.classList.contains("disabled")){
// 执行操作
)
// 迭代类名
for (let class of div.classList){
doStuff(class);
}
焦点管理
首先是document.activeElement ,始终包含当前拥有焦点的DOM元素。
默认情况下,document.activeElement 在页面刚加载完之后会设置为document.body 。而在页面完全加载之前,document.activeElement 的值为null 。
document.hasFocus() 方法,该方法返回布尔值,表示文档是否拥有焦点
4.HTMLDocument 扩展
readyState 属性
loading ,表示文档正在加载;
complete ,表示文档加载完成。
if (document.readyState == "complete"){
// 执行操作
}
compatMode 属性
标准模式下document.compatMode 的值是"CSS1Compat" ,而在混杂模式下,document.compatMode 的值是"BackCompat" :
if (document.compatMode == "CSS1Compat"){
console.log("Standards mode");
} else {
console.log("Quirks mode");
}
head 属性
作为对document.body (指向文档的 元素)的补充,HTML5增加了document.head 属性,指向文档的<head>
元素。可以像下面这样直接取得<head>
元素:
let head = document.head;
5.字符集属性
characterSet属性表示文档实际使用的字符集,也可以用来指定新字符集。这个属性的默认值是"UTF-16"
console.log(document.characterSet); // "UTF-16"
document.characterSet = "UTF-8";
6.自定义数据属性
HTML5允许给元素指定非标准的属性,但要使用前缀data- 以便告诉浏览器,这些属性既不包含与渲染有关的信息,也不包含元素的语义信
息。除了前缀,自定义属性对命名是没有限制的,data- 后面跟什么都可以。
通过dataset访问
7.插入标记
innerHTML 属性
在读取innerHTML 属性时,会返回元素所有后代的HTML字符串,包括元素、注释和文本节点。
如果赋值中不包含任何HTML标签,则直接生成一个文本节点
div.innerHTML = "Hello & welcome, <b>\"reader\"!</b>";
相当于
<div id="content">Hello & welcome, <b>"reader"!</b></div>
outerHTML属性
会返回调用它的元素(及所有后代元素)的HTML字符串。
insertAdjacentHTML() 和
insertAdjacentText()
接收两
个参数:要插入标记的位置和要插入的HTML或文本。第一个参数
必须是下列值中的一个:
"beforebegin" ,插入当前元素前面,作为前一个同胞节
点;
"afterbegin" ,插入当前元素内部,作为新的子节点或放在
第一个子节点前面;
"beforeend" ,插入当前元素内部,作为新的子节点或放在
最后一个子节点后面;
"afterend" ,插入当前元素后面,作为下一个同胞节点。
// 作为前一个同胞节点插入
element.insertAdjacentHTML("beforebegin", "<p>Hello world!</p>");
element.insertAdjacentText("beforebegin", "Hello world!");
// 作为第一个子节点插入
element.insertAdjacentHTML("afterbegin", "<p>Hello world!</p>");
element.insertAdjacentText("afterbegin", "Hello world!");
// 作为最后一个子节点插入
element.insertAdjacentHTML("beforeend", "<p>Hello world!</p>");
element.insertAdjacentText("beforeend", "Hello world!");
// 作为下一个同胞节点插入
element.insertAdjacentHTML("afterend", "<p>Hello world!</p>"); element.
insertAdjacentText("afterend", "Hello world!");
性能:
在使用innerHTML 、outerHTML 和insertAdjacentHTML() 之前,最好手动删除要被替换的元素上关联的事件处理程序和JavaScript对象。
还有,要减少innerHTML和outerHTML使用次数
for (let value of values){
ul.innerHTML += '<li>${value}</li>'; // 别这样做!
}
改为:
let itemsHtml = "";
for (let value of values){
itemsHtml += '<li>${value}</li>';
}
ul.innerHTML = itemsHtml;
还可以
ul.innerHTML = values.map(value => '<li>${value}</li>').join('');
跨站点脚本
尽管innerHTML 不会执行自己创建的
<script>
标签,但仍然向恶 意用户暴露了很大的攻击面,因为通过它可以毫不费力地创建元素并执行onclick 之类的属性。 如果页面中要使用用户提供的信息,则不建议使用innerHTML 。与 使用innerHTML获得的方便相比,防止XSS攻击更让人头疼。此时 一定要隔离要插入的数据,在插入页面前必须毫不犹豫地使用相关 的库对它们进行转义。
scrollIntoView()
这个方法的参数如下:
alignToTop 是一个布尔值。
true :窗口滚动后元素的顶部与视口顶部对齐。
false :窗口滚动后元素的底部与视口底部对齐。
scrollIntoViewOptions 是一个选项对象。
behavior :定义过渡动画,可取的值为"smooth" 和"auto"
,默认为"auto" 。
block :定义垂直方向的对齐,可取的值为"start"
、"center" 、"end" 和"nearest" ,默认为 "start" 。
inline :定义水平方向的对齐,可取的值为"start"
、"center" 、"end" 和"nearest" ,默认为 "nearest" 。
// 确保元素可见
document.forms[0].scrollIntoView();
1
// 同上
document.forms[0].scrollIntoView(true);
document.forms[0].scrollIntoView({block: 'start'});
// 尝试将元素平滑地滚入视口
document.forms[0].scrollIntoView({behavior: 'smooth', block: 'start'});
8.专有扩展
children 属性,表示包含元素的Element类型子节点
contains() 方法
contains() 方法应该在要搜索的祖先元素上调用,参数是待确定的目标节点
console.log(document.documentElement.contains(document.body)); // true
compareDocumentPosition() 方法
要模仿contains() 方法,就需要用到掩码
16(0x10)。compareDocumentPosition() 方法的结果可以通过按位与来确定参考节点是否包含传入的节点,比如:
let result = document.documentElement.compareDocumentPosition(document.body);
console.log(!!(result & 0x10));
插入标记
innerText 属性
<div id="content">
<p>This is a <strong>paragraph</strong> with a list following it.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
This is a paragraph with a list following it.
Item 1
Item 2
Item 3
设置innerText 会移除元素之前所有的后代节点,完全改变DOM子树。此外,设置innerText 也会编码出现在字符串中的HTML语法字符(小于号、大于号、引号及和号)。下面是一个例子:
div.innerText = "Hello & welcome, <b>\"reader\"!</b>";
执行之后的结果如下:
<div id="content">Hello & welcome, <b>"reader"!</b></div>
outerText 属性
outerText 不止会移除所有后代节点,而是会替换整个元素。比如:
div.outerText = "Hello world!";
这行代码的执行效果就相当于以下两行代码:
let text = document.createTextNode("Hello world!");
div.parentNode.replaceChild(text, div);
scrollIntoViewIfNeeded(alingCenter) 会在元素不可见的情
况下,将其滚动到窗口或包含窗口中,使其可见;如果已经在视口中可
见,则这个方法什么也不做。如果将可选的参数alingCenter 设置为true ,则浏览器会尝试将其放在视口中央。