第10章、DOM

10.1 节点层次

DOM可以将任何HTML和XML文档描绘成一个由多层节点构成的结构,文档节点只有一个子节点,即<html>元素,称之为文档元素,文档元素是最外层元素,文档中的其他所有元素都包含在文档元素中,每个文档只能有一个文档元素。

10.1.1 Node类型

所有节点类型都继承自Node类型,因此所有节点类型都共享相同的基本属性和方法。总共有12种节点类型,每个节点都有一个nodeType属性,用于表明节点类型,是一个常量。nodeName和nodeValue两个属性中保存着节点的信息。
每个节点都有一个childNodes属性。其中保存着一个NodeList对象,用于保存一组有序的节点,它实际上是基于DOM结构动态执行查询的结果,因此DOM结构变化能够自动反应在NodeList对象中。

// 将NodeList对象转为数组
function toArray(nodes) {
    var arr = [];
    try {
        arr = Array.prototype.slice.call(nodes,0);
    }catch(ex){
    // IE8以前需要枚举所有成员
        for(var i=0,len=nodes.length;i<len;i++){
            arr.push(nodes[i]);
        }
    }
    return arr;
}

每个节点都有parentNode属性,指向文档树中的父节点。同一个childNodes列表中的所有节点都具有相同的父节点。因此它们的parentNode属性都指向同一个节点。包含在childNodes列表中的每个节点相互之间都是同胞节点,previousSibling表示前一节点,nextSibling表示后一节点。父节点的firstChild表示childNodes列表中的第一个节点,lastChild表示childNodes列表中的最后一个节点。也可以用方括号表示。即firstChild等于childNodes[0],lastChild等于childNodes[childNodes.length-1]。节点的hasChildNodes()方法在节点包含一个或多个子节点时返回true。所有节点都有ownerDocument属性,指向表示整个文档的文档节点。
appendChild()方法用于向childNodes列表末尾添加一个节点,并返回新添加的节点。如果传给该方法的节点已经是文档中的一部分,那结果就是将该节点从原来的位置转移到新位置。
insertBefore()方法用于将节点插入childNodes列表中特定的位置,这个方法接收两个参数,要插入的节点和作为参照的节点,插入后返回被插入的节点,参照节点为null时与appendChild()方法相同。
replaceChild()方法用于替换childNodes列表中的节点,这个方法接收两个参数,要插入的节点和要替换的节点,要替换的节点会从文档树中被移除,并被这个方法返回。
removeChild()方法用于移除childNodes列表中的节点,接收一个参数,即被移除的节点,被移除的节点将作为返回值返回。
cloneNode()方法用于创建调用和这个方法的节点的一个完全相同的副本,始终接收一个布尔值参数,表示是否执行深度复制,即复制节点及其整个子节点树,在参数为false时,只执行浅复制,即只复制节点本身。

10.1.2 Doucment类型

最常见的Doucment类型就是作为HTMLDocument实例的document对象。
document.documentElement属性指向<html>元素;
document.body属性指向<body>元素;
document.title属性包含<title>元素中的文本,可以通过这个取得当前页面的标题,也可以修改并反映在浏览器的标题中,但修改其值不会改变<title>元素;
document.URL属性保存页面完整的URL,即地址栏中显示的URL。
document.domain属性中包含当前页面的域名。
document.referrer属性中包含页面来源的URL。
查找元素的方法:
document.getElementById(),按照元素ID进行查找;
document.getElementByTagName(),按照元素标签名进行查找,返回HTMLCollection对象;
document.getElementByName(),按照元素标name特性进行查找,返回HTMLCollection对象;
HTMLCollection对象有一个namedItem()方法,可以根据元素的name特性取得HTMLCollection对象中对应的对象。
特殊合集:
document.anchors,包含文档中所有带有name特性的<a>元素;
document.forms,包含文档中所有<form>元素,与document.getElementByTagName("form")相同;
document.images,包含文档中所有<img>元素,与document.getElementByTagName("img")相同;
document.links,包含文档中所有带有href特性的<a>元素;
特殊合集中的内容会随当前文档的更新而更新。

10.1.3 Element类型

Element节点即元素节点,提供了对元素标签名,子节点及特性的访问。
nodeType值为1;
noadeName值为元素标签名,与tagName值相同,输出的元素标签名为大写;

<p id="a">asd</p>

console.log(document.getElementById("a").nodeType);  // 1
console.log(document.getElementById("a").tagName);  // P
console.log(document.getElementById("a").nodeName);  // P

所有HTML元素都由HTMLElement类型表示,每个HTML元素都具有以下标准特性,下列特性均可以修改。
id,元素在文档中的唯一标识符;
title,有关元素附加说明信息;
lang,元素内容语言代码;
dir,语言的方向;
className,元素的class特性。
特性修改方法:
getAttribute(),接收一个字符串参数,为需要获取的特性名称,直接获取传入特性的值,也可以获取自定义特性的值,并且不区分大小写,只有公认的(非自定义)特性才会以属性的形式添加到DOM对象当中,获取style特性值得到的是CSS文本,通过属性访问style得到的是一个对象,获取onclick之类事件处理程序特性值返回的是相应代码的字符串,通过属性访问onclick之类事件处理程序得到的是一个JavaScript函数(未指定事件处理程序则返回null)。

<p id="a" myID="b">asd</p>

var p = document.getElementById("a");
console.log(p.id);  // a
console.log(p.myID);  // undefined

