Go to comments

JavaScript DOM节点类型

关系类的选择也有一个语法叫“节点树”,把关系拟成了一个数形结构


什么意思呢?

html 代码有一个树型结构,

最顶层的是 div 有两个孩子 span、div,这个孩子 div 还有一个孩子是 p 标签

<div>
  <span></span>
  <div>
    <p></p>
  </div>
</div>

接下来的方法就是基于这个树形结构所有的关系,把元素全部选出来

image.png

比如,

树形结构有父子关系、还有兄弟关系,左边兄弟关系、右边兄弟关系,

下面是遍历这个节点树的方法,总称叫遍历节点树


一、遍历节点树

1. parentNode  父节点(最顶端的 parentNode 为 #document

2. childNodes   子节点们

3. firstChild       第一个子节点

4. lastChild       最后一个子节点

5. nextSibling   后一个兄弟节点 

6. previousSibling  前一个兄弟节点


一个元素在 DOM 操作里叫一个节点(新词),元素节点、元素、标签指的都是一个人,

学了 DOM 之后名字就更加高大上了,以后管标签叫 DOM 元素,意思是可被 DOM 操作的元素就叫DOM元素。


1. parentNode

选中下面的 strong 元素

<div>
  <strong></strong>
  <span></span>
  <em></em>
</div>

<script>

  var oStrong = document.getElementsByTagName('strong')[0]; // 选中strong元素

</script>


选中 strong 元素之后,

这个 oStrong 代表一个 din 元素,就是一个 HTML 元素选出来一个 dom 的形式

这个 dom 形式的元素(dom对象),身上有很多属性和方法,其中有一个属性就是 parentNode(一切 dom 元素都有这些属性和方法)


oStrong.parentNode 存着的是 string 的父元素,它的父元素是 div,通过这种方法找到 div 元素

<div>
  <strong></strong>
  <span></span>
  <em></em>
</div>

<script>

  var oStrong = document.getElementsByTagName('strong')[0];
  console.log(oStrong.parentNode); // <div>...</div>

</script>


Strong.parentNode 代表了 div 元素,这个 div 也是 dom 元素,它身上也有个爹,它爹也是 parentNode

<div>
  <strong></strong>
  <span></span>
  <em></em>
</div>

<script>

  var Strong = document.getElementsByTagName('strong')[0];

  console.log(Strong.parentNode); // string元素的父元素的div 
  console.log(Strong.parentNode.parentNode); // div的父元素是body
  console.log(Strong.parentNode.parentNode.parentNode); // body的父元素是HTML
  console.log(Strong.parentNode.parentNode.parentNode.parentNode); // HTML的父元素是#document。
  console.log(Strong.parentNode.parentNode.parentNode.parentNode.parentNode); // document是顶层了,没有了返回null

</script>


parentNode 最顶层的父级节点是 #document

#document 是 HTML 它爹,

#document 包含 HTML,包含的关系就可以认为是一种父子关系


2. childNodes

一个元素找它的 parendNode 只能找到一个元素,

而找它的 chileNodes 孩子节点就不一定有多少个了


把下面 div 选出来,遍历节点树找 chileNodes

<div>
  <strong>
    <span>1</span>
  </strong>
  <span></span>
  <em></em>
</div>


<script>

  var oDiv = document.getElementsByTagName('div')[0];

  console.log(oDiv.childNodes); // NodeList(7) [text, strong, text, span, text, em, text]
  console.log(oDiv.childNodes.length); // 7

</script>

oDiv.chileNodes 找出来肯定是类数组


这个类数组有多少个节点?

直系的子元素就 3 个,然而它的长度是 7


为什么长度是 7 呢?

这里是遍历节点树,没说只有HTML节点算节点,childNodes 选的是 div 下面的所有子节点,节点的类型是五花八门的

image.png


节点的类型:

元素节点  —— 1

属性节点  —— 2

文本节点  —— 3

注释节点  —— 8

document  —— 9

DocumentFragment  ——  11 


首先看元素节点、文本节点、注释节点,把这三个先记住就行了,

现在 div 下的 childNodes 选择的是它的子节点们,div 的子节点们(oDiv.childNodes)一共有多少个?

<div>
  <!-- This is comment -->
  <strong></strong>
  <span></span>
</div>

<script>

  var oDiv = document.getElementsByTagName('div')[0];
  console.log(oDiv.childNodes); // NodeList(7) [text, comment, text, strong, text, span, text]

</script>

有 7 个子节点,div 的子节点们是并列结构的,并且有很多个

第一个是文本节点,

第二个是注释节点,

第三个是文本节点,

第四个是元素节点,

第五个是文本节点,

第六个是元素节点,

第七个是文本节点


再加一个文本 123,现在 div 有多少个节点?

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
</div>


<script>

  var oDiv = document.getElementsByTagName('div')[0];
  console.log(oDiv.childNodes); // NodeList(7) [text, comment, text, strong, text, span, text]

</script>

还是 7 个子节点,一定要分辩出节点的个数,节点是分不同类型的

image.png


3. firstChild

它能选择一个元素里面的第一个子节点

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
</div>


<script>

  var oDiv = document.getElementsByTagName('div')[0];
  console.log(oDiv.firstChild); // "123"

</script>

第一子节点是文本节点"123"

image.png


4. lastChild

oDiv.lastChild 是最后一个节点,

最后一个节点还是文本节点,只不过是空没有内容,所以返回的是 #text

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
</div>


<script>

  var oDiv = document.getElementsByTagName('div')[0];
  console.log(oDiv.lastChild); // #text

</script>


5. nextSibling

Sibling 是兄弟的意思,

nextSibling 是下一个兄弟节点

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
</div>


<script>

  var oStrong = document.getElementsByTagName('strong')[0]; // 选中strong元素。

  console.log(oStrong.nextSibling); // strong下一个兄弟节点是文本"#text"
  console.log(oStrong.nextSibling.nextSibling); // "#text"文本的下一个兄弟节点是"<span></span>"
  console.log(oStrong.nextSibling.nextSibling.nextSibling); // span的再下一个兄弟节点还是文本"#text"
  console.log(oStrong.nextSibling.nextSibling.nextSibling.nextSibling); // #text文本再下一个兄弟节点什么也没有了"null"

</script>


6. previousSibling

previous 是前一个的意思,

previousSibling 前一个兄弟节点

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
</div>


<script>

  var oStrong = document.getElementsByTagName('strong')[0];
  console.log(oStrong.nextSibling.previousSibling); // strong下一个的前一个是strong自己

</script>


上面是遍历节点树,下面也是遍历树,

但是遍历的是更加方便的树,叫做遍历元素节点树,这回的节点树基于真正的元素节点树


二、基于元素节点树的遍历

1. parentElement  返回当前元素的父元素节点(IE9以下不兼容)

2. children             只返回当前元素的元素子节点

3. node.childElementCount   当前元素节点的子元素节点个数(IE9以下不兼容)

4. firstElementChild     返回的是第一个元素节点(IE9以下不兼容)

5. lastElementChild      返回的是最后一个元素节点(IE9以下不兼容)

6. nextElementSibling  前一个兄弟元素节点(IE9以下不兼容)

7. previousElementSibling  返回后一个兄弟元素节点(IE9以下不兼容)


1. parentElement

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
</div>


<script>

  var oDiv = document.getElementsByTagName('div')[0];

  console.log(oDiv.parentElement); // div的父元素节点是body
  console.log(oDiv.parentElement.parentElement); // body的父元素节点是HTML
  console.log(oDiv.parentElement.parentElement.parentElement); // html的父元素节点是null

</script>

HTML 元素还有父节点吗?

#document 不叫元素它自成一个节点,#document 不是元素节点,所以 html 元素节的父点是 null

parentElement 和 parentNode 的区别就是 parentElement 不能到 #document


2. children

div 元素子节点只有两个

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
</div>


<script>

  var oDiv = document.getElementsByTagName('div')[0];

  console.log(oDiv.children); // HTMLCollection(2) [strong, span]
  console.log(oDiv.children[0]); // <strong></strong>
  console.log(oDiv.children[1]); // <span></span>
  console.log(oDiv.children[2]); // 没有节点返回undefined

</script>

children 和 childNodes 不一样,children 是元素子节点


3. childElementCount

它是一个非常没用的属性

node.childElementCount 直接等于 node.children.length

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
</div>


<script>

  var oDiv = document.getElementsByTagName('div')[0];
  console.log(oDiv.childElementCount); // 返回2,求的是div元素子元素节点的个数,建议不要记了

</script>


node.childElementCount 求当前元素节点的子元素子节点个数,建议直接用 node.children.length

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
</div>

<script>

  var oDiv = document.getElementsByTagName('div')[0];

  console.log(oDiv.childElementCount); // 2
  console.log(oDiv.children.length); // 2
  console.log(oDiv.childElementCount + ' == ' + oDiv.children.length); // 2 == 2

</script>


4. firstElementChild

div 的第一个元素子节点 div.firstElementChild


5. lastElementChild

div 的最后一个元素子节点 div.lastElementChild

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
</div>

<script>

  var oDiv = document.getElementsByTagName('div')[0];

  console.log(oDiv.firstElementChild); // <strong></strong>
  console.log(oDiv.lastElementChild); // <span></span>

</script>


6. nextElementSibling

下一个兄弟元素节点


7. previousElementSibling

前一个兄弟元素节点

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
  <em></em>
  <i></i>
  <b></b>
</div>

<script>

  var Strong = document.getElementsByTagName('strong')[0]; // 选出strong元素

  console.log(Strong.nextElementSibling); // strong下一个元素节点是<span></span>
  console.log(Strong.nextElementSibling.nextElementSibling); // span下一个元素节点是<em></em>
  console.log(Strong.nextElementSibling.nextElementSibling.previousElementSibling);// em的上一个元素节点回到<span></span>
  console.log(Strong.nextElementSibling.nextElementSibling.previousElementSibling.previousElementSibling); // 再previousElementSibling又回到strong了
  console.log(Strong.nextElementSibling.nextElementSibling.previousElementSibling.previousElementSibling.previousElementSibling); // 再previousElementSibling变成null

</script>


遍历节点树不区分元素不元素的,这些方法任何一个浏览器都好使,

但是基于元素节点树的这些方法除了 children 以外,都是 IE9 及 IE9 以下不兼容的,真正在开发的时候用的最多的是 children 方法


三、节点的四个属性

nodeName 元素的标签名,以大写形式表示,只读

nodeValue Text节点或Comment节点的文本内容,可读写

nodeType 该节点的类型,只读

attributes Element节点的属性集合

节点的一个方法 Node.hasChildNodes()

每一个节点基本上都有四个属性,什么是每一个节点?元素节点、文本节点……那些节点都有这四个属性


1. nodeName

比如 document 是个节点,它也有 nodeName 属性,打印出来是 #document 形式

console.log(document.nodeName); // #document


div 的子节点们,第一个兄弟子节点是 123 文本,文本的 nodeName 属性是 #text

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
  <em></em>
  <i></i>
  <b></b>
</div>


<script>

  var oDiv = document.getElementsByTagName('div')[0];

  console.log(oDiv.firstChild.nodeName); // #text

</script>


第二个兄弟子节点是注释,

注释节点的 nodeName 属性是 #comment

var oDiv = document.getElementsByTagName('div')[0];

console.log(oDiv.childNodes[1]); // <!-- This is comment -->

console.log(oDiv.childNodes[1].nodeName); // 注释接的nodeName属性是"#comment"


第 [3] 个元素子节点

元素节点的 nodeName 属性是 STRONG

var oDiv = document.getElementsByTagName('div')[0];

console.log(oDiv.childNodes[3].nodeName); // STRONG


nodeName 属性能区别出标签是什么名,返回的是一个字符串,只读取值不能写入,该属性不能赋值

var oDiv = document.getElementsByTagName('div')[0];

oDiv.childNodes[3].nodeName = 'abc'; // 把STRONG改成abc

console.log(oDiv.childNodes[3].nodeName); // 再访问返回还是STRONG


2. nodeValue

该属性不是所以的节点都有,只有文本节点(Text)和注释节点(Comment)有


div.childNodes[0] 第 0 位是文本节点,文本节点的 nodeValue 是 123

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
  <em></em>
  <i></i>
  <b></b>
</div>


<script>

  var oDiv = document.getElementsByTagName('div')[0];

  console.log(oDiv.childNodes[0].nodeValue); // 123

</script>


div.childNodes[0] 取值是123,然后赋值 234,页面上显示也变成 234 了

var oDiv = document.getElementsByTagName('div')[0];

oDiv.childNodes[0].nodeValue = 234; // 赋值234
console.log(oDiv.childNodes[0].nodeValue); // 再取值就变成234了
console.log(oDiv.childNodes[0]); // 这样取也是"234",只不过这样取得是节点的值

oDiv.childNodes[0].nodeValue   这样取的是内容,返回的是数字类型

oDiv.childNodes[0]                     这样取的是节点,返回的是字符串类型


div.childNodes[1] 第一位是注释,注释也有 nodeValue 属性,注释节点的 nodeValue 属性也是可以写入读取的

var oDiv = document.getElementsByTagName('div')[0];

console.log(oDiv.childNodes[1]); // <!-- This is comment -->


修改注释节点的 nodeValue 属性

var oDiv = document.getElementsByTagName('div')[0];

oDiv.childNodes[1].nodeValue = "That is comment"; // This is comment 改成 That is comment

console.log(oDiv.childNodes[1]); // 再看注释节点变成 <!-- That is comment -->


nodeValue属性只有"文本节点"和"注释节点"上面有,其它节点都没有,比如document节点返回null

console.log(document.nodeValue);  // null


3. nodeType

终于到重点了,这四个属性里面最有用的就是 nodeType


nodeType 是干嘛的?

它能帮我们分辨一个节点到底是什么节点,

比如现在给我们一个节点,不知道是什么节点,分辩出这是什么节点,只能通过 nodeType 属性

每一个节点都有 nodeType 属性,nodeType 属性里面装的是节点的类型。


节点类型:

元素节点    —— 1

属性节点    —— 2

文本节点    —— 3

注释节点    —— 8

document —— 9

DocumentFragment  ——  11(文档碎片节点) 


节点后面跟的数字是干什么的呢?

调用该节点的nodeType返回的就是这些对应的数


用 document 节点试一下,document.nodeType 返回 9

console.log(document.nodeType); // 9


div 第一个子节点是注释节点,注释节点的 nodeType 返回 8

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
  <em></em>
  <i></i>
  <b></b>
</div>

<script>

  var oDiv = document.getElementsByTagName('div')[0];

  console.log(oDiv.childNodes[1].nodeType); // 注释节点的返回 8
  console.log(oDiv.childNodes[0].nodeType); // 文本节点的返回 3
  console.log(oDiv.childNodes[3].nodeType); // 元素节点的返回 1

</script>


小例子

封装一个 retElementChild(node) 方法,

函数里面放一个参数 node 传一个 DOM 节点(node),把 node 里面所有的直接子元素节点放到一个数组里面返回,并且不允许用 children


思路,

不允许用 children,

把 node.childNodes 遍历一遍,把 nodeType 等于 1 的元素,放到一个数组里返回

<div>
  123
  <!-- This is comment -->
  <strong></strong>
  <span></span>
  <em></em>
  <i></i>
  <b></b>
</div>


<script>

  function retElementChild(node){
    var arr = [],
        child = node.childNodes,
        len = child.length;

    for(var i = 0; i < len; i++){
      if(child[i].nodeType === 1){
        arr.push(child[i])
      }
    }

    return arr;
  }

  var div = document.getElementsByTagName('div')[0];
  console.log(retElementChild(div));// (5) [strong, span, em, i, b]

</script>


方法再做的丰满些,返回更像系统的类数组

function retElementChild(node){
  var temp = { // 不用[]用类数组temp
      length : 0,
      push : Array.prototype.push
    },
    child = node.childNodes,
    len = child.length;

  for(var i = 0; i < len; i++){
    if(child[i].nodeType === 1){
      temp.push(child[i]);
    }
  }
  return temp;
}

var div = document.getElementsByTagName('div')[0];
console.log(retElementChild(div)); // {0: strong, 1: span, 2: em, 3: i, 4: b, length: 5, push: ƒ}


怎么让它更形象点,让它看起来就是一个数组?

加 splice 方法,有了 splice : Array.prototype.splice 看起来更像数组,在控制台上看长的跟更像数组了

function retElementChild(node){
  var temp = {
      length : 0,
      push : Array.prototype.push,
      splice : Array.prototype.splice // 有了splice看起来更像数组
    },
    child = node.childNodes,
    len = child.length;

  for(var i = 0; i < len; i++){
    if(child[i].nodeType === 1){
      temp.push(child[i]);
    }
  }
  return temp;
}

var div = document.getElementsByTagName('div')[0];
console.log(retElementChild(div)); // Object(5) [strong, span, em, i, b, push: ƒ, splice: ƒ]


4. attributes

特殊的   属性节点 —— 2   属性节点基本上是没什么用但是它存在


div 元素上面有两个属性节点,

1. class="demo"  在 js 里面认为是一个属性节点,节点类型是 2,

2. id="only"  也是一个属性节点,

<div id="only" class="demo"></div>


怎么在 js 里面查看属性节点呢?

div 有两个属性,

oDiv.attributes 就是这两个属性节点的集合,

两个属性放到一个类数组里面去,第 0 位就是一个属性节点

<div id="only" class="demo"></div>

<script>

  var oDiv = document.getElementsByTagName('div')[0];

  console.log(oDiv.attributes); // amedNodeMap {0: id, 1: class, id: id, class: class, length: 2}
  console.log(oDiv.attributes[0]); // id="only"
  console.log(oDiv.attributes[0].nodeType); // 这个属性节点的nodeType返回的类型是 2

</script>


属性节点的方法可以把属性节点的 值 和 名 取出来

var oDiv = document.getElementsByTagName('div')[0];

console.log(oDiv.attributes[0].value); // only
console.log(oDiv.attributes[0].name);  // id


属性节点能赋值吗?

var oDiv = document.getElementsByTagName('div')[0];

oDiv.attributes[0].value = 'abc';

console.log(oDiv); // 能赋值,id的值变了<div id="abc" class="demo"></div>


属性名 id 能改吗?

var oDiv = document.getElementsByTagName('div')[0];

oDiv.attributes[0].name = 'abc';

console.log(oDiv); // 属性名不能改<div id="abc" class="demo"></div>


属性值能改,属性名改不了,

但是我们不这么用,因为属性值可以通过 getAttribute 方法和 setAttribute 方法操作属性值


5 .Node.hasChildNodes() 方法

每个节点都有一个 Node.hasChildNodes() 方法,

hasChildNodes 翻译的意思是有没有子节点,返回结果不是 true 就是 false


下面 div 有子节点吗?

<div id="only" class="demo">
  <span></span>
</div>

<script>

  var oDiv = document.getElementsByTagName('div')[0];

  console.log(oDiv.hasChildNodes()); // 有子节点返回true

</script>


div里面就有注释,还有子节点吗?

还有节点返回 true,说的节点没说元素节点,没区别节点类型还是有子节点

<div id="only" class="demo">
  <!-- this is comment -->
</div>

<script>

  var oDiv = document.getElementsByTagName('div')[0];

  console.log(oDiv.hasChildNodes()); // true

</script>


div 里面都删除了,还有子节点吗?

还有子节点返回 true,div 里面不是空,虽然什么也没写也算文本,div 里面是文字分隔符文本

<div id="only" class="demo">

</div>

<script>

  var oDiv = document.getElementsByTagName('div')[0];

  console.log(oDiv.hasChildNodes()); // true

</script>


把这些都删了,现在还有子节点吗?

这种情况下才返回 false,但凡里面有个空格、回车返回的都不可能是 false

<div id="only" class="demo"></div>

<script>

  var oDiv = document.getElementsByTagName('div')[0];

  console.log(oDiv.hasChildNodes()); // false

</script>



Leave a comment 0 Comments.

Leave a Reply

换一张