DOM 可以将任何 HTML 或 XML 文档描绘成一个由多层次节点构成的结构。

节点分为多种不同的类型,每种类型分别表示文档中不同的信息及标记。每个节点都拥有各自的特点、数据和方法,也与其他节点存在某种关系。

文档节点是每一个文档的根节点。在 HTML 中,文档节点只有一个子节点,即 html 元素,称为文档元素。文档元素是文档最外层的元素,每个文档只能有一个文档元素,在 HTML 页面中,文档元素始终是 html 元素

1. Node 类型

DOM1 级定义了一个 Node 接口,该接口将由 DOM 中所有节点类型实现。

在 JavaScript 中,这个 Node 接口是作为 Node 类型实现的,除了 IE 外,其他所有浏览器中都可以访问到这个类型。

在一个 HTMl 文档中出现的所有东西都是节点

  • 元素节点(HTML 标签)
  • 文本节点(文字内容)
  • 注释节点(注释内容)
  • 文档节点(document)

JavaScript 中所有节点类型都继承自 Node 类型,因此所有节点都共享着 Node 类型的基本属性和方法。

节点类型由在 Node 类型中定义的 12 个数值常量来表示:

  1. Node.ELEMENT_NODE:
  2. Node.ATTRIBUTE_NODE:
  3. Node.TEXT_NODE:
  4. Node.CDATA_NODE:
  5. Node.ENTITY_REFERENCE_NODE:
  6. Node.ENTITY_NODE:
  7. Node.PROCESSING_INSTRUCTION_NODE:
  8. Node.COMMENT_NODE:
  9. Node.DOCUMENT_NODE:
  10. Node.DOCUMENT_TYPE_NODE:
  11. Node.DOCUMENT_FRAGMENT_NODE:
  12. Node.NOTATION_NODE:

注意:由于 IE 不支持访问 Node,所以为了确保浏览器的兼容,在判断节点类型时,最好还是将 nodeType 属性与数字值进行比较

2. 常用节点类型与属性

每一种类型的节点都会有一些属性区分自己的特性和特征。

  • nodeType:节点类型
  • nodeName:节点名称
  • nodeValue:节点值

常用:主要记住 nodeType 的值

2.1元素节点

  • nodeType:1
  • nodeName:大写标签名
  • nodeValue:null

    2.2文本节点

  • nodeType: 3

  • nodeName:’#text’
  • nodeValue:文本内容

在标准浏览器中会把空格、换行都当作文本节点处理

2.3注释节点

  • nodeType:8
  • nodeName:’#common’
  • nodeValue:注释内容

    2.4文档节点

  • nodeType: 9

  • nodeName:’#document’
  • nodeValue:null

    2.5节点类型小结

    1. 元素节点是HTML标签元素,元素节点主要提供了对元素标签名、子节点及属性的访问。
    2. 属性节点:一般是元素节点的属性,每一个 XML 属性算是一个属性节点。
    3. 文本节点:是DOM中用于呈现文本的部分,一般被包含在元素节点的开闭合标签内部。
    4. 文档节点是DOM中用于呈现整个文档的部分,文档节点的 childNodes 属性中包含了它的所有子节点,这些子节点可能是元素、文本、注释、处理指令节点。

如下图例:
DOM节点 - 图1
可以得出:
(1)元素节点:一般是拥有一对开闭合标签的元素整体,例如上图中的

这一部分都属于一个元素节点。
(2)属性节点:一般是元素节点的属性,比如上图中的“宽度”属性 也就是width=”80px”这一部分。
(3)文本节点:DOM中用于呈现文本的部分,如上图中的 “点我” ,一般被包含在元素节点的开闭合标签内部。

3. 节点关系

文档中所有节点之间都存在一定的关系,可以用传统的家族关系来描述,将文档树比喻成家谱。

DOM节点 - 图2
每个节点都有一些属性,存储着关系指针。这些关系指针指向特定关系的节点,是只读的。

3.1 父节点

parentNode

获取当前节点唯一的父亲节点
每一个节点都有一个 parentNode 属性,指向文档树中的父节点。包含在 childNodes 列表中的所有节点都有相同的父节点。

3.2 子节点

childNodes

获取当前元素的所有子节点

  • 子节点:只获取儿子级别的
  • 所有:包含元素节点、文本节点等

每个节点都有一个 childNodes 属性,其中保存的是一个 NodeList 对象。

NodeList 是一个类数组对象,用于保存一组有序的节点,可以通过位置来访问这些节点。NodeList 对象的独特之处在于,它实际上是基于 DOM 结构动态执行查询的结果,因此 DOM 结构的变化能够自动反映在 NodeList 对象中。

可以通过方括号,也可以使用 item() 方法,来访问 NodeList 中的节点;通过 length 属性可以访问其长度。

  1. var firstChild = someNode.childNodes[0];
  2. var secondChild = someNode.childNodes.item(1);
  3. var count = someNode.childNodes.length;

children

获取当前元素所有的元素子节点
在 IE6~8 中会把注释节点也当作元素节点获取到,所以兼容性不好。需要自己编写兼容方法,不过,以后也不需要考虑 IE 低版本的兼容了。

3.3 兄弟节点

previousSibling

获取当前节点的上一个兄弟节点(哥哥)
可能是元素也可能是文本等

previousElementSibling

获取上一个兄弟(哥哥)元素的节点(不兼容 IE6~8)


nextSibling

获取当前节点的下一个兄弟节点(弟弟)
可能是元素也可能是文本等

nextElementSibling

获取下一个兄弟(弟弟)元素的节点(不兼容 IE6~8)

3.4 首尾节点

firstChild

