参考书:《JavaScript高级程序设计》
知识点前提:什么是节点
Node类型
DOM1级定义了一个Node接口,该接口将DOM中所有节点类型实现。Node接口在JavaScript中是作为Node类型实现的;除了IE之外,在其他所有浏览器都可以访问到这个类型。
nodeType
只读
每个节点都有一个nodeType属性,有用于表明节点的类型。节点类型由在Node类型中定义的下列12个数值常量表示,任何节点类型必占据一种。
- Node.ELEMENT_NODE(1);
- Node.ATTRIBUTE_NODE(2);
- Node.TEXT_NODE(3);
- Node.CDATA_SECTION_NODE(4);
- Node.ENTITY_REFERENCE_NODE(5);
- Node.ENTITY_NODE(6);
- Node.PROCESSING_INSTRUCTION_NODE(7);
- Node.COMMENT_NODE(8);
- Node.DOCUMENT_NODE(9);
- Node.DOCUMENT_TYPE_NODE(10);
- Node.DOCUMENT_FRAGMENT_NODE(11);
- Node.NOTATION_NODE(12);
test1
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<h1 id="h">i'm h1</h1>
</body>
<script>
var h = document.getElementById("h");
console.log(h.nodeType); // 1
if (h.nodeType === Node.ELEMENT_NODE) console.log(true); // true
</script>
</html>
使用Node类型常量来比较。
由于IE没有公开Node类型函数,因此上面代码在IE中会导致错误。为确保浏览器兼容,最好将nodeType属性与数字进行比较
test2
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<h1 id="h">i'm h1</h1>
</body>
<script>
var h = document.getElementById("h");
if (h.nodeType === 1) console.log(true); // true
</script>
</html>
不是所有节点都会受到浏览器支持,最常用的就是元素和文本节点。
nodaName | nodeValue
nodaName
只读
nodaValue
可写可读
使用nodeName和nodeValue可以具体了解节点信息,这两个属性的值完全取决于节点的类型。
可以检测节点类型后在使用其属性,下图列出了不同节点类型的nodeValue的返回值。
所以,对于element这样的节点,返回值都为null。
test3
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<h1 id="h">i'm h1</h1>
</body>
<script>
var h = document.getElementById("h");
console.log(h.nodeName+" | "+h.nodeValue);
// H1 | null
</script>
</html>
可以这样
test4
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<h1 id="h">i'm h1</h1>
</body>
<script>
var h = document.getElementById("h").getAttributeNode("id");
console.log(h); // id [object attr]
console.log(h.nodeValue); // h
</script>
</html>
使用getAttributeNode方法,输入属性名,返回其attribute节点对象,然后使用NodeValue方法,返回值就为实际属性值。
节点关系
文档中所有的节点之间都存在这样或者那样的关系。节点间的各种关系可以用传统的家族关系来描述,每个节点存在childNodes属性,其中保存着一个Nodelist对象,是一种类数组对象,保存一组有序的节点,可以通过位置来访问这些节点。
虽然可以使用方括号语法来访问Nodelist的值,而且这个对象也有length属性,但它并不是Array的实例。Nodelist对象的独特之处在于,它实际上是基于DOM结构动态执行查询的结果,因此DOM结构的变化能够自动反映在Nodelist对象中。
我们常说,Nodelist是有生命、有呼吸的对象,而不是在我们第一次访问它们的某个瞬间拍摄下来的一张快照。
test5*
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div">
<p>i'm P</p>
<h1>i'm H1</h1>
</div>
</body>
<script>
var html = document.getElementsByTagName("html")[0];
console.log(html);
</script>
</html>
可以看到其中childNodes属性里,Nodelist对象的传承,包含了从根目录下的所有节点。
其中,#div的Nodelist里,是否发现了有5个节点,但实际上源代码只有2个元素啊,其实[0],[2],[4]
所代表的#text是我们源代码换行时留下的空位,在Nodelist里就用#text来表示了。
<div id="div"><p>i'm P</p><h1>i'm H1</h1></div>
修改后,将只有两个element。
注意,length属性表示的是访问Nodelist的那一刻,其中包含的节点数量。
对于JS函数内部类数组对象arguments对象使用Array.prototype.slice()
方法可以将其转换为数组。而采用同样的方法,也可以将Nodelist对象转换为数组。
test6
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div">
<p>i'm P</p>
<h1>i'm H1</h1>
</div>
</body>
<script>
var div = document.getElementById("div");
// 将Nodelist对象转换为数组
var arrayOfNodes = Array.prototype.slice.call(div.childNodes,0);
// 修改数组不会反映到Nodelist
arrayOfNodes.splice(1,0,arrayOfNodes[3]);
console.log(div.childNodes);
</script>
</html>
为方便演示,缩小范围,只抽取了#div做演示,
白箭头即为转换后的真数组,对转换后数组进行了一次数组splice增添节点操作,那么,是否会映射到Nodelis上呢?
红箭头是Nodelist对象,看看它,依然不变。
当然,通过Nodelist对象方法添加是可以,后续~
对于刚才的Nodelist对象转换数组方法,由于在IE8及更早版本将Nodelist实现为一个COM对象,而我们不能像使用JavaScript对象那样使用这种对象,因此原方法会导致错误,要想在IE中Nodelist转换为数组,必须手动枚举所有成员。
下列代码在所有浏览器均可运行:
test7
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div">
<p>i'm P</p>
<h1>i'm H1</h1>
</div>
</body>
<script>
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]);
}
}e
return array;
}
var div = document.getElementById("div");
console.log(convertToArray(div.childNodes));
</script>
</html>
Array(5) [#text, p, #text, h1, #text]
函数首先尝试了创建数组的最简单的方式。如果导致错误(说明在IE8及更早版本中),则通过try-catch来捕获错误,然后手动创建数组。
每个节点上都有一个parentNode属性,该属性指向文档树中的父节点。包含在childNodes列表中的所有节点都具有相同的父节点,因此它们的parentNode属性都指向同一个节点。此外,包含在childNodes列表中的每个节点相互之间都是同胞节点(siblingNode)。通过使用列表中的每个节点的previousSibling和nextSibling属性,可以访问同一列表中的其他节点。
关于节点关系,主要就是子节点、节点、父节点之间的定位关系。
test8
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div"><p id="p">i'm P</p><h1 id="h1">i'm H1</h1></div>
</body>
<script>
var div = document.getElementById("div");
var p = document.getElementById("p");
var h = document.getElementById("h1");
console.log(div.firstChild); // 选中#div的第一个子元素:p#p
console.log(div.lastChild); // 选中#div的最后一个子元素:h1#h1
console.log(p.nextSibling); // 选中#p的下一个兄弟元素:h1#h1
console.log(h.previousSibling); // 选中#h1的上一个兄弟元素:p#p
console.log(p.parentNode); // 选中#p的父元素:div#div
</script>
</html>
在反映这些关系的所有属性当中,childNodes属性与其他属性相比更方便一些,因为只须使用简单的关系指针,就可以通过它访问文档树中的任何节点。另外,hasChildNodes()也是一个非常有用的方法,这个方法在节点包含一或多个子节点的情况下返回true;应该说,这是比查询childNodes列表的length属性更简单的方法。
所有节点都有的最后一个属性时ownerDocument,该属性指向表示整个文档的文档节点。这种关系表示的是任何节点都属于它所在的文档,任何节点都不能同时存在与两个或多个文档中。通过这个属性,我们可以不必在节点层次中通过层层回溯到达顶端,而是可以直接访问文档节点。
操作节点
因为关系指针都是只读的,所以DOM提供了一些操作节点的方法。其中,最常用的方法时appendChild(),用于向childNodes列表的末尾添加一个节点。添加节点后,childNodes的新增节点、父节点及以前的最后一个子节点的关系指针都会相应的得到更新(因为childNodes属性里的Nodelist对象更新了)。更新完成后,appendChild()返回新增的节点。
test9
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div">
<p id="p">i'm P</p>
<h1 id="h1">i'm H1</h1>
</div>
</body>
<script>
var h6 = document.createElement("h6");
var div = document.getElementById("div");
var returnNode = div.appendChild(h6);
console.log(returnNode === h6); // true
console.log(div.lastChild === h6); // true
</script>
</html>
如果传入到appendChild()中的节点已经是文档中的一部分了,那结果就是将该节点从原来的位置转移到新位置。即使可以将DOM数看成是由一系列指针连接起来的 ,但任何DOM节点也不能同时出现在文档中的多个位置上。因此,如果在调用appenChild()时传入了父节点的第一个子节点,那么该节点就会成为父节点的最后一个子节点。
test10
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div">
<p id="p">i'm P</p>
</div>
</body>
<script>
var div = document.getElementById("div");
var returnNode = div.appendChild(div.firstChild);
console.log(returnNode === div.firstChild); // false
console.log(returnNode === div.lastChild); // true
</script>
</html>
如果需要把节点放在childNodes列表中某个特定的位置上,而不是放在末尾,那么可以使用inserBefore()方法。这个方法接受两个参数:要插入的节点和作为参照的节点。插入节点后,被插入的节点会变成参照节点的前一个同胞节点(preivousSibling),同时被方法返回,如果参照节点是null,则insertBefore()与appendChild()执行相同的操作。
test11
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div"><p id="p">i'm P</p><h1 id="h1">i'm H1</h1></div>
</body>
<script>
var div = document.getElementById("div");
var h1 = document.getElementById("h1");
var p = document.getElementById("p");
console.log(div);
// 创建的新节点h6
var h6 = document.createElement("h6");
var text = document.createTextNode("i'm h6");
h6.appendChild(text);
// 将新创建的h6节点插入到h1之前
div.insertBefore(h6,h1);
console.log(h1.previousSibling === h6); // true
// p > h6 > h1
// 将h1插入到p之前,同时h6位置为最后一个子节点
div.insertBefore(h6,p);
console.log(p.previousSibling === h6); // true
console.log(div.firstChild === h6); // true
console.log(div.lastChild === h1); // true
</script>
</html>
注意,如果将代码换行,保证可读性的同时,会使Nodelist对象增加若干的#text
节点,这是换行导致的。
如若要替换节点可使用replaceChild()方法
方法接受两个参数:要插入的节点,要替换的节点。要替换的节点将由这个方法返回并从文档树中移除,同时插入的节点占据其位置。
test12
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div"><p id="p">i'm P</p><h1 id="h1">i'm H1</h1>
</div>
</body>
<script>
var div = document.getElementById("div");
var h1 = document.getElementById("h1");
var p = document.getElementById("p");
// 创建一个新节点h3
var h3 = document.createElement("h3");
h3.appendChild(document.createTextNode("i'm h3"));
// 替换p节点
var returnNode = div.replaceChild(h3,p);
console.log(returnNode); // p#p
</script>
</html>
使用replaceChild()插入一个节点时,该节点所有关系指针都会从被它替换的节点赋值过来,尽管从技术上讲,被替换的节点还在文档中,但它在文档中已经没有了自己的位置。
如果只想移除而非替换节点,可以使用removeChild()方法。
方法接受一个参数,即要移除的节点。被移除的节点将成为方法的返回值。
test13
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<div id="div"><p id="p">i'm P</p><h1 id="h1">i'm H1</h1>
</div>
</body>
<script>
var div = document.getElementById("div");
var h1 = document.getElementById("h1");
var p = document.getElementById("p");
// 创建一个新节点h3
var h3 = document.createElement("h3");
h3.appendChild(document.createTextNode("i'm h3"));
// 替换p节点
var returnNode = div.replaceChild(h3,p);
console.log(returnNode); // p#p
// 移除第一个节点
var formerFirstChild = div.removeChild(div.firstChild);
console.log(formerFirstChild); // h3
console.log(div.firstChild); // h1#h1
</script>
</html>
有两个方法是所有类型节点都有的。
-
cloneNode()
-
normalize()
cloneNode()方法接受一个布尔值参数,表示是否执行深复制。参数为true情况下,执行深复制,也就是复制节及其整个子节点树;在参数为false情况下,执行潜复制,即只复制节点本身。复制后返回的节点副本属于文档所有,但并没有为它指定父节点。因此这个节点副本就成为了一个“孤儿”,除非通过appedChild,insertBefore,replaceChild将它添加到文档中。
test14
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Fuck!</title>
</head>
<body>
<ul>
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
</ul>
</body>
<script>
var ul = document.getElementsByTagName("ul")[0];
var deepList = ul.cloneNode(true);
var shallow = ul.cloneNode(false);
console.log(deepList.childNodes.length); // 7 4个空隙,三个元素,长度为7
console.log(shallow.childNodes.length); // 0
</script>
</html>
cloneNode()不会赋值添加到DOM节点中的JavaScript属性,例如事件处理程序等,这个方法只复制特性、子节点(true),其他一切都不会复制。
normalize()
这个方法唯一作用就是处理文档树中的文本节点。
由于解析器的实现或者DOM操作等原因,可能会出现文本节点或者不包含文本,或者接连出现两个文本节点的情况。
当在某个节点上调用这个方法时,就会在该节点的后代节点中查找上述两种情况。如果找到空白文本节点,则删除它,如果找到相邻的文本节点,则将它们合并为一个文本节点。
Document类型
JavaScript通过Document类型表示文档。在浏览器汇总,document对象是HTMLDocument(继承自Document类型)的一个实例,表示整个HTML页面。而且,document对象是window对象的一个属性,因此可以将其作为全局对象来访问。
window.document === document // true
Document节点具有下列特征:
-
nodeType的值为:9
-
nodeName的值为:"#document"
-
nodeValue的值为:null
-
parentNode的值为:null
-
其子节点可能是一个DocumentType(最多一个)、Element(最多一个)、ProcessingInstruction或Comment。
Document类型可以表示HTML页面或者其他基于XML的文档。不过,最常见的应用还是作为HTMLDocument实例的document对象。通过这个文档对象,不仅可以取得与页面有关的信息,而且还能操作页面的外观及其底层结构。
1.文档的子节点
虽然DOM标准规定Document节点的子节点可以是DocumentType、Element、ProcessingIn-struction或Comment,但还有两个内置的访问其子节点的快捷方式。第一个就是documentElement属性,该属性始终指向HTML页面中的<html>元素。另一个就是通过childNodes列表访问文档元素,但通过documentElement属性则能更快捷、直接的访问元素。
<html>
<body>
</body>
</html>
这个页面经浏览器解析后,其文档中只包含一个子节点,即<html>
元素。可以通过documentElement或childNodes列表来访问这个元素。
var html = document.documentElement; // 取得对<html>的引用
console.log(html === document.childNodes[0]); // true
console.log(html === document.firstChild); // true
documentElement、firstChild和childNodes[0]的值相同。,都指向<html>元素。
作为HTMLDocument的实例,document对象还有一个body属性,直接指向<body>元素。因为开发人员经常要使用这个元素,所以document.body在JS代码汇总出现的频率很高。
var body = document.body;
所有浏览器都支持document.documentElement和document.body属性。
Document另一个可能的子节点就是DocumentType。通常将<!DOCTYPE>标签看成一个与文档其他部分不同的实体,可以通过doctype属性来访问它的信息。
var doctype = document.doctype;
类似的,还有如下功能方法,取得文档信息。
取得文档标题
var originalTitle = document.title;
设置文档标题
document.title = "New page ttile";
网页请求相关方法;
- URL:属性中包含页面完整的URL(即地址栏中显示的URL)
- domain:只包含页面的域名
- referrer:保存链接到当前页面的页面的URL(从哪到这的),referrer属性可能包含空字符串。所有信息都存在请求HTTP头部,只不过通过这些属性让我们能够在JavaScript中访问它们而已。
//取得完整URL
var url = document.URL;
//取得域名
var domain = document.domain;
//取得来源页面的URL
var referrer = document.referrer;
URL和domain属性是相互关联的。例如,如果document.URL为http://www.wrox.com/WileyCDA/
,那么document.domain就是www.wrox.com
。
这三个属性中,只有domain是可以设置的,但由于安全方面限制,并非可以给domain设置任何值。如果URL包含一个子域名,例如p2p.wrox.com,那么就只能将domain设置为wrox.com
。不能将这个属性设置为URL中不包含的域。
// 假设页面来自:p2p.wrox.com
document.domain = "wrox.com"; // 成功
document.domain = "nczonline.net"; // 出粗!
当页面中包含来自其他子域的框架或内嵌框架时,能够设置domain就非常方便了。由于跨域安全限制,来自不同子域的页面无法通过JavaScript通信。而通过将每个页面的document.domain设置为相同的值,这些页面就可以互相访问对方包含的JavaScript对象了。例如,假设有一个页面加载自www.wrox.com,其中包含一个内嵌框架,框架内的页面加载来自p2p.wrox.com。由于document.domain字符串不一样,内外两个页面之间无法相互访问对方的JavaScript对象。但如果将两个页面的document.domain值设置为wrox.com,他们之间就可以通信了。
浏览器对domain属性还有一个限制,即如果域名一开始是松散的,那么不能将它设置为紧绷的。换句话说,在将document.domain设置为wrox.com之后,不能再将其设置回p2p.wrox.com,否认导致错误。
// 假设页面来自于p2p.wrox.com域
document.domain = "wrox.com"; // 松散的(成功)
document.domain = "p2p.wrox.com" // 紧绷的(出错!)
查找元素
Document类型提供的两个方法:
- getElementById()
- getElementsByTagName()
第一个方法,接受一个参数:要取得的元素ID,如果找到相应的元素则返回该元素,如果不存在带有相应的ID的元素,则返回null。注意,这里的ID必须与页面中的元素的ID特性严格匹配,包括大小写。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv"></div>
<script>
var div = document.getElementById("myDiv"); // 取得对<div>元素的引用
</script>
</body>
</html>
但是,在IE7下和更早版本的浏览器都将返回null。
var div = document.getElementById("mydiv");
IE8及较早版本不区分ID的大小写,因此"myDiv"和"mydiv"会被当作相同的元素ID。
如果页面中多个元素的ID值相同,getElmentById()只返回文档中第一次出现的元素。
document.getElementsByTagName()
接受一个参数,既要取得元素的标签名,返回的是包含零或多个元素的Nodelist。在HTML文档中,这个方法会返回一个HTMLCollection对象,作为一个动态集合,该对象与Nodelist对象非常类似。例如,下列代码回取得页面中所有的<img>元素,并返回一个HTMLCollection。
var images = document.getElementsByTagName("img");
这行代码会将一个HTMLCollection对象保存在images变量中。与Nodelist对象类似,可以使用方括号语法或item()方法来访问对象中的项。而这个对象中元素的数量则可以通过其length属性取得。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv"></div>
<script>
var divs = document.getElementsByTagName("div");
console.log(divs.length); // 1 输出div的数量
console.log(divs[0].id); // myDiv 输出第一个div元素的ID特性
console.log(divs.item(0).id); // myDiv 输出第一个div元素的ID特性
</script>
</body>
</html>
HTMLCollection对象还有一个方法,叫做namedItem(),使用这个方法可以通过元素的那么name特性取得集合中的项。例如,假设上面提到的页面中包含如下<div>元素
<div id="myDiv" name="myDiv">
那么可以通过如下方式从divs变量中取得这个<div>元素。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv" name="myDiv"></div>
<script>
var divs = document.getElementsByTagName("div");
var div = divs.namedItem("myDiv");
console.log(div); // div#myDiv
</script>
</body>
</html>
咋提供按索引访问项的基础上,HTMLCollection还支持按名称访问项,这就为我们取得实际想要的元素提供了便利。而且,对命名的项也可以使用方括号语法来访问。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv"></div>
<script>
var divs = document.getElementsByTagName("div");
var div = divs["myDiv"];
console.log(div); // div#myDiv
</script>
</body>
</html>
第三个方法,也就是只有HTMLDocument类型才有的方法,是getElementsByName(),顾名思义,方法会返回带有给定name特性的所有元素。最常使用getElementsByName()方法的情况是取得单选按钮。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<fieldset>
<legend>Which color do you prefer?</legend>
<ul>
<li>
<input type="radio" value="red" name="color" id="colorRed">
<label for="colorRed">red</label>
</li>
<li>
<input type="radio" value="green" name="color" id="colorGreen">
<label for="colorGreen">Green</label>
</li>
<li>
<input type="radio" value="blue" name="color" id="colorBlue">
<label for="colorBlue">Blue</label>
</li>
</ul>
</fieldset>
<script>
var radios = document.getElementsByName("color");
</script>
</body>
</html>
所有的单选按钮的name特性值相同都是color,但它们的ID不同,ID作用在于将label元素应用到每个单选按钮,而name特性则用以确保三个值中只有一个被发送给浏览器。这样,我们取得所有单选按钮。
特殊集合
除了属性和方法,document对象还有一些特殊的集合,这些集合都是HTMLCollection对象,为访问文档常用的部分提供了快捷方式,包括:
- document.anchors,包含文档中所有带name特性的<a>元素
- document.applets,包含文档中所有的<applet>元素,很少使用。
- document.forms,包含所有的<form>元素
- document.images,包含所有的<img>元素
- document.links,包含文档中所有的带href特性的<a>元素。
Element类型
访问元素的标签名,使用nodeName属性,也可以使用tagName属性;这两个属性会返回相同的值,使用后者主要是为了清晰可见。
<div id="myDiv"></div>
可以先这样取得元素及其标签名:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv"></div>
<script>
var div = document.getElementById("myDiv");
console.log(div.tagName); // "DIV"
console.log(div.tagName === div.nodeName); // true
</script>
</body>
</html>
所有的HTML元素都由HTMLElement类型表示,不是直接通过这个类型,也是通过它的子类型表示。HTMLElement类型直接继承自Element并添加了一些属性。添加的这些属性分别对应于每个HTML元素中都存在的下列标准特性。
- id,元素在文档中的唯一标识符
- title,元素附加说明信息,通过工具提示条显示
- lang,元素语言代码
- dir,语言方向,ltr(left to right),rtl(right to left),很少使用。
- className,与元素class属性对应。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
<script>
var div = document.getElementById("myDiv");
console.log(div.id); // myDiv
console.log(div.className); // bd
console.log(div.title); // Body text
console.log(div.lang); // en
console.log(div.dir); // ltr
</script>
</body>
</html>
也可以为每个属性赋值,修改对应属性的特性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
<script>
var div = document.getElementById("myDiv");
div.id = "someOtherId";
div.className = "ft";
div.title = "Some other text";
div.lang = "fr";
div.dir = "rtl";
console.log(div.id);
console.log(div.className);
console.log(div.title);
console.log(div.lang);
console.log(div.dir);
/*
someOtherId
ft
Some other text
fr
rtl
*/
</script>
</body>
</html>
*取得特性
getAttribute()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv" class="bd" title="Body text" lang="en" dir="ltr"></div>
<script>
var div = document.getElementById("myDiv");
console.log(div.getAttribute("id"));
console.log(div.getAttribute("class"));
console.log(div.getAttribute("title"));
console.log(div.getAttribute("lang"));
console.log(div.getAttribute("dir"));
/*
myDiv
bd
Body text
en
ltr
*/
</script>
</body>
</html>
传递给方法的特性名与实际的特性名相同,因此要想得到class的特性值,应该传入class而不是className,后者只有在通过对象属性访问特性时采用。如果给定名称特性名不存在,则返回null。
当然,你可以添加自定义特性,标准HTML中没有的特性。
<div id="myDiv" my_special_attrbute="hello!"></div>
这个元素包含一个名为my_special_attrbute
的自定义特性,值为hello!
,可以像取得其他特性一样取得这个值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div id="myDiv" my_special_attrbute="hello!"></div>
<script>
var div = document.getElementById("myDiv");
var value = div.getAttribute("my_special_attrbute");
console.log(value); // hello!
</script>
</body>
</html>
不过,只有公认的特性才会以属性的形式添加到DOM对象中
<div id="myDiv" align="left" my_special_attribute="hello!"></div>
因为id和align在HTML中是<div>的公认特性,因此该元素的DOM对象中也将存在对应的属性。不过,自定义特性my_special_attribute
在Safari、Opera、Chrome及Firefox是不存在的;但IE却会为自定义特性也创建属性。
console.log(div.id); // "myDiv"
console.log(div.my_special_attribute); // undefined(IE除外)
console.log(div.align); // "left"
有两类特殊的特性,它们虽然有对应的属性名,但属性值与通过getAttribute()返回的值并不相同,第一类是style,通过CSS为元素指定形式。通过getAttribute()访问时,返回的style特性值中包含的是CSS文本,而通过属性来访问它则返回一个对象。由于style属性用于以编程方式访问元素样式的,因此并没有映射到style属性。
第二类与众不同的特性时onclick这样的事件处理程序,当在元素上使用时,onclick特性中包含的是JavaScript代码,如果通过getAttribute()访问,则会返回相应的代码的字符串。而在访问onclick属性时,则会返回一个JavaScript函数(如果未指定,返回null)这是因为onclick及其他事件处理程序属性本身就应该被赋予函数值。
由于这些差别,通过JavaScript编程方式操作DOM时,开发者经常不使用getAttribute(),而是只使用对象属性,只有在取得自定义特性值情况下,才会使用getAttribute()方法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style>
</style>
<body>
<div id="myDiv" style="background-color:red;width:100px;height:100px"></div>
<script>
var div = document.getElementById("myDiv");
console.log(div.getAttribute("style")); // background-color:red;width:100px;height:100px
console.log(div.style);// CSSStyleDeclaration {0: "background-color", 1: "width", 2: "height", alignContent: "", alignItems: "", alignSelf: "", alignmentBaseline: "", all: "", …}
</script>
</body>
</html>
设置特性
与getAttribute()对应的方法是setAttribute(),这个方法接受两个参数:要设置的特性名和值。如果特性已经存在,setAttribute()会以指定的值替换现有的值;如果特性不存在,setAttribute()则创建该属性并设置相应的值。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style>
</style>
<body>
<div></div>
<script>
var div = document.getElementsByTagName("div")[0];
div.setAttribute("id","someOtherId");
div.setAttribute("class","ft");
div.setAttribute("title","Some other text");
div.setAttribute("lang","fr");
div.setAttribute("dir","rtl");
</script>
</body>
</html>
通过方法既可以操作HTML特性,也可以操作自定义特性。通过这个方法设置的特性名会统一转换为小写形式,即ID最终会成为id。
不过,添加自定义属性,该属性不会自动成为元素的特性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style>
</style>
<body>
<div></div>
<script>
var div = document.getElementsByTagName("div")[0];
div.mycolor = "red";
console.log(div.getAttribute("mycolor")); // null
</script>
</body>
</html>
attributes属性
Element类型是使用attributes属性的唯一一个DOM节点类型。attributes属性中包含一个NamedNodeMap,与Nodelist类似,也是一个动态集合。元素的每一个特性都由一个Attr节点表示,每个节点都保存在NamedNodeMap对象中。NamedNodeMap拥有下列方法:
- getNamedItem(name):返回nodeName属性等于name的节点;
- removeNamedItem(name):从列表中移除nodeName属性等于name的节点;
- setNamedItem(node):向列表中添加节点,以节点的nodeName属性为索引;
- item(pos):返回位于数字pos位置处的节点。
attributes属性中包含一系列节点,每个节点的nodeName就是特性的名称,而节点的nodeValue就是特性的值。要取得元素的id特性,可以:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style>
</style>
<body>
<div id="myDiv"></div>
<script>
var div = document.getElementById("myDiv");
var id = div.attributes.getNamedItem("id").nodeValue;
console.log(id); // myDiv
</script>
</body>
</html>
或者使用方括号语法通过特性名称访问节点的简写方式。
var id = div.attributes["id"].nodeValue;
也可以通过以上的方法设置特性的值。
div.attributes["id"].nodeValue = "someOtherId";
一般来说,attributes的方法不够方便,更多的会使用getAttribute()、removeAttribute()和setAttribute()方法。
不过,如果想要遍历元素特性,attributes属性可以派上用场。在需要将DOM结构序列化为XML或者HTML字符串时,多数都会涉及遍历元素特性。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<style>
</style>
<body>
<div id="myDiv" class="div_class" align="left" name="DIV"></div>
<script>
var div = document.getElementById("myDiv");
function outputAttributes(element) {
var attrName;
var attrValue;
var pairs = [];
var len = element.attributes.length;
for (var i = 0; i < len; i++) {
attrName = element.attributes[i].nodeName;
attrValue = element.attributes[i].nodeValue;
pairs.push(attrName + '=\"' + attrValue + '\"');
}
return pairs;
}
console.log(outputAttributes(div));
</script>
</body>
</html>
针对attributes对象中的特性,不同浏览器返回的顺序不同。这些特性在XML和HTML代码中出现的先后顺序,不一定与它们出现在attributes对象中的顺序一致。
IE7及更早版本会返回HTML元素中所有可能的特性,包括没有指定的特性。换句话说,返回100多个特性的情况会很常见。
创建元素
var div = document.createElement("div");
使用createElement()方法创建新元素的同时,也为新元素设置了ownerDocument属性。此时,还可以操作元素的特性,为它添加更多子节点,以及执行其他操作。
div.id = "myNewDiv";
div.className = "box";
然后添加到文档数中。可以使用appendChild()、insertBefore()、replaceChild()方法。
document.body.appendChild(div);