setAttribute(),接收两个参数,一个为需要设置的特性名称,一个为需要设置的特性的值,如果需要设置的特性已经存在,则替换原有的特性值。
removeAttribute(),接收一个字符串参数,彻底删除元素的特性。
Element类型是使用attributes属性的唯一一个DOM节点类型。attributes属性中包含一个NamedNodeMap,与NodeList类型,是一个动态集合。每一个特性节点都保存在NamedNodeMap中,attributes属性仅在需要遍历元素特性的时候才用得到。每个特性节点都有一个specified属性,如果这个属性为true,则表示该特性要么是在HTML中指定的,要么是通过setAttribute()方法设置的。来自继承的特性和方法都为false。

<p id="a" class="b" title="c" data-myID="x">asd</p>

var p = document.getElementById("a");
var obj = {};
for(var i = 0 ;i<p.attributes.length; i++){
    if(p.attributes[i].specified){
        obj[p.attributes[i].nodeName]=p.attributes[i].nodeValue;
    }
}
console.log(obj);  // {id: "a", class: "b", title: "c", data-myid: "x"}

使用document.createElement()可以创建新元素,传入需要创建的新元素名称,创建后就可以对其进行各种DOM操作,包括设置各种特性。但最后需要appendChild()、insertBefore()、replaceChild()等方法将其添加到DOM当中。
浏览器会将元素之间的空白符解释为文本节点,如果需要通过childNodes属性遍历子节点,则需要对节点类型进行判断后再执行操作。

for(var i = 0 ;i<ele.childNodes.length; i++){
    if(ele.childNodes[i].nodeType == 1){
        // 执行操作
    }
}

每个元素应该只有一个文本子节点,使用normalize()方法可以将元素中的自文本节点合并成一个节点。


10.2 DOM操作技术

10.2.1 动态脚本

在页面加载完成后动态加载外部javascript文件的通用方法。

function loadScript(url) {
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = url;
    document.body.appendChild(script);
}

在页面加载完成后动态加载外部javascript代码片段的通用方法。

function loadScriptCode(code) {
    var script = document.createElement("script");
    script.type = "text/javascript";
    // IE不允许访问script元素的子节点,所以需要用script元素的text属性来添加代码片段。
    try{
        script.appendChild(document.createTextNode(code));
    }catch(ex) {
        script.text = code;
    }
    document.body.appendChild(script);
}
10.2.2 动态样式

在页面加载完成后动态加载外部CSS文件的通用方法。注意CSS代码应该添加到head元素中,不是body元素中。

function loadStyle(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);
}

在页面加载完成后动态加载外部CSS代码片段的通用方法。

function loadStyleCode(code) {
    var style = document.createElement("style");
    style.type = "text/css";
    // IE不允许访问style元素的子节点,所以需要用style元素的styleSheet属性的cssText属性来添加代码片段。
    try {
        style.appendChild(document.createTextNode(code));
    }catch (ex){
        style.styleSheet.cssText = code;
    }
    var head = document.getElementsByTagName("head")[0];
    head.appendChild(style);
}
10.2.3 操作表格

<table>元素具有以下属性和方法:
caption属性,保存着对<caption>元素的指针;
tBodies属性,<tbody>元素的HTMLCollection;
tFoot属性,保存着对<tfoot>元素的指针;
tHead属性,保存着对<thead>元素的指针;
rows属性,表格中所有行的HTMLCollection;
creatTHead(),创建<thead>元素,将其放到表格中,返回引用;
creatTFoot(),创建<tfoot>元素,将其放到表格中,返回引用;
creatCaption(),创建<caption>元素,将其放到表格中,返回引用;
deleteTHead(),删除<thead>元素;
deleteTFoot(),删除<tfoot>元素;
deleteCaption(),删除<caption>元素;
deleteRow(),删除指定参数的行;
insertRow(),向rows集合中指定位置插入行。
<tbody>元素具有以下属性和方法:
rows属性,<tbody>元素中所有行的HTMLCollection;
deleteRow(),删除指定参数的行;
insertRow(),向rows集合中指定位置插入行,返回对新插入行的引用。
<tr>元素具有以下属性和方法:
cells属性,<tr>元素中所有单元格的HTMLCollection;
deleteCells(),删除指定参数的行;
insertCells(),向cells集合中指定位置插入单元格,返回对新插入单元格的引用。

10.2.4 使用NodeList

与NodeList类似的集合,NameNodeMap、HTMLCollection之类的,都是动态合集,文档结构发生变化时,它们也会得到更新。如果要迭代一个NodeList,最好使用length属性初始化第二个变量,以保证迭代的数量不会随着迭代而发生变化。应尽量减少对NodeList的访问,因为每次访问NodeList,都会运行一次基于文档的查询,所以应当将NodeList中取得的值缓存起来,方便多次使用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容

  • DOM 就是文档的数据结构,它提供了操作文档的编程接口 API。 10.1 节点层次 文档是由节点组成的树形结构,...
    勤劳的悄悄阅读 347评论 0 0
  • “某某理财平台,提供信用贷款,放款快,流程简,安全无风险。”一早上就被数条如此的垃圾短信骚扰,顿时没了享受周末的好...
    奇楠说阅读 1,162评论 1 8
  • 最近发生了很多事,心里很不开心,工作生活都在进行,可是却没办法去和解什么,因为担心自己坠入下一个深渊。 世界就在那...
    自由家AU阅读 277评论 11 6