获取和操作 DOM 节点
想要操作 DOM 节点,就必须先获取到 DOM 节点。
1. 获取 DOM 节点
获取 DOM 节点的方式有很多,这里例举几个常用的,所有的 DOM 元素都具有以下方法:
- element.getElementById
- element.getElementByName
- element.getElementsByTagName
- element.getElementsByClassName
- element.querySelector
- element.querySelectorAll
1.1 element.getElementById
element.getElementById 是指去 element 节点下根据 id 查找子节点。
通常在程序开始前,没有主动去获取过节点,这个时候会使用根节点 document 来进行查找。
在使用 JavaScript 操作 DOM 节点的时候,也会把 DOM 节点称为 DOM 对象,以契合编程中对象的概念,更好理解。
以上例子通过 document.getElementById 获取 id 为 html-element 的 DOM 节点,并通过修改 innerHTML 属性,将这个节点的内容进行了修改。
1.2 element.getElementByName
element.getElementByName 是通过元素的 name 属性进行查找的,过去操作表单的时候会经常用到。
通过 getElementsByName 获取到的是 DOM 节点的集合,需要注意的是,这个集合不是数组类型的,而是 NodeList ,其不具备数组的 map 、filter 等方法,但是具备 forEach 方法。
Tips:IE 和早期浏览器的 NodeList 是没有 forEach 方法的,具体版本可以通过 Can I Use 查看。
1.3 element.getElementsByTagName
element.getElementsByTagName 是通过标签名获取 DOM 节点的,返回的也是一个集合。
此方法返回值的类型是 HTMLCollection ,不是 NodeList ,没有 forEach 方法。
Tips: 特别要注意,此方法为 getElementsByTagName,前往不要忘记有个 s。
1.4 element.getElementsByClassName
element.getElementsByClassName 通过元素的类名来获取 DOM 节点。
与 getElementsByTagName 返回值类型相同,此方法返回类型也是 HTMLCollection。
Tips:注意,getElementsByTagName 中也有 s 。同时此方法也不支持 IE8。
1.5 element.querySelector
文档对象模型 Document 引用的 querySelector () 方法返回文档中与指定选择器或选择器组匹配的第一个 html 元素 Element 。 如果找不到匹配项,则返回 null 。
element.querySelector 是获取 DOM 节点最常用的方法之一,可以传入 CSS 选择器来匹配获取 DOM 节点。
如使用 CSS 在给 id 为 tip 的元素设置红色字体样式的时候,选择器使用的是 #tip。
使用 element.querySelector 获取 id 为 tip 的元素,传入的参数也是 #tip,与 CSS 选择器一致。
通过设置 style 下的 color 属性,可以更改字体颜色。通过 style 设置的样式都是内联样式。
即便传入的选择器能匹配到多个 DOM 对象,此方法也只会返回一个 DOM 对象。
1.6 element.querySelectorAll
返回与指定的选择器组匹配的文档中的元素列表 (使用深度优先的先序遍历文档的节点)。返回的对象是 NodeList 。
此方法传入的参数与 querySelector 一致,但会返回匹配到的所有 DOM 对象。
element.querySelectorAll 返回的也是一个 NodeList。
2. 操作 DOM 节点
到目前为止已经做了许多 DOM 操作了,如使用 innerText 修改文本,使用 style 修改样式,这些其实都是在操作 DOM。
2.1 修改 class 属性
修改节点的 class 属性,这个操作频率是非常高的。
通过 DOM 节点的 className 属性,来控制 class。
2.2 设置 / 获取其他属性
修改 class 也属于这个场景,但使用 className 更为频繁,所以单独拿出来介绍。
节点的许多状态是使用属性表示的,如复选框是否选中,就是由 checked 属性决定。
getAttribute 方法就可以获得某个属性的值。
removeAttribute 则是将属性从元素上移除。
这三个方法可以用于元素的任意属性,包括 class 。
3. 其他
3.1 将集合转化为数组
通过几种获取 DOM 节点的方法的返回值可以发现,当要获取多个 DOM 节点组成的集合的时候,返回的都不是数组。
<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 % 2); }); </script>
如使用上述例子对获取到的所有 DOM 节点 用 filter 方法进行过滤是会报错的。
这个时候就需要通过一些方式,来获得由 DOM 节点组成的数组。
3.1.1 使用数组的 slice 方法
这里并不是让 DOM 节点的集合去调用 slice 方法,而是利用 slice 方法来将 DOM 节点的集合转化为数组。
slice 方法可以接收两个参数,分别是起始下标和结束下标,其作用是浅复制起始下标到结束下标的所有项,然后产生一个新数组返回,如果不提供参数,则直接浅复制所有项,形成一个新数组返回。
使用 [].slice.call(类数组) 或 Array.prototype.slice.call(类数组) 即可将一个类数组转化为数组。
通过在控制台观察 NodeList 和 HTMLCollection 类型,可以发现他们是符合类数组特性的。
数组的 slice 方法在执行的时候内部是使用循环来操作数组项的,所以操作一个类数组不会出现问题,使用 call 方法将操作的数组指定为传入的伪数组,就达到了将类数组转化为数组的目的。
3.1.2 将数组的原型方法挂载到目标对象的原型上
通过在 NodeList 原型上提供数组的方法,就可以直接进行方法的调用。
3.1.3 使用 for 循环
3.1.4 使用 Array.from 方法
Array.from() 方法从一个类似数组或可迭代对象创建一个新的,浅拷贝的数组实。(MDN)
Array.from 可以将一个类数组转化为数组。
var arrayLike = {
0: '9',
1: '9',
2: '6',
3: ' bye!',
length: 4,
};
var str = Array.from(arrayLike).join('');
console.log(str);
该方法由 ES2015 提供,所以旧版的浏览器不支持。
3.1.5 使用扩展运算符
... 即扩展运算符,根据使用场景,他还能被作为剩余参数操作符使用。
通过 Array.isArray 可以判断一个值是不是数组。
3.2 null 判断
当获取节点的方法没有匹配到任何元素的时候,是可能返回 null 或者 空集合的。
var el = document.querySelector('#dfsafds');
var elList = document.querySelectorAll('.dfsafds');
el.innerHTML = '<p>我写的代码从来不会报错!</p>';
elList[1].innerHTML = '<p>我写的代码从来不会报错!</p>';
碰到这种情况,上述代码就报错了,假如后面代码存在渲染逻辑,则不会再继续执行,最后换来一份 辞退报告。
var el = document.querySelector('#dfsafds');
if (el) {
el.innerHTML = '<p>我写的代码从来不会报错!</p>';
} else {
console.log('节点还没渲染出来');
}
var el = document.querySelector('#dfsafds');
try {
el.innerHTML = '<p>我写的代码从来不会报错!</p>';
} catch (err) {
console.error(err);
console.log('节点还没渲染出来');
}
4. 小结
操作 DOM 是前端程序员的基本功,也是编写网页的重要知识之一。
获取 DOM 节点的方法有很多,部分方法返回的是 NodeList 或 HTMLCollection 类型,而不是数组,不能像操作数组一样操作这些集合,转换成数组可以更方便的利用数组的原生方法对其进行操作。
操作节点的时候,特别是动态渲染的节点,需要做空判断,防止程序报错中断执行。