获取 和操作 DOM 节点
DOM 节点也会被称为 DOM 元素。
想要操作 DOM 节点,就必须先 获取 到 DOM 节点。
1. 获取 DOM 节点
获取 DOM 节点的方式有很多,这里例举几个常用的,所有的 DOM 元素都具有以下 方法 :
element.getElementById element.getElementByName element.getElementsByTagName element.getElementsByClassName element.querySelector element.querySelectorAll
1.1 element.getElementById
返回对拥有指定 id 的第 一个 对象的引用。
element.getElementById 是指去 element 节点下根据 id 查找子节点。
通常在程序开始前,没有主动去 获取 过节点,这个时候会使用根节点 document 来进行查找。
< div id = " html-element " > 我是 一个 元素 </ div > < script > var element = document . getElementById ( 'html-element' ) ; element . innerHTML = '<a href="//imooc.com">我变成了超 链接 </a>' ; </ script >
在使用 JavaScript 操作 DOM 节点的时候,也会把 DOM 节点称为 DOM 对象 ,以契合编程中 对象 的概念,更好理解。
以上例子通过 document.getElementById 获取 id 为 html-element 的 DOM 节点,并通过 修改 innerHTML 属性 ,将这个节点的 内容 进行了 修改 。
1.2 element.getElementByName
返回带有指定 名称 的对象集合。
element.getElementByName 是通过元素的 name 属性 进行查找的,过去操作表单的时候会经常用到。
< form > < div > < label > < input type = " check Box " name = " skill " checked = " checked " value = " JavaScript " > JavaScript </ label > < label > < input type = " check Box " name = " skill " value = " c++ " > C++ </ label > < label > < input type = " check Box " name = " skill " checked = " checked " value = " Java " > Java </ label > </ div > </ form > < div id = " result " > </ div > < script > var check Box es = document . getElementsByName ( 'skill' ) ; var skills = [ ] ; check Box es . forEach ( function ( check Box ) { if ( check Box . getAttribute ( 'checked' ) ) { skills . push ( check Box . value ) ; } } ) ; document . getElementById ( 'result' ) . innerHTML = '选中的技能:' + skills . join ( '、' ) ; </ script >
通过 getElementsByName 获取 到的是 DOM 节点的集合,需要注意的是,这个集合不是数组类型的,而是 NodeList ,其不具备数组的 map 、 filter 等 方法 ,但是具备 forEach 方法 。
Tips:IE 和早期浏览器的 NodeList 是没有 forEach 方法 的,具体版本可以通过 Can I Use 查看。
1.3 element.getElementsByTagName
返回带有指定 标签 名的对象集合。
element.getElementsByTagName 是通过 标签 名 获取 DOM 节点的,返回的也是 一个 集合。
< div > < p > 我是第 一个 段落。 </ p > < p > 我是第二个段落。 </ p > < p > 我是第三个段落。 </ p > < p > 我是第四个段落。 </ p > < p > 我是第五个段落。 </ p > </ div > < div id = " result " style =" color : #4caf50 ; " > </ div > < script > var pList = document . getElementsByTagName ( 'p' ) ; var res = [ ] ; var i , len ; for ( i = , len = pList . length ; i < len ; i ++ ) { res . push ( pList [ i ] . innerText ) ; } document . getElementById ( 'result' ) . innerHTML = '所有 p 标签 的 内容 :<br>' + res . join ( '<br>' ) ; </ script >
此 方法 返回值的类型是 HTMLCollection ,不是 NodeList ,没有 forEach 方法 。
可以使用 for 循环对返回值进行遍历。
Tips: 特别要注意,此 方法 为 getElement s ByTagName,前往不要忘记有个 s 。
1.4 element.getElementsByClassName
返回 一个 包含了所有指定类名的子元素的类数组对象。
element.getElementsByClassName 通过元素的类名来 获取 DOM 节点。
< div > < div class = " odd " > 1 </ div > < div class = " even " > 2 </ div > < div class = " odd " > 3 </ div > < div class = " even " > 4 </ div > < div class = " odd " > 5 </ div > < div class = " even " > 6 </ div > </ div > < div id = " result " > </ div > < script > var odd = document . getElementsByClassName ( 'odd' ) ; var res = [ ] ; var i , len ; for ( i = , len = odd . length ; i < len ; i ++ ) { res . push ( odd [ i ] . innerText ) ; } var resultElement = document . getElementById ( 'result' ) ; resultElement . innerHTML = '所有奇数:<br>' + res . join ( '<br>' ) ; </ script >
与 getElementsByTagName 返回值类型相同,此 方法 返回类型也是 HTMLCollection 。
Tips:注意,getElement s ByTagName 中也有 s 。同时此 方法 也 不支持 IE8。
1.5 element.querySelector
文档对象模型 Document 引用的 querySelector () 方法 返回文档中与指定选择器或选择器组匹配的第 一个 html 元素 Element 。 如果找不到匹配项,则返回 null 。
element.querySelector 是 获取 DOM 节点最常用的 方法 之一,可以传入 CSS 选择器来匹配 获取 DOM 节点。
如使用 CSS 在给 id 为 tip 的元素设置红色字体样式的时候,选择器使用的是 #tip 。
#tip { color : red ; }
使用 element.querySelector 获取 id 为 tip 的元素,传入的参数也是 #tip ,与 CSS 选择器一致。
< div > < div id = " tip " > 今日大甩卖!!一双袜子 < strong > 三 </ strong > 块!三双袜子只要 < strong > 十 </ strong > 块!! </ div > </ div > < script > var tip = document . querySelector ( '#tip' ) ; tip . style . color = 'red' ; </ script >
通过设置 style 下的 color 属性 ,可以 更改字体 颜色。通过 style 设置的样式都是内联样式。
即便传入的选择器能匹配到多个 DOM 对象,此 方法 也只会返回 一个 DOM 对象。
1.6 element.querySelectorAll
返回与指定的选择器组匹配的文档中的元素列表 (使用深度优先的先序遍历文档的节点)。返回的对象是 NodeList 。
此 方法 传入的参数与 querySelector 一致,但会返回匹配到的所有 DOM 对象。
< ul > < li > 我是列表1 </ li > < li > 我是列表2 </ li > < li > 我是列表3 </ li > < li > 我是列表4 </ li > </ ul > < button class = " change " > 变! </ button > < script > var lis = document . querySelectorAll ( 'li' ) ; var btn = document . querySelector ( '.change' ) ; btn . addEventListener ( 'click' , function ( ) { lis . forEach ( function ( li , index ) { li . innerText = index ; } ) ; } ) ; </ script >
element.querySelectorAll 返回的也是 一个 NodeList 。
2. 操作 DOM 节点
到目前为止已经做了许多 DOM 操作了,如使用 innerText 修改 文本,使用 style 修改 样式,这些其实都是在操作 DOM。
这里列举两个常用的操作。
2.1 修改 class 属性
修改 节点的 class 属性 ,这个操作频率是非常高的。
如使用 class 来控制元素的 显示 与隐藏。
< style > .show { dis play : block ; } .hidden { dis play : none ; } </ style > < p class = " tip show " > 我要早睡早起,不能再熬夜了。 </ p > < button class = " toggle " > 切换 </ button > < script > var toggleBtn = document . querySelector ( '.toggle' ) ; var tipEle = document . querySelector ( '.tip' ) ; toggleBtn . addEventListener ( 'click' , function ( ) { var className = tipEle . className ; if ( className . indexOf ( 'show' ) > - ) { tipEle . className = 'tip hidden' ; return ; } tipEle . className = 'tip show' ; } ) ; </ script >
通过 DOM 节点的 className 属性 ,来控制 class。
2.2 设置 / 获取 其他 属性
修改 class 也属于这个场景,但使用 className 更为频繁,所以单独拿出来介绍。
节点的许多状态是使用 属性 表示的,如 复选框 是否选中,就是由 checked 属性 决定。
< label > < input type = " check Box " class = " check Box " > 爱我别走 </ label > < div style =" margin-top : px ; " > < button class = " toggle " > 切换! </ button > </ div > < script > var check Box = document . querySelector ( '.check Box ' ) ; var toggleBtn = document . querySelector ( '.toggle' ) ; toggleBtn . onclick = function ( ) { var checked = check Box . getAttribute ( 'checked' ) ; if ( checked ) { check Box . removeAttribute ( 'checked' , '' ) ; } else { check Box . setAttribute ( 'checked' , 'checked' ) ; } } ; </ script >
getAttribute 方法 就可以获得某个 属性 的值。
setAttribute 用于给 属性 设置 属性 值。
removeAttribute 则是将 属性 从元素上移除。
这三个 方法 可以用于元素的任意 属性 , 包括 class 。
3. 其他
3.1 将集合转化为数组
通过几种 获取 DOM 节点的 方法 的返回值可以发现,当要 获取 多个 DOM 节点组成的集合的时候,返回的都不是数组。
@H_83_2 301 @ < div > < ul > < li > 1 </ li > < li > 2 </ li > < li > 3 </ li > </ ul > </ div > < script > var lis = document . querySelectorAll ( 'li' ) ; var filtered = lis . filter ( function ( li ) { return Number ( li . innerText % ) ; } ) ; </ script >
如使用上述例子对 获取 到的所有 DOM 节点 用 filter 方法 进行过滤是会报错的。
这个时候就需要通过一些方式,来获得由 DOM 节点组成的数组。
3.1.1 使用数组的 slice 方法
这里并不是让 DOM 节点的集合去 调用 slice 方法 ,而是利用 slice 方法 来将 DOM 节点的集合转化为数组。
< div > < ul > < li > i </ li > < li > lov e </ li > < li > imooc </ li > </ ul > </ div > < script > var lisColl = document . querySelectorAll ( 'li' ) ; var lisArray1 = [ ] . slice . call ( lisColl ) ; var lisArray2 = Array . prototype . slice . call ( lisColl ) ; var map = function ( li ) { return li . innerText ; } ; console . log ( lisArray1 . map ( map ) . join ( ' ' ) ) ; // 输出 :i lov e imooc console . log ( lisArray2 . map ( map ) . join ( ' ' ) ) ; // 输出 :i lov e imooc </ script >
slice 方法 可以接收两个参数,分别是起始下标和结束下标,其作用是浅复制起始下标到结束下标的所有项,然后产生 一个 新数组返回,如果不提供参数,则直接浅复制所有项,形成 一个 新数组返回。
使用 [].slice.call(类数组) 或 Array.prototype.slice.call(类数组) 即可将 一个 类数组转化为数组。
通过在控制台观察 NodeList 和 HTMLCollection 类型,可以发现他们是符合类数组特性的。
所以就可以通过这种方式来转化成数组。
数组的 slice 方法 在执行的时候内部是使用循环来操作数组项的,所以操作 一个 类数组不会出现问题,使用 call 方法 将操作的数组指定为传入的伪数组,就达到了将类数组转化为数组的目的。
3.1.2 将数组的原型 方法 挂载到目标对象的原型上
< div > < ul > < li > i </ li > < li > lov e </ li > < li > imooc </ li > </ ul > </ div > < script > NodeList . prototype . join = Array . prototype . join ; // 提供 join 方法 var lisColl = document . querySelectorAll ( 'li' ) ; lisColl . __proto__ . map = Array . prototype . map ; // 提供 map 方法 var map = function ( li ) { return li . innerText ; } ; console . log ( lisColl . map ( map ) . join ( ' ' ) ) ; // 输出 :i lov e imooc </ script >
通过在 NodeList 原型上提供数组的 方法 ,就可以直接进行 方法 的 调用 。
其原理可以参考原型相关章节。
3.1.3 使用 for 循环
使用 for 循环遍历集合,将每一项放入新数组。
< div > < ul > < li > i </ li > < li > lov e </ li > < li > imooc </ li > </ ul > </ div > < script > var lisColl = document . querySelectorAll ( 'li' ) ; var lis = [ ] ; var i , len ; for ( i = , len = lisColl . length ; i < len ; i ++ ) { lis . push ( lisColl [ i ] ) ; } var str = lis . map ( function ( li ) { return li . innerText } ) . join ( ' ' ) ; console . log ( str ) ; // 输出 :i lov e imooc </ script >
这种方式没有特殊情况通常不会去使用。
3.1.4 使用 Array.from 方法
Array.from() 方法 从 一个 类似数组或可迭代 对象创建 一个 新的,浅拷贝的数组实。(MDN)
Array.from 可以将 一个 类数组转化为数组。
var arrayLike = { : '9' , : '9' , : '6' , : ' bye!' , length : , } ; var str = Array . from ( arrayLike ) . join ( '' ) ; console . log ( str ) ; // 输出 :996 bye!
该 方法 由 ES2015 提供,所以旧版的浏览器 不支持 。
3.1.5 使用扩展运算符
扩展运算符也是由 ES2015 提供的。
< div > < ul > < li > i </ li > < li > lov e </ li > < li > imooc </ li > </ ul > </ div > < script > var lisColl = document . querySelectorAll ( 'li' ) ; var arr = [ ... lisColl ] ; console . log ( Array . isArray ( lisColl ) ) ; // 输出 :false console . log ( Array . isArray ( arr ) ) ; // 输出 :true </ script >
... 即扩展运算符,根据使用场景,他还能被作为剩余参数操作符使用。
通过 Array.isArray 可以判断 一个 值是不是数组。
3.2 null 判断
当 获取 节点的 方法 没有匹配到任何元素的时候,是可能返回 null 或者 空集合的。
var el = document . querySelector ( '#dfsafds' ) ; var elList = document . querySelectorAll ( '.dfsafds' ) ; el . innerHTML = '<p>我写的 代码 从来不会报错!</p>' ; elList [ ] . innerHTML = '<p>我写的 代码 从来不会报错!</p>' ; // Cannot set property 'innerHTML' of null
碰到这种情况,上述 代码 就报错了,假如后面 代码 存在渲染逻辑,则不会再继续执行,最后换来一份 辞退报告 。
所以在没有把握的情况下一定要进行空判断。
var el = document . querySelector ( '#dfsafds' ) ; if ( el ) { el . innerHTML = '<p>我写的 代码 从来不会报错!</p>' ; } else { console . log ( '节点还没渲染出来' ) ; }
或者使用 try ... catch ... 。
var el = document . querySelector ( '#dfsafds' ) ; try { el . innerHTML = '<p>我写的 代码 从来不会报错!</p>' ; } catch ( err ) { console . error ( err ) ; console . log ( '节点还没渲染出来' ) ; }
4. 小结
操作 DOM 是前端程序员的基本功,也是编写网页的重要知识之一。
获取 DOM 节点的 方法 有很多,部分 方法 返回的是 NodeList 或 HTMLCollection 类型,而不是数组,不能像操作数组一样操作这些集合,转换成数组可以更方便的利用数组的原生 方法 对其进行操作。
操作节点的时候,特别是动态渲染的节点,需 要做 空判断,防止程序报错中断执行。
DOM 与事件 ? ?DOM 和 JavaScript 的关系声明:本文来自网络,不代表【好得很程序员自学网】立场,转载请注明出处:http://haodehen.cn/did92428