第11章 DOM扩展
1. 选择符 API (Selectors API)
Selectors API Level 1 的核心是两个方法:querySelector()
和 querySelectorAll()
。在兼容的浏览器中,可以通过 Document
及 Element
类型的实例调用它们。
(1) querySelector()方法
- querySelector()方法接收一个 CSS 选择符,
返回与该模式匹配的第一个元素
,如果没有找到匹配的元素,返回 null。- 通过 Document 类型调用 querySelector()方法时,会在文档元素的范围内查找匹配的元素。而通过 Element 类型调用 querySelector()方法时,只会在该元素后代元素的范围内查找匹配的元素。
//取得 body 元素
var body = document.querySelector("body");
//取得 ID 为"myDiv"的元素
var myDiv = document.querySelector("#myDiv");
//取得类为"selected"的第一个元素
var selected = document.querySelector(".selected");
//取得类为"button"的第一个图像元素
var img = document.body.querySelector("img.button");
(2) querySelectorAll()方法
- querySelectorAll()方法接收的参数是一个 CSS 选择符,但
返回的是所有匹配的元素
而不仅仅是一个元素。这个方法返回的是一个NodeList 的实例
。- 只要传给 querySelectorAll()方法的 CSS 选择符有效,该方法都会返回一个 NodeList 对象, 而不管找到多少匹配的元素。如果没有找到匹配的元素,NodeList 就是空的。
- 够 调 用 querySelectorAll() 方 法 的 类 型 包 括
Document
、DocumentFragment
和Element
。
//取得某<div>中的所有<em>元素(类似于 getElementsByTagName("em"))
var ems = document.getElementById("myDiv").querySelectorAll("em");
//取得类为"selected"的所有元素
var selecteds = document.querySelectorAll(".selected");
//取得所有<p>元素中的所有<strong>元素
var strongs = document.querySelectorAll("p strong");
- 返回的 NodeList 中的每一个元素,可以使用
item()方法
,也可以使用方括号
语法
var i, len, strong;
for (i=0, len=strongs.length; i < len; i++){
strong = strongs[i]; //或者 strongs.item(i)
strong.className = "important";
}
(3) matchesSelector()方法
Selectors API Level 2规范为Element类型新增了一个方法matchesSelector()。这个方法接收 一个参数,即 CSS 选择符,
如果调用元素与该选择符匹配,返回 true;否则,返回 false
。
function matchesSelector(element, selector){
if (element.matchesSelector){
return element.matchesSelector(selector);
} else if (element.msMatchesSelector){
//IE 9+
return element.msMatchesSelector(selector);
} else if (element.mozMatchesSelector){
//Firefox 3.6+
return element.mozMatchesSelector(selector);
} else if (element.webkitMatchesSelector){
//Safari 5+和 Chrome
return element.webkitMatchesSelector(selector);
} else {
throw new Error("Not supported.");
}
}
if (matchesSelector(document.body, "body.page1")){
//执行操作
}
2. 元素遍历
Element Traversal API
为 DOM 元素添加了以下 5 个属性。
(1) childElementCount:返回子元素(不包括文本节点和注释)的个数。
(2) firstElementChild:指向第一个子元素;firstChild 的元素版。
(3) lastElementChild:指向最后一个子元素;lastChild 的元素版。
(4) previousElementSibling:指向前一个同辈元素;previousSibling 的元素版。
(5) nextElementSibling:指向后一个同辈元素;nextSibling 的元素版。
过去,要跨浏览器遍历某元素的所有子元素,需要像下面这样写代码。
var i, len,
child = element.firstChild;
while(child != element.lastChild){
if (child.nodeType == 1){ //检查是不是元素
processChild(child);
}
child = child.nextSibling;
}
var i, len,
child = element.firstElementChild;
while(child != element.lastElementChild){
processChild(child); //已知其是元素
child = child.nextElementSibling;
}
3. HTML 5
(1) 与类相关的扩充
* getElementsByClassName()方法
- 接收一个参数,即一个包含
一或多个类名的字符串
,返回带有 指定类的所有元素
的 NodeList。传入多个类名时,类名的先后顺序不重要。- 可以通过
document 对象
及所有HTML 元素
调用该方法。在 document 对象上调用 getElementsByClassName()始终会返回与类名匹配的所有元素,在元素上调用该方法就只会返回后 代元素中匹配的元素。
//取得所有类中包含"username"和"current"的元素,类名的先后顺序无所谓
var allCurrentUsernames =
document.getElementsByClassName("username current");
//取得 ID 为"myDiv"的元素中带有类名"selected"的所有元素
var selected =
document.getElementById("myDiv").getElementsByClassName("selected");
* classList 属性
在操作类名时,需要通过
className 属性
添加、删除和替换类名。因为 className 中是一个字 符串,所以即使只修改字符串一部分,也必须每次都设置整个字符串的值。
<div class="bd user disabled">...</div>
//删除"user"类 //首先,取得类名字符串并拆分成数组
var classNames = div.className.split(/\s+/);
//找到要删的类名
var pos = -1,i,len;
for (i=0, len=classNames.length; i < len; i++){
if (classNames[i] == "user"){
pos = i;
break; }
}
//删除类名
classNames.splice(i,1);
//把剩下的类名拼成字符串并重新设置
div.className = classNames.join(" ");
- HTML5 新增了一种
操作类名的方式
,可以让操作更简单也更安全,那就是为所有元素添加classList 属性
。- 这个 classList 属性是新集合类型 DOMTokenList 的实例,DOMTokenList 有一个表示自己包含多少元素的
length 属性
,而要取得每个元素可以使用item()方法
,也可以使用方括号
语法。
classList 属性方法
:
(1) add(value):将给定的字符串值添加到列表中。如果值已经存在,就不添加了。
(2) contains(value):表示列表中是否存在给定的值,如果存在则返回 true,否则返回 false。 2 remove(value):从列表中删除给定的字符串。
(3) toggle(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 (var i=0,len=div.classList.length; i < len; i++ ) {
doSomething(div.classList[i]);
}
(2) 焦点管理
document.activeElement 属性
- 这个 属性始终会引用 DOM 中当前获得了焦点的元素。元素获得焦点的方式有页面加载、用户输入(通常是 通过按 Tab 键)和在代码中调用 focus()方法。
var button = document.getElementById("myButton");
button.focus();
alert(document.activeElement === button); //true
- 默认情况下,文档刚刚加载完成时,document.activeElement 中保存的是 document.body 元 素的引用。文档加载期间,document.activeElement 的值为 null。
document.hasFocus()方法
:
这个方法用于确定文档是否获得了焦点。
var button = document.getElementById("myButton");
button.focus();
alert(document.hasFocus()); //true
(3) HTMLDocument的变化
*readyState 属性
Document 的 readyState 属性有两个可能的值:
(1) loading,正在加载文档;
(2) complete,已经加载完文档。
if (document.readyState == "complete"){
//执行操作
}
*兼容模式
compatMode 的属性
在标准模式下,document.compatMode 的 值等于"CSS1Compat",而在混杂模式下,document.compatMode 的值等于"BackCompat"。
*head 属性
HTML5 新增了
document.head 属性
, 引用文档的<head>元素。
var head = document.head || document.getElementsByTagName("head")[0];
(4) 字符集属性
-
charset属性
表示文档中实际使用的字符集, 也可以用来指定新字符集。默认情况下,这个属性的值为"UTF-16",但可以通过<meta>元素、响应头 部或直接设置 charset 属性修改这个值。
alert(document.charset); //"UTF-16"
document.charset = "UTF-8";
defaultCharset属性
,表示根据默认浏览器及操作系统的设置,当前文档默认的字符集应该是什么。
(5) 自定义数据属性
- HTML5 规定可以为元素添加非标准的属性,但要
添加前缀 data-
,目的是为元素提供与渲染无关的信息,或者提供语义信息
。
- 添加了自定义属性之后,可以通过元素的
dataset 属性
来访问自定义属性的值。
- dataset 属性的 值是
DOMStringMap
的一个实例,也就是一个名值对儿的映射
。在这个映射中,每个 data-name 形式 的属性都会有一个对应的属性,只不过属性名没有 data-前缀(比如,自定义属性是 data-myname, 那映射中对应的属性就是 myname)。
<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
var div = document.getElementById("myDiv");
//取得自定义属性的值
var appId = div.dataset.appId; var myName = div.dataset.myname;
//设置值
div.dataset.appId = 23456; div.dataset.myname = "Michael";
//有没有"myname"值呢?
if (div.dataset.myname){
alert("Hello, " + div.dataset.myname);
}
(6) 插入标记
* innerHTML 属性
1.在
读模式
下,innerHTML 属性返回与调用元素的所有子节点(包括元素、注释和文本节点)对应 的 HTML 标记。
- 在
写模式
下,innerHTML 会根据指定的值创建新的 DOM 树,然后用这个 DOM 树完全 替换调用元素原先的所有子节点。
(1)
innerHTML 属性在不同浏览器返回的文本格式会有所不同
。 IE 和 Opera 会将所有标签转换为大写形式,而 Safari、 Chrome 和 Firefox 则会原原本本地按照原先文档中(或指定这些标签时)的格式返回 HTML,包括空格 和缩进。不要指望所有浏览器返回的 innerHTML 值完全相同。
(2) 如果设置的值仅是文本而没有 HTML 标签,那么结果就是设置纯文本。
(3)为 innerHTML 设置的包含 HTML 的字符串值与解析后 innerHTML 的值大不相同。
div.innerHTML = "Hello & welcome, <b>\"reader\"!</b>";
<div id="content">Hello & welcome, <b>"reader"!</b></div>
(4) 不支持 innerHTML 的元素有:<col>、<colgroup>、 <frameset>、<head>、<html>、<style>、<table>、<tbody>、<thead>、<tfoot>和<tr>。此 外,在 IE8 及更早版本中,<title>元素也没有 innerHTML 属性。
IE8 为此提供了 window.toStaticHTML()方法
:
这个方法接收一个参数,即一个 HTML 字符串;返回 一个经过无害处理后的版本——从源 HTML 中删除所有脚本节点和事件处理程序属性。
var text = "<a href=\"#\" onclick=\"alert('hi')\">Click Me</a>";
var sanitized = window.toStaticHTML(text); //Internet Explorer 8 only
alert(sanitized); //"<a href=\"#\">Click Me</a>"
*outerHTML 属性
- 在读模式下,outerHTML 返回调用它的元素及所有子节点的 HTML 标签。
- 在写模式下,outerHTML 会根据指定的 HTML 字符串创建新的 DOM 子树,然后用这个 DOM 子树完全替换调用元素。
*insertAdjacentHTML()方法
接收两个参数:插入位置
和要插入的 HTML
文本。
第一个参数必须是下列值之一:
(1)
"beforebegin"
,在当前元素之前插入一个紧邻的同辈元素;
(2)"afterbegin"
,在当前元素之下插入一个新的子元素或在第一个子元素之前再插入新的子元素;
(3)"beforeend"
,在当前元素之下插入一个新的子元素或在最后一个子元素之后再插入新的子元素;
(4)"afterend"
,在当前元素之后插入一个紧邻的同辈元素。
* 性能问题
在删除带有事件处理程序或引用了其他 JavaScript 对象子树时,就有可能导致内存占用问题。假设 某个元素有一个事件处理程序(或者引用了一个 JavaScript 对象作为属性),在使用前述某个属性将该元 素从文档树中删除后,元素与事件处理程序(或 JavaScript 对象)之间的绑定关系在内存中并没有一并 删除。
(7) scrollIntoView()方法
scrollIntoView()可以在所有 HTML 元素上调用,通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视口中。如果给这个方法传入 true 作为参数,或者不传入任何参数,那么窗口滚动 之后会让调用元素的顶部与视口顶部尽可能平齐。如果传入 false 作为参数,调用元素会尽可能全部 出现在视口中,(可能的话,调用元素的底部会与视口顶部平齐。)不过顶部不一定平齐。
//让元素可见
document.forms[0].scrollIntoView();
4. 专有扩展
(1) 文档模式
页面的
文档模式
决定了可以使用什么功 能。换句话说,文档模式决定了你可以使用哪个级别的 CSS,可以在 JavaScript 中使用哪些 API,以及 如何对待文档类型(doctype)。
*4 种文档模式:
(1) IE5:以混杂模式渲染页面(IE5 的默认模式就是混杂模式)。IE8 及更高版本中的新功能都无法 使用。
(2) IE7:以 IE7 标准模式渲染页面。IE8 及更高版本中的新功能都无法使用。
(3) IE8:以 IE8 标准模式渲染页面。IE8 中的新功能都可以使用,因此可以使用 Selectors API、更多
CSS2 级选择符和某些 CSS3 功能,还有一些 HTML5 的功能。不过 IE9 中的新功能无法使用。
(4) IE9:以 IE9 标准模式渲染页面。IE9 中的新功能都可以使用,比如 ECMAScript 5、完整的 CSS3
以及更多 HTML5 功能。这个文档模式是最高级的模式。
* 要强制浏览器以某种模式渲染页面,可以使用 HTTP 头部信息 X-UA-Compatible,或通过等价的 <meta>标签来设置:
<meta http-equiv="X-UA-Compatible" content="IE=IEVersion">
* IE 的版本(IEVersion)
(1) Edge:始终以最新的文档模式来渲染页面。忽略文档类型声明。对于 IE8,始终保持以 IE8 标 准模式渲染页面。对于 IE9,则以 IE9 标准模式渲染页面。
(2) EmulateIE9:如果有文档类型声明,则以 IE9 标准模式渲染页面,否则将文档模式设置为 IE5。
(3) EmulateIE8:如果有文档类型声明,则以 IE8 标准模式渲染页面,否则将文档模式设置为 IE5。 4
(4) EmulateIE7:如果有文档类型声明,则以 IE7 标准模式渲染页面,否则将文档模式设置为 IE5。
(5) 9:强制以 IE9 标准模式渲染页面,忽略文档类型声明。
(6) 8:强制以 IE8 标准模式渲染页面,忽略文档类型声明。
(7) 7:强制以 IE7 标准模式渲染页面,忽略文档类型声明。
(8) 5:强制将文档模式设置为 IE5,忽略文档类型声明
*document.documentMode 属性
可以知道给定页面使用的是什么文档模式。
(2) children属性
这个属性是 HTMLCollection 的实例,
只包含元素中同样还是元素的子节点
。除此之外, children 属性与 childNodes 没有什么区别,即在元素只包含元素子节点时,这两个属性的值相同。
(3) contains()方法
- 调用 contains()方法的应该是祖先节点, 也就是搜索开始的节点,这个方法接收一个参数,即要检测的后代节点。如果被检测的节点是后代节点, 该方法返回 true;否则,返回 false。
- 支持 contains()方法的浏览器有 IE、Firefox 9+、Safari、Opera 和 Chrome。
alert(document.documentElement.contains(document.body)); //true
*DOM Level 3 compareDocumentPosition()
也能够确定节点间的关系。
位掩码的值
- 支持这个方法的浏 览器有 IE9+、Firefox、Safari、Opera 9.5+和 Chrome。
- 这个方法用于确定两个节点间的关系,
返回一个表示该关系的位掩码( bitmask)
。
var result = document.documentElement.compareDocumentPosition(document.body);
alert(!!(result & 16));
执行上面的代码后,结果会变成 20(表示“居后”的 4 加上表示“被包含”的 16)。对掩码 16 执 行按位操作会返回一个非零数值,而
两个逻辑非操作符会将该数值转换成布尔值
。
* 使用一些浏览器及能力检测:
function contains(refNode, otherNode){
if (typeof refNode.contains == "function" &&
(!client.engine.webkit || client.engine.webkit >= 522)){
return refNode.contains(otherNode);
} else if (typeof refNode.compareDocumentPosition == "function"){
return !!(refNode.compareDocumentPosition(otherNode) & 16);
} else {
var node = otherNode.parentNode;
do {
if (node === refNode){
return true;
} else {
node = node.parentNode;
}
} while (node !== null);
return false;
}
}
(4) 插入文本
*innerText 属性
通过 innertText 属性可以操作元素中包含的所有文本内容,包括子文档树中的文本。
(1) 在通过 innerText 读取值时,它会按照由浅入深的顺序,将子文档树中的所有文本拼接起来。
(2) 在通过 innerText 写入值时,结果会删除元素的所有子节点,插入包含相应文本值的文本节点。
- 设置 innerText 属性的同时,也对文本中存在的 HTML 语法字符(小于号、大于号、引号及和号)进行了编码。
div.innerText = "Hello & welcome, <b>\"reader\"!</b>";
//运行结果
<div id="content">Hello & welcome, <b>"reader"!</b></div>
- 设置 innerText 永远只会生成当前节点的
一个子文本节点
,而为了确保只生成一个子文本节点, 就必须要对文本进行 HTML 编码。
通过 innerText 属性过滤掉 HTML 标签
div.innerText = div.innerText;
textContent 属性:
DOM Level 3 规定的一个属性,其 他支持 textContent 属性的浏览器还有 IE9+、Safari 3+、Opera 10+和 Chrome。
了确保跨浏览器兼 容,有必要编写一个类似于下面的函数来检测可以使用哪个属性。
function getInnerText(element){
return (typeof element.textContent == "string") ?
element.textContent : element.innerText;
}
function setInnerText(element, text){
if (typeof element.textContent == "string"){
element.textContent = text;
} else {
element.innerText = text;
}
}
*outerText 属性
(1) 在读取文本值时,outerText 与 innerText 的结果完全一样。
(2) 但在写模式下,outerText 就完全不 同了:outerText 不只是替换调用它的元素的子节点,而是会替换整个元素(包括子节点)。
(5) 滚动
(1) scrollIntoViewIfNeeded(alignCenter):只在当前元素在视(2) 中不可见的情况下,才滚 动浏览器窗口或容器元素,最终让它可见。如果当前元素在视口中可见,这个方法什么也不做。 如果将可选的 alignCenter 参数设置为 true,则表示尽量将元素显示在视口中部(垂直方向)。 10 Safari 和 Chrome 实现了这个方法。
(3) scrollByLines(lineCount):将元素的内容滚动指定的行高,lineCount 值可以是正值, 也可以是负值。Safari 和 Chrome 实现了这个方法。
(4) scrollByPages(pageCount):将元素的内容滚动指定的页面高度,具体高度由元素的高度决 定。Safari 和 Chrome 实现了这个方法。
scrollIntoView()和scrollIntoViewIfNeeded()的作用对象是元素的 容器,而 scrollByLines()和 scrollByPages()影响的则是元素自身。
//将页面主体滚动 5 行
document.body.scrollByLines(5);
//在当前元素不可见的时候,让它进入浏览器的视口
document.images[0].scrollIntoViewIfNeeded();
//将页面主体往回滚动 1 页
document.body.scrollByPages(-1);
网友评论