js高级程序设计:DOM
DOM
DOM(文档对象模型)是针对 HTML 和 XML 文档的一个 API(应用程序编程接口)
节点层次
- 文档元素,文档的最外层元素,文档中的其他所有元素都包含在文档元素中。每个文档只能有一个文档元素。
- 在 HTML 页面中,文档元素始终都是元素。
- 在 XML 中,没有预定义的元素,因此任何元素都可能成为文档元素
- HTML中的标记可以通过节点来表示: HTML 元素通过元素节点表示,特性(attribute)通过特性节点表示,文档类型通过文档类型节点表示,而注释则通过注释节点表示
后面的内容在首页不显示,请点击下方的展开全文或者
Node类型
- 除了 IE 之外,在其他所有浏览器中都可以访问到Node 类型;
- 每个节点都有一个 nodeType 属性,用于表明节点的类型,共有12种,都有一个数字常量来表示
- 为了确保跨浏览器兼容,最好还是将 nodeType 属性与数字值进行比较
if (someNode.nodeType == 1){ //适用于所有浏览器
alert("Node is an element.");
}
- nodeName 和 nodeValue 属性
要了解节点的具体信息,可以使用 nodeName 和 nodeValue 这两个属性。这两个属性的值完全取决于节点的类型,最好先检测一下
if (someNode.nodeType == 1){
value = someNode.nodeName; //nodeName 的值是元素的标签名
}
- 节点关系,类似家谱
- 每个节点都有一个 childNodes 属性,其中保存着一个 NodeList 对象。NodeList 是一种类数组对象(注意不是Array的实例),用于保存一组有序的节点,可以通过位置来访问这些节点
- NodeList 对象的实际上是基于 DOM 结构动态执行查询的结果,因此 DOM 结构的变化能够自动反映在 NodeList 对象中
- 访问保存在NodeList中的节点可以通过角标或使用item()方法
- 注意 length 属性表示的是访问 NodeList 的那一刻,其中包含的节点数量
var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.childNodes.length;
+ ,对 NodeList 对象使用 Array.prototype.slice()方法可以将其转换为数组
//在 IE8 及之前版本中无效,因为那时NodeList实现为为一个 COM 对象
var arrayOfNodes = Array.prototype.slice.call(someNode.childNodes,0);
//在所有浏览器中都可以运行的版本
function convertToArray(nodes){
var array = null;
try {
array = Array.prototype.slice.call(nodes, 0); //针对非 IE 浏览器
} catch (ex) {
array = new Array();
for (var i=0, len=nodes.length; i < len; i++){
array.push(nodes[i]);
}
}
return array;
}
- 每个节点都有一个 parentNode 属性,该属性指向文档树中的父节点
- 通过使用在 childNodes 列表中每个节点的 previousSibling和 nextSibling 属性,可以访问同一列表中的其他节点,。列表中第一个节点的 previousSibling 属性值为 null,而列表中最后一个节点的 nextSibling 属性的值同样也为 null,可以通过这个特性判断是不是第一个或者最后一个孩子
- 父节点的 firstChild 和 lastChild属性分别指向其 childNodes 列表中的第一个和最后一个节点
- someNode.firstChild 的值始终等于 someNode.childNodes[0] , 而 someNode.lastChild 的值始终等于 someNode. childNodes [someNode.childNodes.length-1]。
- ownerDocument,该属性指向表示整个文档的顶端文档节点
- 操作节点
- appendChild(),用于向 childNodes 列表的末尾添加一个节点,返回值是新增的节点
- insertBefore()方法可以把节点放在 childNodes 列表中某个特定的位置上。返回值是被插入的节点,这个方法接受两个参数:要插入的节点和作为参照的节点。如果参照节点是null,则 insertBefore()与 appendChild()执行相同的操作
- replaceChild()方法替换节点(移除要替换的节点并且同时由要插入的节点占据其位置),返回要替换的节点,接受的两个参数是:要插入的节点和要替换的节点
- removeChild()移除节点,这个方法接受一个参数,即要移除的节点。被移除的节点将成为方法的返回值
- 注意:使用这几个方法必须先取得父节点(使用 parentNode 属性),在不支持子节点的节点上调用了这些方法会报错
- 其他方法
- 所有类型的节点都具备的方法
- cloneNode(),用于创建调用这个方法的节点的一个完全相同的副本,cloneNode()方法接受一个布尔值参数,表示是否执行深复制。
- 在参数为 true的情况下,执行深复制,也就是复制节点及其整个子节点树;
- 在参数为 false 的情况下,执行浅复制,即只复制节点本身。复制后返回的节点副本属于文档所有,但并没有为它指定父节点。因此,这个节点副本就成为了一个“孤儿”,除非通过 appendChild()、insertBefore()或 replaceChild()将它添加到文档
- cloneNode()方法不会复制添加到 DOM 节点中的 JavaScript 属性,例如事件处理程序等
- cloneNode(),用于创建调用这个方法的节点的一个完全相同的副本,cloneNode()方法接受一个布尔值参数,表示是否执行深复制。
- normalize()作用是处理文档树中的文本节点,找到了空文本节点,则删除它;如果找到相邻的文本节点,则将它们合并为一个文本节点
Document类型
- 在浏览器中,document 对象是 HTMLDocument(继承自 Document 类型)的一个实例,表示整个 HTML 页面,,document 对象是 window 对象的一个属性,因此可以将其作为全局对象来访问。通过这个文档对象,不仅可以取得与页面有关的信息,而且还能操作页面的外观及其底层结构
- Document 节点具有下列特征
- nodeType 的值为 9;
- nodeName 的值为"#document";
- nodeValue 的值为 null;
- parentNode 的值为 null;
- ownerDocument 的值为 null
- 其子节点可能是一个 DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或 Comment。
- 文档的子节点
- 两个内置的访问其子节点的快捷方式
- documentElement属性,该属性始终指向 HTML 页面中的元素,这个更快更直接
- 通过 childNodes 列表访问文档元素
- document 对象还有一个 body 属性,直接指向元素
var body = document.body; //取得对<body>的引用
- Document 另一个可能的子节点是 DocumentType,通常将<!DOCTYPE>标签看成一个与文档其他部分不同的实体,可以通过 doctype 属性(在浏览器中是 document.doctype)来访问它的信息,浏览器对 document.doctype 的支持差别很大,[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MoMSzDCN-1569921316126)(JavaScript高级程序设计_files/11.jpg)]
- 文档信息
- document 对象还有一些标准的 Document 对象所没有的属性
- title,包含着
元素中的文本——显示在浏览器窗口的标题栏或标签页上 - URL 属性中包含页面完整的 URL(即地址栏中显示的 URL)
- domain 属性中只包含页面的域名。可设置,但是有限制,只能设为URL中包含的域
- referrer属性中则保存着链接到当前页面的那个页面的 URL
- title,包含着
- 查找元素
- getElementById(),接收一个参数:要取得的元素的 ID,。如果找到相应的元素则返回该元素,如果不存在带有相应 ID 的元素,则返回 null
- 如果页面中多个元素的 ID 值相同,getElementById()只返回文档中第一次出现的元素
- 。IE7 及较低版本还为此方法添加了一个有意思的“怪癖”:name 特性与给定 ID 匹配的表单元素(、、及)也会被该方法返回
- getElementsByTagName(),接受一个参数,即要取得元素的标签名,而返回的是包含零或多个元素的 NodeList,。
- 在 HTML 文档中,这个方法会返回一个 HTMLCollection 对象,作为一个“动态”集合,可以使用方括号语法或 item()方法来访问 HTMLCollection 对象中的项。而这个对象中元素的数量则可以通过其 length 属性取得。
- HTMLCollection 对象还有一个方法,叫做 namedItem(),使用这个方法可以通过元素的 name特性取得集合中的项
- HTMLCollection 还支持按名称访问项,对命名的项也可以使用方括号语法来访问
- 对 HTMLCollection 而言,我们可以向方括号中传入数值或字符串形式的索引值。在后台,对数值索引就会调用 item(),而对字符串索引就会调用 namedItem()
- 在 HTML 文档中,这个方法会返回一个 HTMLCollection 对象,作为一个“动态”集合,可以使用方括号语法或 item()方法来访问 HTMLCollection 对象中的项。而这个对象中元素的数量则可以通过其 length 属性取得。
var images = document.getElementsByTagName("img");
var myImage = images.namedItem("myImage");
var myImage = images["myImage"];
- getElementsByName()返回带有给定 name 特性的所有元素HTMLCollectioin,一般用于取得单选按钮;为了确保发送给浏览器的值正确无误,所有单选按钮必须具有相同的 name 特性。
- 特殊集合
这些集合都是 HTMLCollection 对象,为访问文档常用的部分提供了快捷方式,包括:
- document.anchors,包含文档中所有带 name 特性的元素;
- document.applets,包含文档中所有的元素,因为不再推荐使用元素,所以这个集合已经不建议使用了;
- document.forms,包含文档中所有的元素,与 document.getElementsByTagName(“form”)得到的结果相同;
- document.images,包含文档中所有的元素,与 document.getElementsByTagName (“img”)得到的结果相同;
- document.links,包含文档中所有带 href 特性的元素。
- DOM一致性检测
document.implementation 属性可以检测浏览器实现了 DOM 的哪些部分,它规定了hasFeature()方法,这个方法接受两个参数:要检测的 DOM 功能的名称及版本号。如果浏览器支持给定名称和版本的功能,则该
方法返回 true,不过这个方法也有缺陷,返回 true有时候也不意味着实现与规范一致
var hasXmlDom = document.implementation.hasFeature("XML", "1.0");
- 文档写入
write()、writeln()、open()和 close()
- write()和 writeln()方法都接受一个字符串参数,即要写入到输出流中的文本。
- write()会原样写入
- writeln()则会在字符串的末尾添加一个换行符(\n)
- 注意参数中不能直接包含字符串"",需要加入转义字符\,因为这会导致该字符串被解释为脚本块的结束,它后面的代码将无法执行。
- 方法 open()和 close()分别用于打开和关闭网页的输出流。如果是在页面加载期间使用 write()或 writeln()方法,则不需要用到这两个方法。
Element类型
- Element 类型用于表现 XML 或 HTML元素,提供了对元素标签名、子节点及特性的访问,Element 节点具有以下特征
- nodeType 的值为 1;
- nodeName 的值为元素的标签名;
- nodeValue 的值为 null;
- parentNode 可能是 Document 或 Element;
- 其子节点可能是 Element、Text、Comment、ProcessingInstruction、CDATASection 或EntityReference。
- 要访问元素的标签名,可以使用 nodeName 属性,也可以使用 tagName 属性;这两个属性会返回相同的值,但是后者更清晰
- HTML元素
- 所有 HTML 元素都由 HTMLElement 类型表示,HTMLElement 类型直接继承自Element 并添加了一些属性:
- id,元素在文档中的唯一标识符。
- title,有关元素的附加说明信息,一般通过工具提示条显示出来
- lang,元素内容的语言代码,很少使用
- dir,语言的方向,值为"ltr"(left-to-right,从左至右)或"rtl"(right-to-left,从右至左),也很少使用。
- className,与元素的class 特性对应,即为元素指定的CSS类。没有将这个属性命名为class,是因为 class 是 ECMAScript 的保留字
var div = document.getElementById("myDiv");
alert(div.id);//myDiv
- 取得特性,推荐通过属性来取得和设置特性
操作特性的DOM 方法主要有三个,分别是 getAttribute()、setAttribute()和 removeAttribute()
- getAttribute()
//这样为 DOM 元素添加一个自定义的属性,该属性不会自动成为元素的特性
div.mycolor = "red";
alert(div.getAttribute("mycolor")); //null(IE 除外)
- setAttribute(),这个方法接受两个参数:要设置的特性名和值。如果特性已经存在,setAttribute()会以指定的值替换现有的值;如果特性不存在,setAttribute()则创建该属性并设置相应的值
- attributes 属性
- Element 类型是使用 attributes 属性的唯一一个 DOM 节点类型。attributes 属性中包含一个NamedNodeMap,是一个“动态”的集合。元素的每一个特性都由一个 Attr 节点表示,每个节点都保存在 NamedNodeMap 对象中。NamedNodeMap 对象拥有下列方法:
- getNamedItem(name):返回 nodeName 属性等于 name 的节点;
- removeNamedItem(name):从列表中移除 nodeName 属性等于 name 的节点;
- setNamedItem(node):向列表中添加节点,以节点的 nodeName 属性为索引;
- item(pos):返回位于数字 pos 位置处的节点。
- attributes 属性中包含一系列节点,每个节点的 nodeName 就是特性的名称,而节点的 nodeValue就是特性的值
//取得元素的 id 特性的两种写法
var id = element.attributes.getNamedItem("id").nodeValue;
var id = element.attributes["id"].nodeValue;
- 使用attributes 属性遍历元素的特性,
//迭代元素的每一个特性,然后将它们构造成 name="value" name="value"
function outputAttributes(element){
var pairs = new Array(),
attrName,
attrValue,
i,
len;
for (i=0, len=element.attributes.length; i < len; i++){
attrName = element.attributes[i].nodeName;
attrValue = element.attributes[i].nodeValue;
pairs.push(attrName + "=\"" + attrValue + "\"");
}
return pairs.join(" ");
}
- 创建元素
- document.createElement()方法可以创建新元素。这个方法只接受一个参数,即要创建元素的标签名
- 使用 createElement()方法创建新元素的同时,也为新元素设置了 ownerDocuemnt 属性。此时,还可以操作元素的特性,为它添加更多子节点,以及执行其他操作
- 新建的元素要添加到文档树中,可以使用 appendChild()、insertBefore()或 replaceChild()方法
- 在 IE7 及更早版本中动态创建元素的某些问题,这些问题可以通过在createElement()中指定完整的HTML标签来解决
- 不能设置动态创建的元素的 name 特性。
- 不能通过表单的 reset()方法重设动态创建的元素
- 动态创建的 type 特性值为"reset"的元素重设不了表单。
- 动态创建的一批 name 相同的单选按钮彼此毫无关系。name 值相同的一组单选按钮本来应该用于表示同一选项的不同值,但动态创建的一批这种单选按钮之间却没有这种关系。
//只在需要避开 IE 及更早版本中上述某个问题的情况下使用,别的浏览器不用
if (client.browser.ie && client.browser.ie <=7){
//创建一个带 name 特性的 iframe 元素
var iframe = document.createElement("<iframe name=\"myframe\"></iframe>");
//创建 input 元素
var input = document.createElement("<input type=\"checkbox\">");
//创建 button 元素
var button = document.createElement("<button type=\"reset\"></button>");
//创建单选按钮
var radio1 = document.createElement("<input type=\"radio\" name=\"choice\" "+
"value=\"1\">");
var radio2 = document.createElement("<input type=\"radio\" name=\"choice\" "+
"value=\"2\">");
}
- 元素的子节点
注意在IE和其他浏览器中对子节点的认定不一致,IE不识别文本节点作为子节点,如果文档里将元素间的空白符删除,那么所有浏览器都会返回相同数目的子节点。
Text类型
- 文本节点由 Text 类型表示,包含的是可以照字面解释的纯文本内容。纯文本中可以包含转义后的HTML 字符,但不能包含 HTML 代码
- Text 节点具有以下特征:
- nodeType 的值为 3;
- nodeName 的值为"#text";
- nodeValue 的值为节点所包含的文本;
- parentNode 是一个 Element;
- 不支持(没有)子节点。可以通过 nodeValue 属性或 data 属性访问 Text 节点中包含的文本,这两个属性中包含的值相同。对 nodeValue 的修改也会通过 data 反映出来,反之亦然。
- 使用下列方法可以操作节点中的文本。
- appendData(text):将 text 添加到节点的末尾。
- deleteData(offset, count):从 offset 指定的位置开始删除 count 个字符。
- insertData(offset, text):在 offset 指定的位置插入 text。
- replaceData(offset, count, text):用 text 替换从 offset 指定的位置开始到 offset+ count 为止处的文本。
- splitText(offset):从 offset 指定的位置将当前文本节点分成两个文本节点。
- substringData(offset, count):提取从 offset 指定的位置开始到 offset+count 为止处的字符串。
- 文本节点还有一个 length 属性,保存着节点中字符的数目。而且,nodeValue.length 和 data.length 中也保存着同样的值。
- 默认情况下,每个可以包含内容的元素最多只能有一个文本节点,而且必须确实有内容存在
<!-- 没有内容,也就没有文本节点 -->
<div></div>
<!-- 有空格,因而有一个文本节点 -->
<div> </div>
- 创建文本节点
- document.createTextNode()创建新文本节点,这个方法接受一个参数——要插入节点中的文本
- 创建新文本节点的同时,也会为其设置 ownerDocument 属性,新建的元素要添加到文档树中,可以使用 appendChild()、insertBefore()或 replaceChild()方法
var element = document.createElement("div");
element.className = "message";
var textNode = document.createTextNode("Hello world!");
element.appendChild(textNode);
document.body.appendChild(element);
- 规范化文本节点
normalize()能够将相邻文本节点合并。如果在一个包含两个或多个文本节点的父元素上调用 normalize()方法,则会将所有文本节点合并成一个节点,结果节点的 nodeValue 等于将合并前每个文本节点的 nodeValue 值拼接起来的值 - 分割文本节点
splitText()。这个方法会将一个文本节点分成两个文本节点,即按照指定的位置分割 nodeValue 值。原来的文本节点将包含从开始到指定位置之前的内容,新文本节点将包含剩下的文本。这个方法会返回一个新文本节点,该节点与原节点的parentNode 相同
Comment类型
- 注释在 DOM 中是通过 Comment 类型来表示的。Comment 节点具有下列特征:
- nodeType 的值为 8;
- nodeName 的值为"#comment";
- nodeValue 的值是注释的内容;
- parentNode 可能是 Document 或 Element;
- 不支持(没有)子节点
- Comment 类型与 Text 类型继承自相同的基类,因此它拥有除 splitText()之外的所有字符串操作方法。与 Text 类型相似,也可以通过 nodeValue 或 data 属性来取得注释的内容。
CDATASection类型,一看就不重要
- CDATASection 类型只针对基于 XML 的文档,表示的是 CDATA 区域。与 Comment 类似,CDATASection 类型继承自 Text 类型,因此拥有除 splitText()之外的所有字符串操作方法。
- CDATASection 节点具有下列特征:
- nodeType 的值为 4;
- nodeName 的值为"#cdata-section";
- nodeValue 的值是 CDATA 区域中的内容;
- parentNode 可能是 Document 或 Element;
- 不支持(没有)子节点。
DocumentType类型
- DocumentType 类型包含着与文档的 doctype 有关的所有信息,不常用
- 特征
- nodeType 的值为 10;
- nodeName 的值为 doctype 的名称;
- nodeValue 的值为 null;
- parentNode 是 Document;
- 不支持(没有)子节点。
DocumentFragment类型
- DOM 规定文档片段(document fragment)是一种“轻量级”的文档,可以包含和控制节点,但不会像完整的文档那样占用额外的资源。
- DocumentFragment 节点具有下列特征
- nodeType 的值为 11;
- nodeName 的值为"#document-fragment";
- nodeValue 的值为 null;
- parentNode 的值为 null;
- 子节点可以是 Element、ProcessingInstruction、Comment、Text、CDATASection 或EntityReference。
Attr类型,很少用
- 元素的特性在 DOM 中以 Attr 类型来表示,从技术角度讲,特性就是存在于元素的 attributes 属性中的节点。
- 特性节点具有下列特征:
- nodeType 的值为 2;
- nodeName 的值是特性的名称;
- nodeValue 的值是特性的值;
- parentNode 的值为 null;
- 在 HTML 中不支持(没有)子节点;
- 在 XML 中子节点可以是 Text 或 EntityReference。
- Attr 对象有 3 个属性:name、value 和 specified(布尔值)
DOM操作技术
动态脚本
- 动态脚本,指的是在页面加载时不存在,但将来的某一时刻通过修改 DOM 动态添加的脚本
- 创建方式:插入外部文件和直接插入 JavaScript 代码。
//第一种方式
function loadScript(url){
var script = document.createElement("script");
script.type = "text/javascript";
script.src = url;
document.body.appendChild(script);
}
//第二种方式
function loadScriptString(code){
var script = document.createElement("script");
script.type = "text/javascript";
try {
script.appendChild(document.createTextNode(code));
} catch (ex){
/* 这是为了兼容ie,IE对 <script>类节点,不允许访问其子节点 */
script.text = code;
}
document.body.appendChild(script);
}
动态样式
- 动态样式是指在页面刚加载时不存在的样式;动态样式是在页面加载完成后动态添加到页面中的
- 创建方式
- 利用dom操作:添加外部文件
//<link>元素添加到<head>元素里
function loadStyles(url){
var link = document.createElement("link");
link.rel = "stylesheet";
link.type = "text/css";
link.href = url;
var head = document.getElementsByTagName("head")[0];
head.appendChild(link);
}
- 通过dom操作:通过
function loadStyleString(css){
var style = document.createElement("style");
style.type = "text/css";
try{
style.appendChild(document.createTextNode(css));
} catch (ex){
/* 这是为了兼容ie,IE 将<style>视为一个特殊的、与<script>类似的节点,不允许访问其子节点
解决 IE 中这个问题的办法,就是访问元素的 styleSheet 属性,该属性又有一个 cssText 属性,可以接受CSS 代码*/
style.styleSheet.cssText = css;
}
var head = document.getElementsByTagName("head")[0];
head.appendChild(style);
}
操作表格
- 使用原始的DOM方法创建表格,要写很多行
- 为了方便,HTML DOM 还为、和元素添加了一些属性和方法如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gQ1oirBf-1569921316129)(JavaScript高级程序设计_files/12.jpg)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VRXA43HY-1569921316130)(JavaScript高级程序设计_files/13.jpg)]
//创建 table
var table = document.createElement("table");
table.border = 1;
table.width = "100%";
//创建 tbody
var tbody = document.createElement("tbody");
table.appendChild(tbody);
//创建第一行
tbody.insertRow(0);
tbody.rows[0].insertCell(0);
tbody.rows[0].cells[0].appendChild(document.createTextNode("Cell 1,1"));
tbody.rows[0].insertCell(1);
tbody.rows[0].cells[1].appendChild(document.createTextNode("Cell 2,1"));
//创建第二行
tbody.insertRow(1);
tbody.rows[1].insertCell(0);
tbody.rows[1].cells[0].appendChild(document.createTextNode("Cell 1,2"));
tbody.rows[1].insertCell(1);
tbody.rows[1].cells[1].appendChild(document.createTextNode("Cell 2,2"));
//将表格添加到文档主体中
document.body.appendChild(table);
使用NodeList
- NodeList ,NamedNodeMap 和 HTMLCollection这三个集合都是“动态的”,每当文档结构发生变化时,它们都会得到更新,所有 NodeList 对象都是在访问 DOM 文档时实时运行的查询
- 如果想要迭代一个 NodeList,最好是使用 length 属性初始化第二个变量,然后将迭代器与该变量进行比较
var divs = document.getElementsByTagName("div"),
i,
len,
div;
for (i=0, len=divs.length; i < len; i++){
div = document.createElement("div");
document.body.appendChild(div);
}
- 应该尽量减少访问 NodeList 的次数。因为每次访问 NodeList,都会运行一次基于文档的查询。所以,可以考虑将从 NodeList 中取得的值缓存起来。
DOM扩展
DOM 的两个主要的扩展是 Selectors API(选择符 API)和 HTML5
选择符API
Selectors API Level 1 的核心是两个方法:querySelector()和 querySelectorAll()。
querySelector()方法
- querySelector()方法接收一个 CSS 选择符,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,返回 null。
- 通过 Document 类型调用 querySelector()方法时,会在文档元素的范围内查找匹配的元素。而通过 Element 类型调用 querySelector()方法时,只会在该元素后代元素的范围内查找匹配的元素。
querySelectorAll()方法
- querySelectorAll()方法接收的参数与是一个 CSS 选择符,但返回的是一个 NodeList 的实例。
- 能够调用 querySelectorAll()方法的类型包括 Document、DocumentFragment 和 Element
- 要取得返回的 NodeList 中的每一个元素,可以使用 item()方法,也可以使用方括号语法
//取得所有<p>元素中的所有<strong>元素
var strongs = document.querySelectorAll("p strong");
var i, len, strong;
for (i=0, len=strongs.length; i < len; i++){
strong = strongs[i]; //或者 strongs.item(i)
strong.className = "important";
}
matchesSelector()方法,浏览器不实现该方法
元素遍历
- Element Traversal API 为 DOM 元素添加了以下 5 个属性。
- childElementCount:返回子元素(不包括文本节点和注释)的个数。
- firstElementChild:指向第一个子元素;firstChild 的元素版。
- lastElementChild:指向最后一个子元素;lastChild 的元素版。
- previousElementSibling:指向前一个同辈元素;previousSibling 的元素版。
- nextElementSibling:指向后一个同辈元素;nextSibling 的元素版。
- 示例
//跨浏览器遍历某元素的所有子元素
var i,
len,
child = element.firstChild;
while(child != element.lastChild){
if (child.nodeType == 1){ //检查是不是元素
processChild(child);
}
child = child.nextSibling;
}
//使用 Element Traversal 新增的元素
var i,
len,
child = element.firstElementChild;
while(child != element.lastElementChild){
processChild(child); //已知其是元素
child = child.nextElementSibling;
}
HTML5
与类相关的扩充
- getElementsByClassName()方法,接收一个参数,即一个包含一或多个类名的字符串,返回带有指定类的所有元素的 NodeList。
- 传入多个类名时,类名的先后顺序不重要。
- 在 document 对象上调用getElementsByClassName()始终会返回与类名匹配的所有元素,在元素上调用该方法就只会返回后代元素中匹配的元素。
- classList 属性,注意兼容问题
- 以往在操作类名时,需要通过 className 属性添加、删除和替换类名,都需要设置整个字符串的值。
- classList 属性是新集合类型 DOMTokenList 的实例,DOMTokenList 有一个表示自己包含多少元素的 length 属性,而要取得每个元素可以使用 item()方法,也可以使用方括号语法。此外,这个新类型还定义如下方法。
- add(value):将给定的字符串值添加到列表中。如果值已经存在,就不添加了。
- contains(value):表示列表中是否存在给定的值,如果存在则返回 true,否则返回 false。
- remove(value):从列表中删除给定的字符串。
- toggle(value):如果列表中已经存在给定的值,删除它;如果列表中没有给定的值,添加它
//删除"disabled"类
div.classList.remove("disabled");
//添加"current"类
div.classList.add("current");
//切换"user"类
div.classList.toggle("user");
焦点管理
- document.activeElement 属性,这个属性始终会引用 DOM 中当前获得了焦点的元素。元素获得焦点的方式有页面加载、用户输入(通常是通过按 Tab 键)和在代码中调用 focus()方法。默认情况下,文档刚刚加载完成时,document.activeElement 中保存的是 document.body 元素的引用。文档加载期间,document.activeElement 的值为 null。
- document.hasFocus()方法,这个方法用于确定文档是否获得了焦点。
HTMLDocument的变化
- Document 的 readyState 属性,通过它来实现一个指示文档已经加载完成的指示器,要实现这样一个指示器,必须借助 onload 事件处理程序设置一个标签,表明文档已经加载完毕,它有两个可能的值:
- loading,正在加载文档;
- complete,已经加载完文档。
if (document.readyState == "complete"){
//执行操作
}
- compatMode 的属性,这个属性说明浏览器采用了哪种渲染模式(标准还是混杂),在标准模式下,document.compatMode 的值等于"CSS1Compat",而在混杂模式下,document.compatMode 的值等于"BackCompat"。
- document.head 属性,引用文档的元素
字符集属性
- charset 属性表示文档中实际使用的字符集,也可以用来指定新字符集
- 是 defaultCharset,表示根据默认浏览器及操作系统的设置,当前文档默认的字符集应该是什么
自定义数据属性,兼容性差
- HTML5 规定可以为元素添加非标准的属性,但要添加前缀 data-,目的是为元素提供与渲染无关的信息,或者提供语义信息
- 添加了自定义属性之后,可以通过元素的 dataset 属性来访问自定义属性的值。dataset 属性的值是 DOMStringMap 的一个实例,也就是一个名值对儿的映射。在这个映射中,每个 data-name 形式的属性都会有一个对应的属性,只不过属性名没有 data-前缀
插入标记
- ,innerHTML 属性返回与调用元素的所有子节点(包括元素、注释和文本节点)对应的 HTML 标记
- 在写模式下,innerHTML 的值会被解析为 DOM 子树,替换调用元素原来的所有子节点。
- 限制:在大多数浏览器中,通过 innerHTML 插入
- outerHTML 返回调用它的元素及所有子节点的 HTML 标签。
//新创建的<p>元素会取代 DOM 树中的<div>元素。
div.outerHTML = "<p>This is a paragraph.</p>";
- insertAdjacentHTML()方法,它接收两个参数:插入位置和要插入的 HTML 文本,第一个参数必须是下列值之一:
- “beforebegin”,在当前元素之前插入一个紧邻的同辈元素;
- “afterbegin”,在当前元素之下插入一个新的子元素或在第一个子元素之前再插入新的子元素;
- “beforeend”,在当前元素之下插入一个新的子元素或在最后一个子元素之后再插入新的子元素;
- “afterend”,在当前元素之后插入一个紧邻的同辈元素。
- 内存与性能问题
- 使用本节介绍的方法替换子节点可能会导致浏览器的内存占用问题
- 提高效率,避免频繁使用innerHTML
//单独构建字符串,然后再一次性地将结果字符串赋值给 innerHTML
var itemsHtml = "";
for (var i=0, len=values.length; i < len; i++){
itemsHtml += "<li>" + values[i] + "</li>";
}
ul.innerHTML = itemsHtml;
scrollIntoView()方法
scrollIntoView()可以在所有 HTML 元素上调用,通过滚动浏览器窗口或某个容器元素,调用元素就可以出现在视口中。如果给这个方法传入 true 作为参数,或者不传入任何参数,那么窗口滚动之后会让调用元素的顶部与视口顶部尽可能平齐。如果传入 false 作为参数,调用元素会尽可能全部出现在视口中
专有扩展
文档模式
文档模式决定了你可以使用哪个级别的 CSS,可以在 JavaScript 中使用哪些 API,以及如何对待文档类型(doctype)
children属性
contains()方法
- contains()方法的应该是祖先节点,也就是搜索开始的节点,这个方法接收一个参数,即要检测的后代节点。如果被检测的节点是后代节点,该方法返回 true;否则,返回 false。
- compareDocumentPosition()也能够确定节点间的关系。返回一个表示该关系的位掩码[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0li1edKR-1569921316131)(JavaScript高级程序设计_files/5.jpg)],
插入文本
- innerText 属性,操作元素中包含的所有文本内容,包括子文档树中的文本,。在通过innerText 读取值时,它会按照由浅入深的顺序,将子文档树中的所有文本拼接起来。在通过innerText 写入值时,结果会删除元素的所有子节点,插入包含相应文本值的文本节点。
- 设置innerText属性的同时,也对文本中存在的 HTML 语法字符(小于号、大于号、引号及和号)进行了编码,并且设置 innerText 永远只会生成当前节点的一个子文本节点
div.innerText = "Hello & welcome, <b>\"reader\"!</b>";
//输出下面这行
<div id="content">Hello & welcome, <b>"reader"!</b></div>
//解决方法,过滤掉 HTML 标签
div.innerText = div.innerText;
- 注意。Firefox 虽然不支持innerText,但支持作用类似的 textContent 属性
- innerText 与 textContent 返回的内容并不完全一样。比如,innerText 会忽略行内的样式和脚本,而 textContent 则会像返回其他文本一样返回行内的样式和脚本代码。避免跨浏览器兼容问题的最佳途径,就是从不包含行内样式或行内脚本的 DOM 子树副本或 DOM 片段中读取文本。
//跨浏览器兼容
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 属性,跟innerText差不多,但在写模式下不同了:outerText 不只是替换调用它的元素的子节点,而是会替换整个元素(包括子节点),该属性尽量不要用
滚动,没什么用
DOM2,DOM3
DOM1 级主要定义的是 HTML 和 XML 文档的底层结构。DOM2 和 DOM3 级则在这个结构的基础上引入了更多交互能力,也支持了更高级的 XML 特性,分成了不同模块
- DOM2 级核心(DOM Level 2 Core):在 1 级核心基础上构建,为节点添加了更多方法和属性。
- DOM2 级视图(DOM Level 2 Views):为文档定义了基于样式信息的不同视图。
- DOM2 级事件(DOM Level 2 Events):说明了如何使用事件与 DOM 文档交互。
- DOM2 级样式(DOM Level 2 Style):定义了如何以编程方式来访问和改变 CSS 样式信息。
- DOM2 级遍历和范围(DOM Level 2 Traversal and Range):引入了遍历 DOM 文档和选择其特定部分的新接口。
- DOM2 级 HTML(DOM Level 2 HTML):在 1 级 HTML 基础上构建,添加了更多属性、方法和新接口。
DOM 变化
针对XML命名空间的变化,跟HTML无关
其他方面的变化,了解就好
- DocumentType 类型的变化,新增了 3 个属性:publicId、systemId 和 internalSubset。其中,前两个属性表示的是文档类型声明中的两个信息段
- Document 类型的变化中唯一与命名空间无关的方法是 importNode(),不常用。这个方法的用途是从一个文档中取得一个节点,然后将其导入到另一个文档,使其成为这个文档结构的一部分。它接受两个参数:要复制的节点和一个表示是否复制子节点的布尔值。返回的结果是原来节点的副本,但能够在当前文档中使用。
- defaultView 的属性,其中保存着一个指针,指向拥有给定文档的窗口(或框架),IE不支持, IE 中有一个等价的属性名叫 parentWindow()
- “DOM2级核心”还为 document.implementation 对象规定了两个新方法:createDocumentType()和 createDocument()。
- createDocumentType()用于创建一个新的 DocumentType节点,接受 3 个参数:文档类型名称、publicId、systemId。
- createDocument()创建新文档,这个方法接受 3 个参数:针对文档中元素的 namespaceURI、文档元素的标签名、新文档的文档类型
- “DOM2 级 HTML”模块也为 document.implementation 新增了一个方法,名叫 createHTMLDocument(),兼容性差,不推荐。这个方法的用途是创建一个完整的 HTML 文档,包括、、
和元素。这个方法只接受一个参数,即新创建文档的标题(放在<title>元素中的字符串),返回新的 HTML 文档 - isSupported()方法于确定当前节点具有什么能力,接受相同的两个参数:特性名和特性版本号,返回值是布尔值
- isSameNode()和 isEqualNode()。这两个方法都接受一个节点参数,并在传入节点与引用的节点相同或相等时返回 true
- setUserData()方法会将数据指定给节点,它接受 3 个参数:要设置的键、实际的数据(可以是任何数据类型)和处理函数
- 框架的变化:框架和内嵌框架分别用 HTMLFrameElement 和 HTMLIFrameElement 表示,它们在 DOM2级中都有了一个新属性,名叫 contentDocument。这个属性包含一个指针,指向表示框架内容的文档对象。IE8 之前不支持框架中的 contentDocument 属性,但支持一个名叫 contentWindow 的属性,该属性返回框架的 window 对象,而这个 window 对象又有一个 document 属性
样式
访问元素的样式
- style 对象是 CSSStyleDeclaration 的实例,包含着行内的样式,不包含外部样式。
- 多数情况下,都可以通过简单地转换属性名的格式来实现转换,如background-color转为backgroundColor。其中一个不能直接转换的 CSS 属性就是 float,因为这是保留字,一般浏览器用cssFloat,IE用styleFloat
- 设置属性时,最好始终都指定度量单位
- style 对象定义了一些属性和方法
- cssText:通过它能够读取或写入到 style 特性中的 CSS 代码。
myDiv.style.cssText = "width: 25px; height: 100px; background-color: green";
alert(myDiv.style.cssText);
- length:应用给元素的 CSS 属性的数量。其与 item()方法配套使用,以便迭代在元素中定义的 CSS 属性
var prop, value, i, len;
for (i=0, len=myDiv.style.length; i < len; i++){
prop = myDiv.style[i]; //或者 myDiv.style.item(i)
value = myDiv.style.getPropertyValue(prop);
alert(prop + " : " + value);
}
- parentRule:表示 CSS 信息的 CSSRule 对象。
- getPropertyCSSValue(propertyName):返回包含给定属性值的 CSSValue 对象,不常用。这两个属性分别是:cssText 和 cssValueType。其中,cssText 属性的值与 getPropertyValue()返回的值相同,而 cssValueType 属性则是一个数值常量,表示值的类型:0 表示继承的值,1 表示基本的值,2 表示值列表,3 表示自定义的值
- getPropertyPriority(propertyName):如果给定的属性使用了!important 设置,则返回"important";否则,返回空字符串。
- getPropertyValue(propertyName):返回给定属性的字符串值。
- item(index):返回给定位置的 CSS 属性的名称。
- removeProperty(propertyName):从样式中删除给定属性。
- setProperty(propertyName,value,priority):将给定属性设置为相应的值,并加上优先权标志("important"或者一个空字符串)。
- getComputedStyle()方法。这个方法接受两个参数:要取得计算样式的元素和一个伪元素字符串(例如":after")。如果不需要伪元素信息,第二个参数可以是 null。getComputedStyle()方法返回一个 CSSStyleDeclaration 对象(与 style 属性的类型相同),其中包含当前元素的所有计算的样式,IE 不支持 getComputedStyle()方法,但支持currentStyle 属性。记住所有计算的样式都是只读的;不能修改计算后样式对象中的 CSS 属性
操作样式表,不常用
- CSSStyleSheet 类型表示样式表,只读,继承自 StyleSheet,继承的属性:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-53kJuree-1569921316133)(JavaScript高级程序设计_files/14.jpg)],自身的属性:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NijbBvr1-1569921316134)(JavaScript高级程序设计_files/15.jpg)]
- css规则:CSSRule 对象表示样式表中的每一条规则,有一个实例为是 CSSStyleRule 类型表示样式信息,
- CSSStyleRule 对象包含下列属性:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-klPpcZvE-1569921316136)(JavaScript高级程序设计_files/16.jpg)]
- insertRule()方法可以向现有样式表中添加新规则,法接受两个参数:规则文本和表示在哪里插入规则的索引,注意IE8-支持addRule()
- deleteRule()从样式表中删除规则,法接受一个参数:要删除的规则的位置,注意IE支持的类似方法叫 removeRule(),使用方法相同
元素大小
- 偏移量:元素在屏幕上占用的所有可见的空间。元素的可见大小由其高度、宽度决定,包括所有内边距、滚动条和边框大小(注意,不包括外边距), 4个属性可以取得元素的偏移量,属性是只读的
- offsetHeight:元素在垂直方向上占用的空间大小,以像素计
- offsetWidth:元素在水平方向上占用的空间大小,以像素计
- offsetLeft:元素的左外边框至包含元素的左内边框之间的像素距离。
- offsetTop:元素的上外边框至包含元素的上内边框之间的像素距离。
- 客户区大小,元素内容及其内边距所占据的空间大小,属性有两个:clientWidth 和 clientHeight。属性是只读的
- clientWidth 属性是元素内容区宽度加上左右内边距宽度;
- clientHeight 属性是元素内容区高度加上上下内边距高度
- 滚动大小,包含滚动内容的元素的大小,相关属性
- scrollHeight:在没有滚动条的情况下,元素内容的总高度。
- scrollWidth:在没有滚动条的情况下,元素内容的总宽度。
- scrollLeft:被隐藏在内容区域左侧的像素数。通过设置这个属性可以改变元素的滚动位置。
- scrollTop:被隐藏在内容区域上方的像素数。通过设置这个属性可以改变元素的滚动位置。
- 确定元素大小,getBoundingClientRect()方法。这个方法返回会一个矩形对象,包含 4 个属性:left、top、right 和 bottom
遍历
NodeIterator 类型
- 创建实例:document.createNodeIterator()方法,接受4个参数
- root:想要作为搜索起点的树中的节点。
- whatToShow:表示要访问哪些节点的数字代码,参数是一个位掩码。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4YKIjKKZ-1569921316137)(JavaScript高级程序设计_files/17.jpg)]
- filter:是一个 NodeFilter 对象(,或者一个表示应该接受还是拒绝某种特定节点的函数
- NodeFilter 对象只有一个方法,即 acceptNode();如果应该访问给定的节点,该方法返回 NodeFilter.FILTER_ACCEPT,如果不应该访问给定的节点,该方法返回 NodeFilter.FILTER_SKIP,NodeFilter 是一个抽象的类型,因此不能直接创建它的实例。在必要时,只要创建一个包含 acceptNode()方法的对象,然后将这个对象传入createNodeIterator()中即可
// 传入NodeFilter 对象
var filter = {
acceptNode: function(node){
return node.tagName.toLowerCase() == "p" ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
}
};
var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT,
filter, false);
//传入类似函数
var filter = function(node){
return node.tagName.toLowerCase() == "p" ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
};
var iterator = document.createNodeIterator(root, NodeFilter.SHOW_ELEMENT,
filter, false);
- entityReferenceExpansion:布尔值,表示是否要扩展实体引用。这个参数在 HTML 页面中没有用,因为其中的实体引用不能扩展。
- NodeIterator 类型的两个主要方法是 nextNode()和 previousNode()
- 遍历节点的示例
// 返回遍历中遇到的<li>元素
var div = document.getElementById("div1");
var filter = function(node){
return node.tagName.toLowerCase() == "li" ?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP;
};
var iterator = document.createNodeIterator(div, NodeFilter.SHOW_ELEMENT,
filter, false);
var node = iterator.nextNode();
while (node !== null) {
alert(node.tagName); //输出标签名
node = iterator.nextNode();
}
TreeWalker
- TreeWalker 比 NodeIterator 更高级。除了包括 nextNode()和 previousNode()还提供了下列用于在不同方向上遍历 DOM 结构的方法。
- parentNode():遍历到当前节点的父节点;
- firstChild():遍历到当前节点的第一个子节点;
- lastChild():遍历到当前节点的最后一个子节点;
- nextSibling():遍历到当前节点的下一个同辈节点;
- previousSibling():遍历到当前节点的上一个同辈节点。
- 创建 TreeWalker 对象要使用 document.createTreeWalker()方法,这个方法接受的 4 个参数:作为遍历起点的根节点、要显示的节点类型、过滤器和一个表示是否扩展实体引用的布尔值
- 可以用TreeWalker 代替NodeIterator
var div = document.getElementById("div1");
var filter = function(node){
return node.tagName.toLowerCase() == "li"?
NodeFilter.FILTER_ACCEPT :
NodeFilter.FILTER_SKIP; //这里修改成 NodeFilter.FILTER_REJECT,就不会访问任何节点
};
var walker= document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT,
filter, false);
var node = iterator.nextNode();
while (node !== null) {
alert(node.tagName); //输出标签名
node = iterator.nextNode();
}
- 使用 TreeWalker遍历 DOM 树,即使不定义过滤器,也可以取得想要的某些元素
//直接定位<li>元素在文档结构中的位置
var div = document.getElementById("div1");
var walker = document.createTreeWalker(div, NodeFilter.SHOW_ELEMENT, null, false);
walker.firstChild(); //转到<p>
walker.nextSibling(); //转到<ul>
var node = walker.firstChild(); //转到第一个<li>
while (node !== null) {
alert(node.tagName);
node = walker.nextSibling();
}
范围
DOM中的范围,暂时略过
- document 对象的createRange()方法,使用 hasFeature()或者直接检测该方法,都可以确定浏览器是否支持范围
var supportsRange = document.implementation.hasFeature("Range", "2.0");
var alsoSupportsRange = (typeof document.createRange == "function");
- 使用document.createRange()创建的范围由一个 Range 类型的实例表示,这个实例拥有很多属性和方法
- startContainer:包含范围起点的节点(即选区中第一个节点的父节点)。
- startOffset:范围在 startContainer 中起点的偏移量。如果 startContainer 是文本节点、注释节点或 CDATA 节点,那么 startOffset 就是范围起点之前跳过的字符数量。否则,startOffset 就是范围中第一个子节点的索引。
- endContainer:包含范围终点的节点(即选区中最后一个节点的父节点)。
- endOffset:范围在 endContainer 中终点的偏移量(与 startOffset 遵循相同的取值规则)。
- commonAncestorContainer:startContainer 和 endContainer 共同的祖先节点在文档树中位置最深的那个。
- 用 DOM 范围实现简单选择,是使用 selectNode()或 selectNodeContents()。这两个方法都接受一个参数,即一个 DOM 节点,然后使用该节点中的信息来填充范围。
- selectNode()方法选择整个节点,包括其子节点
- selectNodeContents()方法则只选择节点的子节点
- 用 DOM 范围实现复杂选择
- 操作 DOM 范围中的内容
- 插入 DOM 范围中的内容
- 折叠 DOM 范围
- 比较 DOM 范围
- 复制 DOM 范围
- 清理 DOM 范围
IE8 及更早版本中的范围
IE8 及早期版本支持一种类似的概念,即文本范围,可以调用 createTextRange()方法来创建文本范围
- 用 IE 范围实现简单的选择
- 使用 IE 范围实现复杂的选择
- 操作 IE 范围中的内容
- 折叠 IE 范围
- 比较 IE 范围
- 复制 IE 范围