DOM 可以将任何 HTML 或 XML 文档描绘成一个由多层次节点构成的结构。
节点分为多种不同的类型,每种类型分别表示文档中不同的信息及标记。每个节点都拥有各自的特点、数据和方法,也与其他节点存在某种关系。
文档节点是每一个文档的根节点。在 HTML 中,文档节点只有一个子节点,即 html 元素,称为文档元素。文档元素是文档最外层的元素,每个文档只能有一个文档元素,在 HTML 页面中,文档元素始终是 html 元素。
1. Node 类型
DOM1 级定义了一个 Node 接口,该接口将由 DOM 中所有节点类型实现。
在 JavaScript 中,这个 Node 接口是作为 Node 类型实现的,除了 IE 外,其他所有浏览器中都可以访问到这个类型。
在一个 HTMl 文档中出现的所有东西都是节点
元素节点(HTML 标签)
文本节点(文字内容)
注释节点(注释内容)
文档节点(document)
…
JavaScript 中所有节点类型都继承自 Node 类型,因此所有节点都共享着 Node 类型的基本属性和方法。
节点类型由在 Node 类型中定义的 12 个数值常量来表示:
Node.ELEMENT_NODE:1
Node.ATTRIBUTE_NODE:2
Node.TEXT_NODE:3
Node.CDATA_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
注意:由于 IE 不支持访问 Node,所以为了确保浏览器的兼容,在判断节点类型时,最好还是将
nodeType
属性与数字值进行比较
2. 常用节点类型与属性
每一种类型的节点都会有一些属性区分自己的特性和特征。
nodeType
:节点类型nodeName
:节点名称nodeValue
:节点值
常用:主要记住 nodeType 的值
元素节点
nodeType:1
nodeName:大写标签名
nodeValue:null
文本节点
nodeType: 3
nodeName:’#text’
nodeValue:文本内容
在标准浏览器中会把空格、换行都当作文本节点处理
注释节点
nodeType:8
nodeName:’#common’
nodeValue:注释内容
文档节点
nodeType: 9
nodeName:’#document’
nodeValue:null
3. 节点关系
文档中所有节点之间都存在一定的关系,可以用传统的家族关系来描述,将文档树比喻成家谱。
每个节点都有一些属性,存储着关系指针。这些关系指针指向特定关系的节点,是只读的。
父节点
parentNode
获取当前节点唯一的父亲节点
每一个节点都有一个 parentNode
属性,指向文档树中的父节点。包含在 childNodes
列表中的所有节点都有相同的父节点。
子节点
childNodes
获取当前元素的所有子节点
子节点:只获取儿子级别的
所有:包含元素节点、文本节点等
每个节点都有一个 childNodes
属性,其中保存的是一个 NodeList 对象。
NodeList 是一个类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。NodeList 对象的独特之处在于,它实际上是基于 DOM 结构动态执行查询的结果,因此 DOM 结构的变化能够自动反映在 NodeList 对象中。
可以通过方括号,也可以使用 item()
方法,来访问 NodeList 中的节点;通过 length
属性可以访问其长度。
var firstChild = someNode.childNodes[0];
var secondChild = someNode.childNodes.item(1);
var count = someNode.childNodes.length;
children
获取当前元素所有的元素子节点
在 IE6~8 中会把注释节点也当作元素节点获取到,所以兼容性不好。需要自己编写兼容方法,不过,以后也不需要考虑 IE 低版本的兼容了。
兄弟节点
previousSibling
获取当前节点的上一个兄弟节点(哥哥)
可能是元素也可能是文本等
previousElementSibling
获取上一个兄弟(哥哥)元素的节点(不兼容 IE6~8)
nextSibling
获取当前节点的下一个兄弟节点(弟弟)
可能是元素也可能是文本等
nextElementSibling
获取下一个兄弟(弟弟)元素的节点(不兼容 IE6~8)
首尾节点
firstChild
获取当前元素的第一个子节点(可能是元素也可能是文本等)
firstElementchild
获取当前元素的第一个元素子节点(不兼容 IE6~8)
lastChild
获取当前元素的最后一个子节点(可能是元素也可能是文本等)
lastElementChild
获取当前元素的最后一个元素子节点(不兼容 IE6~8)
ownerDocument
所有节点都有这个属性,指向表示整个文档的文档节点
4. 兼容方法编写
需求一:获取当前元素的所有元素子节点
基于
children
不兼容 IE 低版本浏览器(会把注释也当作元素节点)
首先拿到所有子节点
遍历这些子节点,判断 nodeType 是否为 1,即元素节点,将其放入数组中
/* 基本注释编写:
* children: get all the element subnodes of the current element
* @parameter
* curEle: [object] current element
* @return
* [Array] all the element nodes
* by team on 2018/07/11 21:32
* update ...
*/
function children(curEle) {
// 1. 首先获取当前元素下所有子节点
var nodeList = curEle.childNodes,
result = [];
// 2. 遍历这些节点,然后遍历这些节点,筛选出元素节点,把筛选出来的结果单独存储起来
for (var i = 0; i < nodeList.length; i++) {
var item = nodeList[i];
if (item.nodeType === 1) {
result.push(item);
}
}
return result;
}
需求二:获取去当前元素的上一个兄弟元素节点(哥哥)
基于
previousElementSibling
不兼容 IE 低版本浏览器(会把注释也当作元素节点)
/*
* prev: get the last elder brother element node of the current element
* @parameter
* curEle: [object] current element
* @return
* [object] the element node
* by destiny 2018/7/11 21:45
*/
function prev(curELe) {
//=> 先找到当前元素的哥哥节点
//=> 判断是否为元素节点,不是的话,基于哥哥,找哥哥的上一个哥哥节点,直到找到元素节点或者已经没有哥哥了(说明当前元素就是老大)则结束查找
var pre = curEle.previousSibling;
while(pre && pre.nodeType !== 1) {
/*
* pre && pre.nodeType !== 1
* pre 是验证还有没有,这样写代表有,没有 pre 是 null
* pre.nodeType 是验证是否为元素节点
*/
pre = pre.previousSibling;
}
return pre;
}
其他需求拓展
- next 获取下一个弟弟元素节点
/*
* next: get next brother element node of the current element
* @parameter
* curELe: [object]
* @return
* [object] the element node
* by destiny 2018/07/11 23:18
*/
function next(curEle) {
var nt = curEle.nextSibling;
while (nt && nt.nodeType !== 1) {
nt = nt.nextSibling;
}
return nt;
}
- prevAll 获取所有哥哥元素节点
/*
* pre: get all the elder brother element nodes of the current element
* @parameter
* curEle: [object] current element
* @return
* [Array] all the element nodes
* by destiny 2018/7/11 21:45
*/
function prevAll(curELe) {
//=> 先找到当前元素的哥哥节点,
//=> 如果存在哥哥节点,则判断是否为元素节点,是的话,单独存储起来
//=> 不是的话,基于哥哥,找哥哥的上一个哥哥节点,直到已经没有哥哥节点了,则结束查找
var pre = curEle.previousSibling,
result = [];
while(pre) {
if (pre.nodeType === 1) {
result.push(pre);
}
pre = pre.previousSibling;
}
return result;
}
- nextAll 获取所有弟弟元素节点
/*
* next: get all the next brother element nodes of the current element
* @parameter
* curELe: [object]
* @return
* [Array] all the element nodes
* by destiny 2018/07/11 23:18
*/
function nextAll(curEle) {
var nt = curEle.nextSibling,
result = [];
while (nt) {
if (nt.nodeType === 1) {
result.push(nt);
}
nt = nt.nextSibling;
}
return result;
}
siblings 获取所有兄弟元素节点
先获取当前节点的父节点
获取父节点的所有子节点
遍历这些节点,判断是否是元素节点,如果是并且不是当前节点,则单独存储起来
function sibings(ele) {
var parent = ele.parentNode;
var sons = children(parent);
var result = [];
sons.forEach(function(item) {
if (item !== ele) {
result.push(item);
}
});
return result;
}
index 获取当前元素的索引
获取其父元素的所有子元素
把这些子元素与当前元素相同标签的存储到一个数组中
返回当前元素在数组中的位置
function index(curEle) {
var parent = curEle.parentNode;
var sons = children(parent);
var nodeName = curEle.nodeName;
var ary = [];
sons.forEach(function (item) {
if (item.nodeName === nodeName) {
ary.push(item);
}
});
return ary.indexOf(curEle);
}