DOM
• DOM的全称是Document Object Model 文档对象模型,DOM定义了表示和修改文档所需的对象、这些对象的行为和属性以及这些对象之间的关系。
• DOM对象即为宿主对象,由浏览器厂商定义,用来操作html的css功能的一类对象和集合。不过浏览器厂商之间大部分都遵循w3c标准。
• 简单来说,DOM就是用来操作html和css的,它是一系列对象的集合。
DOM如何操作HTML
那么我们如何查看元素节点?
我们知道css中有id、class、标签等选择器,同样,我们的document对象上也定义了很多类似的方法来查看元素节点。
• getElementsByTagName
document.getElementsByTagName(‘div’);这个方法是可以选择出来具体某一种元素的集合,像前面这一段就可以选择出全部的div集合,当然也是一个类数组。
这个方法所有版本的浏览器都兼容。
<div id = "demo1">
<div id = "demo2">2</div>
</div>
var demo = document.getElementsByTagName('div');
console.log(demo)
结果如下:
• getElementById
document.getElementById(‘id’);方法是通过元素的id来选择出相对应的元素的,因为id是唯一标示,所以方法名中是Element。
值得注意的是,在ie8以下的浏览器中,不区分大小写,而且标签的name属性也可以被当做id被选择出来。
<div name=”demo”></div>
var div = document.getElementById(‘demo’);
这里同样把这个div选择出来了,但是这选取只是一个元素,这样操作比较方便一些。
• getElementsByClassName
document.getElementsByClassName(‘class’); 获取到的是一个类数组,因为很多元素都可以有一个类名。我们可以通过[]的方式来选择到具体的哪一个元素。
<div class=”demo”></div>
<div class=”demo”></div>
var div = document.getElementsByClassName(‘div’)[1];
这样我们就可以选择到第二个div了。
不过如果我们碰到这种情况该怎么办?
<div class=”demo”></div>
<div class=”demo demo1″></div>
<div class=”demo1″></div>
我们改如何选择出来第二个div?
这里,我们的getElementsByClassName其实后面可以填写多个类名。
var div = document.getElementsByClassName(‘demo demo1’)[0];
这样,我们就可以选择出来第二个div了。
但是值得注意的是,ie8及以下的版本中没有这种方法。
• querySelector() 非实时
• querySelectorAll() 非实时
这两个方法通常放在一起说。
我们知道选择元素最强的是css,而这两个里面写的参数就是我们css选择器的写法。
document.querySelector(‘div p #demo .demo);
不过querySelector永远选择一组里面的第一个,所以返回的不是一个类数组而是一个具体的元素。
而我们如果要返回一个类数组的集合的话,那么就用第二个querySelectorAll()方法。
节点
我们页面里面的节点类型很多,比如元素节点、文本节点、注释节点、属性节点等等。
我们可以通过nodeType属性来查看这个节点的类型是什么。而nodeType返回的是一些数字,下面介绍几个基础的类型和数字的对应关系:
元素节点——1 //这个经常使用
属性节点——2
文本节点——3
注释节点——8
document——9
DocumentFragment——11
• nodeName
这个属性可以返回元素的标签名,以大写的形式表示,只读,不允许写入。
有几个特殊的节点返回的也不太一样:
文本节点–> #text
注释节点–> #comment
document节点–>#document
• nodeValue
Text节点或者Comment节点的文本内容,可以读写
• attributes
把元素的行间属性都读取出来,放到一个对象里面返回,对象里面的每一个属性都是一个节点,这个节点就是我们前面提到的属性节点。
注意:对象里面的属性叫做property,而元素里面的属性叫attributes,实际应该叫特性。
节点还有一个方法
hasChildNodes()可以检测是否有子节点
遍历节点树
• parentNode 查找父节点
<div>
<p>
<a></a>
</p>
</div>
这里的a标签的父节点是p,p的父节点是div,div的父节点是body,body的父节点是html,html,的父节点是document,document的父节点就是null了。到达顶端了。
• childNodes 子节点们
var oDiv = document.querySelector('div');
console.log(oDiv.childNodes)
还是这个例子,我们div里面的childNodes其实有3个,第一个是前面的空格——文本节点,第二个是中间的p标签——元素节点,第三个是最后的空格——文本节点。
• firstChild 第一个子节点
• lastChild 最后一个子节点
• nextSibling 下一个兄弟节点
• previousSibling 上一个兄弟节点
以上这些方法的兼容性都很好,所有的浏览器都支持,但是下面这些就不行了。
• 基于元素节点树的遍历
• parentElement 返回当前元素的父元素节点(这个只能返回元素节点)
在这个方法上面,html上面的父元素节点就不是document而是null了。
但是ie不支持这个方法。
• children 所有子元素节点
这个方法所有的浏览器都兼容。
• childElementCount
node.children.length === node.childElementCount
这个属性就是子元素节点的数量,不过我更常用前面的那个。
• nextElementSibling
• previousElementSibling
这两个方法分别是找上一个和下一个兄弟元素节点,但是ie都不兼容。
• DOM结构树
下面是一些DOM结构树的总结:
1.getId方法定义在Document.prototype上,即Element节点上不能使用。
2.getElementByName方法定义在HTMLDocument.prototype上,非html中的document不能使用(xml document、Element);
3.getElementsByTagName方法定义在Document.prototype和Element.prototype上,也就是document和元素都可以用这个方法。
4.HTMLDocument.prototype上定义了一些常用的属性,body、head分别代指HTML文档中的标签。
5.Document.prototype上定义了documentElement属性,指代文档的根元素,在html文档中,它总代指元素。
6.getElementByClassName、querySelectorAll、querySelector在Document、Element类中均有定义。
接下来,看一下代码吧,使用这些方法:
1.遍历元素节点树
var domNode = {
retNode: function(node){
var len = node.childNodes.length;
var child = node.childNodes;
for(var i = 0;i<len;i++){
if(child[i].nodeType == 1){
console.log(child[i]);
child[i].hasChildNodes && domNode.retNode(child[i])
}
}
},
retNode1: function(node){//此方法IE浏览器不适用,上面一种兼容性比较好。
var len = node.children.length;
var child = node.children;
for(var i = 0;i < len; i++){
console.log(child[i]);
child[i].children && domNode.retNode1(child[i]);
}
}
}
2.封装函数,返回元素e的第n层父节点
function retParentNode(e,n){
var n = n || 0;
if(n === 0 ){
return e;
}
for(var i = 0;e && i < n;i++){
e = e.parentNode;
}
return e;
}
3.封装函数,返回元素e的第n个兄弟元素节点,如果n为正,返回后面的兄弟元素节点,n为负,返回前面的,n为0,返回自己
function retSibling(e,n){
var n = n || 0;
if(n == 0){
return e
}
while(e && n !=0 ){
if(n > 0){
if(e && e.nextElementSibling){
e = e.nextElementSibling;
}
else{//兼容IE浏览器
e = e.nextSibling;
//下面防止前面没有兄弟元素
while(e && e.nodeType != 1){
e = e.nextSibling;
}
}
n--;
}
else{
if(e.previousElementSibling){
e = e.previousElementSibling;
}
else{
e = e.previousSibling;
while(e&&e.nodeType != 1){
e = e.previousSibling;
}
}
n++;
}
}
return e
}
4.封装函数,实现children功能,最好哎原型链上编程
Element.prototype.selfchildren = function(){
var child = this.childNodes;
var len = child.length;
var i = 0;
var childrenList = {
'length': 0,
'push': Array.prototype.push
};
while(i<len){
if(child[i].nodeType == 1){
childrenList.push(child[i])
}
i++
}
return childrenList;
}
console.log(oDiv.selfchildren())
5.封装是否有元素子节点的方法
Element.prototype.hasChildren = function () {
var child = this.childNodes,
len = child.length;
for( var i = 0; i < len; i++){
if(child[i].nodeType == 1) {
return true;
}
}
return false;
}