获取当前元素的第一个子节点(可能是元素也可能是文本等)

firstElementchild

获取当前元素的第一个元素子节点(不兼容 IE6~8)

lastChild

获取当前元素的最后一个子节点(可能是元素也可能是文本等)

lastElementChild

获取当前元素的最后一个元素子节点(不兼容 IE6~8)

3.5 ownerDocument

所有节点都有这个属性,指向表示整个文档的文档节点

4. 兼容方法编写

需求一:获取当前元素的所有元素子节点

基于 children 不兼容 IE 低版本浏览器(会把注释也当作元素节点)

  1. 首先拿到所有子节点
  2. 遍历这些子节点,判断 nodeType 是否为 1,即元素节点,将其放入数组中
    1. /* 基本注释编写:
    2. * children: get all the element subnodes of the current element
    3. * @parameter
    4. * curEle: [object] current element
    5. * @return
    6. * [Array] all the element nodes
    7. * by team on 2018/07/11 21:32
    8. * update ...
    9. */
    10. function children(curEle) {
    11. // 1. 首先获取当前元素下所有子节点
    12. var nodeList = curEle.childNodes,
    13. result = [];
    14. // 2. 遍历这些节点,然后遍历这些节点,筛选出元素节点,把筛选出来的结果单独存储起来
    15. for (var i = 0; i < nodeList.length; i++) {
    16. var item = nodeList[i];
    17. if (item.nodeType === 1) {
    18. result.push(item);
    19. }
    20. }
    21. return result;
    22. }

    需求二:获取去当前元素的上一个兄弟元素节点(哥哥)

    基于 previousElementSibling不兼容 IE 低版本浏览器(会把注释也当作元素节点)

  1. /*
  2. * prev: get the last elder brother element node of the current element
  3. * @parameter
  4. * curEle: [object] current element
  5. * @return
  6. * [object] the element node
  7. * by destiny 2018/7/11 21:45
  8. */
  9. function prev(curELe) {
  10. //=> 先找到当前元素的哥哥节点
  11. //=> 判断是否为元素节点,不是的话,基于哥哥,找哥哥的上一个哥哥节点,直到找到元素节点或者已经没有哥哥了(说明当前元素就是老大)则结束查找
  12. var pre = curEle.previousSibling;
  13. while(pre && pre.nodeType !== 1) {
  14. /*
  15. * pre && pre.nodeType !== 1
  16. * pre 是验证还有没有,这样写代表有,没有 pre 是 null
  17. * pre.nodeType 是验证是否为元素节点
  18. */
  19. pre = pre.previousSibling;
  20. }
  21. return pre;
  22. }

其他需求拓展

next 获取下一个弟弟元素节点

  1. /*
  2. * next: get next brother element node of the current element
  3. * @parameter
  4. * curELe: [object]
  5. * @return
  6. * [object] the element node
  7. * by destiny 2018/07/11 23:18
  8. */
  9. function next(curEle) {
  10. var nt = curEle.nextSibling;
  11. while (nt && nt.nodeType !== 1) {
  12. nt = nt.nextSibling;
  13. }
  14. return nt;
  15. }

prevAll 获取所有哥哥元素节点

  1. /*
  2. * pre: get all the elder brother element nodes of the current element
  3. * @parameter
  4. * curEle: [object] current element
  5. * @return
  6. * [Array] all the element nodes
  7. * by destiny 2018/7/11 21:45
  8. */
  9. function prevAll(curELe) {
  10. //=> 先找到当前元素的哥哥节点,
  11. //=> 如果存在哥哥节点,则判断是否为元素节点,是的话,单独存储起来
  12. //=> 不是的话,基于哥哥,找哥哥的上一个哥哥节点,直到已经没有哥哥节点了,则结束查找
  13. var pre = curEle.previousSibling,
  14. result = [];
  15. while(pre) {
  16. if (pre.nodeType === 1) {
  17. result.push(pre);
  18. }
  19. pre = pre.previousSibling;
  20. }
  21. return result;
  22. }

nextAll 获取所有弟弟元素节点

  1. /*
  2. * next: get all the next brother element nodes of the current element
  3. * @parameter
  4. * curELe: [object]
  5. * @return
  6. * [Array] all the element nodes
  7. * by destiny 2018/07/11 23:18
  8. */
  9. function nextAll(curEle) {
  10. var nt = curEle.nextSibling,
  11. result = [];
  12. while (nt) {
  13. if (nt.nodeType === 1) {
  14. result.push(nt);
  15. }
  16. nt = nt.nextSibling;
  17. }
  18. return result;
  19. }

siblings 获取所有兄弟元素节点

  • 先获取当前节点的父节点
  • 获取父节点的所有子节点
  • 遍历这些节点,判断是否是元素节点,如果是并且不是当前节点,则单独存储起来

    1. function sibings(ele) {
    2. var parent = ele.parentNode;
    3. var sons = children(parent);
    4. var result = [];
    5. sons.forEach(function(item) {
    6. if (item !== ele) {
    7. result.push(item);
    8. }
    9. });
    10. return result;
    11. }

    index 获取当前元素的索引

  • 获取其父元素的所有子元素

  • 把这些子元素与当前元素相同标签的存储到一个数组中
  • 返回当前元素在数组中的位置
    1. function index(curEle) {
    2. var parent = curEle.parentNode;
    3. var sons = children(parent);
    4. var nodeName = curEle.nodeName;
    5. var ary = [];
    6. sons.forEach(function (item) {
    7. if (item.nodeName === nodeName) {
    8. ary.push(item);
    9. }
    10. });
    11. return ary.indexOf(curEle);
    12. }