获取和操作 DOM 节点
DOM 节点也会被称为 DOM 元素。
想要操作 DOM 节点,就必须先获取到 DOM 节点。
1. 获取 DOM 节点
- element.getElementById
- element.getElementByName
- element.getElementsByTagName
- element.getElementsByClassName
- element.querySelector
- element.querySelectorAll
1.1 element.getElementById
返回对拥有指定 id 的第一个对象的引用。
element.getElementById
是指去 element
节点下根据 id 查找子节点。
通常在程序开始前,没有主动去获取过节点,这个时候会使用根节点 document
来进行查找。
在使用 JavaScript 操作 DOM 节点的时候,也会把 DOM 节点称为 DOM 对象
,以契合编程中对象
的概念,更好理解。
1.2 element.getElementByName
返回带有指定名称的对象集合。
element.getElementByName
是通过元素的 name
属性进行查找的,过去操作表单的时候会经常用到。
<form>
<div>
<label>
<input type="checkBox" name="skill" checked="checked" value="JavaScript"> JavaScript
</label>
<label>
<input type="checkBox" name="skill" value="c++"> C++
</label>
<label>
<input type="checkBox" name="skill" checked="checked" value="Java"> Java
</label>
</div>
</form>
<div id="result"></div>
<script>
var checkBoxes = document.getElementsByName('skill');
var skills = [];
checkBoxes.forEach(function(checkBox) {
if (checkBox.getAttribute('checked')) {
skills.push(checkBox.value);
}
});
document.getElementById('result').innerHTML = '选中的技能:' + skills.join('、');
</script>
通过 getElementsByName
获取到的是 DOM 节点的集合,需要注意的是,这个集合不是数组类型的,而是 NodeList
,其不具备数组的 map
、filter
等方法,但是具备 forEach
方法。
1.3 element.getElementsByTagName
返回带有指定标签名的对象集合。
<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>
可以使用 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
。
1.5 element.querySelector
如使用 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>
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 节点
这里列举两个常用的操作。
如使用 class 来控制元素的显示与隐藏。
<style>
.show {
display: block;
}
.hidden {
display: 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。
修改 class 也属于这个场景,但使用 className 更为频繁,所以单独拿出来介绍。
<label>
<input type="checkBox" class="checkBox">
爱我别走
</label>
<div style="margin-top: px;">
<button class="toggle">切换!</button>
</div>
<script>
var checkBox = document.querySelector('.checkBox');
var toggleBtn = document.querySelector('.toggle');
toggleBtn.onclick = function() {
var checked = checkBox.getAttribute('checked');
if (checked) {
checkBox.removeAttribute('checked', '');
} else {
checkBox.setAttribute('checked', 'checked');
}
};
</script>
removeAttribute
则是将属性从元素上移除。
3. 其他
3.1 将集合转化为数组
<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 节点组成的数组。
3.1.1 使用数组的 slice 方法
<div>
<ul>
<li>i</li>
<li>love</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 love imooc
console.log(lisArray2.map(map).join(' ')); // 输出:i love imooc
</script>
使用 [].slice.call(类数组)
或 Array.prototype.slice.call(类数组)
即可将一个类数组转化为数组。
通过在控制台观察 NodeList
和 HTMLCollection
类型,可以发现他们是符合类数组特性的。
所以就可以通过这种方式来转化成数组。
3.1.2 将数组的原型方法挂载到目标对象的原型上
<div>
<ul>
<li>i</li>
<li>love</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 love imooc
</script>
其原理可以参考原型相关章节。
3.1.3 使用 for 循环
使用 for 循环遍历集合,将每一项放入新数组。
<div>
<ul>
<li>i</li>
<li>love</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 love imooc
</script>
这种方式没有特殊情况通常不会去使用。
3.1.4 使用 Array.from 方法
Array.from
可以将一个类数组转化为数组。
var arrayLike = {
: '9',
: '9',
: '6',
: ' bye!',
length: ,
};
var str = Array.from(arrayLike).join('');
console.log(str); // 输出:996 bye!
3.1.5 使用扩展运算符
扩展运算符也是由 ES2015
提供的。
...
即扩展运算符,根据使用场景,他还能被作为剩余参数操作符使用。
通过 Array.isArray
可以判断一个值是不是数组。
3.2 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
类型,而不是数组,不能像操作数组一样操作这些集合,转换成数组可以更方便的利用数组的原生方法对其进行操作。
操作节点的时候,特别是动态渲染的节点,需要做空判断,防止程序报错中断执行。