Go to comments

JavaScript DOM节点类型

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


什么意思呢?

html代码有一个树型结构,最顶层的是div有两个孩子span、div,这个孩子div还有一个孩子是p标签

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

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

image.png

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

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

一、遍历节点树

一个元素在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代表一个DOM元素,就是一个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

节点的类型:


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

现在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

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

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


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

    console.log(oDiv.firstChild); // "123"
    
</script>

第一子节点oDiv.firstChild是文本节点"123"。

image.png

4、lastChild

oDiv.lastChild是最后一个节点

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


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

    console.log(oDiv.lastChild); // #text
    
</script>

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

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

<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

childElementCount是一个非常没用的属性

<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>

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

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

nextElementSibling下一个兄弟元素节点

7、previousElementSibling

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方法

三、节点的四个属性

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

1、nodeName

第一个属性是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

<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]); // <!-- This is comment -->
    
    console.log(oDiv.childNodes[1].nodeName); // 注释接的nodeName属性是"#comment"

</script>


元素子节点是第[3]个,元素节点的nodeName属性是STRONG

<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[3].nodeName); // STRONG

</script>


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

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


<script>

    var oDiv = document.getElementsByTagName('div')[0];
    
    oDiv.childNodes[3].nodeName = 'abc'; // 把STRONG改成abc
    
    console.log(oDiv.childNodes[3].nodeName); // 再访问返回还是"STRONG"

</script>

2、nodeValue

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

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


<script>

    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",只不过这样取得是节点的值
    
    // 页面上显示也变成234了

</script>


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

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


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

<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]); // <!-- This is comment -->


</script>


修改注释节点的nodeValue属性

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


<script>

    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 -->"

</script>


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

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

3、nodeType

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

nodeType是干嘛的?

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

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


节点类型:


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

调用该节点的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); // 注释节点的nodeType返回 8

    console.log(oDiv.childNodes[0].nodeType); // 文本节点的nodeType返回 3

    console.log(oDiv.childNodes[3].nodeType); // 元素节点的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>


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

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


<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: ƒ}

</script>


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

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

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


<script>

    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: ƒ]

</script>

4、attributes

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


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

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

id="only"  也是一个属性节点,怎么再js里面查看属性节点呢?

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


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>


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

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

<script>

    var oDiv = document.getElementsByTagName('div')[0];
    
    console.log(oDiv.attributes[0].value); // only
    
    console.log(oDiv.attributes[0].name);  // id
    
</script>


属性节点能赋值吗?

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


<script>

    var oDiv = document.getElementsByTagName('div')[0];
    
    oDiv.attributes[0].value = 'abc';
    
    console.log(oDiv); // 能赋值,id的值变了<div id="abc" class="demo"></div>
    
</script>


属性名id能改吗?

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


<script>

    var oDiv = document.getElementsByTagName('div')[0];
    
    oDiv.attributes[0].name = 'abc';
    
    console.log(oDiv); // <div id="abc" class="demo"></div>属性名不能改
    
</script>


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

但是我们不这么用,因为属性值可以通过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>


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

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

<script>

    var oDiv = document.getElementsByTagName('div')[0];
    
    console.log(oDiv.hasChildNodes()); // false
    
</script>

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



Leave a comment 0 Comments.

Leave a Reply

换一张