DOM
DOM(Document Object Model)—— 文档对象模型。在浏览器眼里,网页的结构可不是我们看到的这个样子,而是一棵树,我们称之为 DOM 树。网页中一个个标签就是这棵树上的节点,所以当我们需要改变 HTML(网页) 的结构时,就需要使用 JavaScript 操作 DOM 树中的节点
随便一棵 DOM 树的结构:
我们简单分析一下这棵树,首先是大家熟悉的 head 和 body 标签,head 标签中有 title 标签,代表网页的标题;body 中有一个链接标签和一级标题,对应的代码如下:
<!-- 是不是很好理解呢(这里不写其他多余的东西) -->
<html>
<head>
<title>文档标题</title>
</head>
<body>
<!-- 这里 href 属性图中并没有给出具体的链接地址 -->
<a href="#">我的链接</a>
<h1>我的标题</h1>
</body>
</html>
对于 DOM 节点的操作无非就是【获取、更新、删除、插入】,用到的对象就是 BOM 中的 document(不知道 BOM 也没关系)
1、获取 DOM 节点
常用的获取 DOM 节点有以下 4 种方式
- 通过元素 id 查找元素(最常用):document.getElementById(‘id 名’)
- 通过类名来查找元素:document.getElementsByClassName(‘类名’)
- 通过标签名来查找元素:document.getElementsByTagName(‘标签名’)
- 通过元素 name 来查找元素:document.getElementsByName(‘元素名’)
<!-- 我们先写一个简单的网页(CSS 代码直接写在标签内了,省点空间) -->
<body style="width: 200px; text-align: center; border: 5px solid black">
<dl id="app">
<dt style="font:bolder normal 20px 宋体">***** DOM *****</dt>
<dd id="first" style="margin-left: 0">第一个节点</dd>
<dd class="second" style="margin-left: 0">第二个节点</dd>
<dd name="third" style="margin-left: 0">第三个节点</dd>
</dl>
</body>
网页长这个样子:
好心提醒一下:这三个节点分别设置了 id、class、name 属性,不要眼挫全都看成 id 了!
function func1() {
//1、按照 id 选择节点,因为 id 是唯一的,所以能直接定位到目标节点(最常用)
let first = document.getElementById('first');
//2、按照 class 名字选择节点,细心的你们发现函数名 Elements 带上 s 了嘛,这是因为通过 class 获取的元素有很多
let second = document.getElementsByClassName('second');
//3、按照标签名选择节点(也很常用,而且可以进行拓展),同样一次可以选取多个
let third = document.getElementsByTagName('dt');
//4、按照 name 选择节点(不常用,这里不详细说了),也是一次选取多个
let forth = document.getElementsByName('third');
console.log(first);
console.log(second);
console.log(third);
console.log(forth);
}
控制台显示结果(注意红色标注):
可以看到除了 id 选择,其它都以 数组 的形式呈现(原因代码上已经注释了)
除了上边 4 种直接查找,我们是否可以利用树的结构实现间接查找呢?当然可以,既然是 DOM 树,无非就利用父节点找孩子节点呗:
function func2(){
//先通过 id 找到父节点
let father = document.getElementById('app');
//然后使用父节点的 firstElementChild 属性找到其第一个孩子节点(在这里就是 "***** DOM *****")
let first_child = father.firstElementChild;
//同理,找到父节点最后一个孩子节点(这里是 "第三个节点")
let last_child = father.lastElementChild;
console.log(first_child);
console.log(last_child);
}
控制台显示结果(结果应该都能看懂吧):
.2、更新 DOM 节点
当我们获取一个 DOM 节点后,可以更新其文本内容或者 CSS 样式。具体我们将通过 innerHTML 或者 innerText 属性实现更新操作
innerHTML:功能强大,但安全方面需要注意
function func3() {
//1、使用 innerHTML 最基本的功能就是更新节点中的文本
let first = document.getElementById('first'); //获取 id = 'first' 的节点
first.innerHTML = 'The first node'; //将其文本内容更新为 'The first node'
//2、除了可以更新文本外,还可以添加新的 HTML 标签
let second = document.getElementsByClassName('second');//获取 class = 'second' 的节点
/* 然后看下面一长串,我们首先更改了节点的内容(将 '第二个节点' 改为 'The second node')
接下来按照 HTML 的格式,添加以下内容(写成你们熟悉的格式):
<ul>
<li class="lg">HTML</li>
<li class="lg">CSS</li>
<li class="lg">JavaScript</li>
</ul>
注意:所有的内容都需要写在单引号内! */
second[0].innerHTML = 'The second node <ul><li class="lg">HTML</li><li class="lg">CSS</li><li class="lg">JavaScript</li></ul>';
}
浏览器执行 func3():
当然除了基本的文本内容,节点还具有 style 属性,自然可以修改样式:
/* 使用 style 格式:
节点名称.style.样式名称 = '样式值'
注意:样式名称不要使用 - 连接,而要采用 驼峰命名法!例如 font-size 要写成 fontSize */
function func4() {
let language = document.getElementsByClassName('lg'); //获取新增的三个节点
let colorArray = ['green', 'orange', 'blue']
for (let i = 0; i < language.length; i++) {
language[i].style.color = colorArray[i]; //修改它们的颜色
language[i].style.listStyle = 'none'; //将它们前面的圆点去掉(驼峰命名法)
}
}
注意,这里必须先执行 func3(),再执行 func4() 才能修改样式,否则不存在 class = ‘lg’ 的节点:
innerText:功能较少,但是比较安全
innerText 为什么比 innerHTML 更加安全呢?因为 innerText 会自动对字符串进行 HTML 编码,保证拿到的内容是纯字符串,这样做就无法设置 HTML 标签,自然就更加安全(避免 XSS 攻击)
function func5() {
let father = document.getElementById('app');
father.lastElementChild.innerText = 'The third node';
}
控制台执行 func5():
3、删除 DOM 节点
删除节点很容易,记住一句话:想要删除某个节点,首先需要找到父节点,再调用父节点的 removeChild 方法删除该节点
/* 现在我们想要删除 '第一个节点',按照规定,首先需要找到它的父节点,也就是 <ul> 标签
接下来再找到待删除的 <dd> 标签
最后执行父节点的 removeChile 方法 */
function func6(){
let father = document.getElementById('app');
let son = document.getElementById('first');
father.removeChild(son); //是不是很简单
}
浏览器执行 func6():
有时候我们只能获取待删除的节点,却不知道它的父节点是谁,这时候怎么办?使用 parentElement(万能方法)
function func7(){
let son = document.getElementById('first');
son.parentElement.removeChild(son); //son.parentElement 就是它的父节点
}//结果就不展示了,和上边一样
相反,遇到只能获取父节点的情况,该怎么办(虽然这种情况很少见)?使用 children
function func8(){
let father = document.getElementById('app');
father.removeChild(father.children[1]); //father.children[1] 就代表 father 这个节点的第二个孩子节点,注意下标从 0 开始
}//结果还是一样
//注意:删除操作是一个动态过程,例如这次你删除了第二个节点,那么原本的第三个节点现在就变成了第二个节点!所以下标问题一定要注意
4、插入 DOM 节点
之前我们接触了 innerHTML,可以利用它实现标签(节点)的添加,但是这种方式并不完美,因为它会覆盖原本的内容。如果我们希望保留原本的节点,通过追加的方式插入新的节点,就需要使用 appendChild() 方法
//插入节点首先必须找到待插入的位置,也就是某一个特定的节点,然后再进行后续的操作
//下面演示最完整的插入操作:找到插入的位置 -> 创建新节点 -> 设置属性 -> 插入新节点
function func9() {
let father = document.getElementById('app'); //父节点(待插入位置)
let newNode = document.createElement('dd'); //新节点的标签名称
newNode.id = 'forth'; //新节点的 id
newNode.innerText = '第四个节点'; //新节点的文本内容
newNode.style.marginLeft = '0'; //设置新节点的 CSS 样式
newNode.style.color = 'orange';
father.appendChild(newNode); //完成插入操作
}
//appendChild() 添加的位置为末尾!
浏览器执行 func9():
有时候我们需要插入的节点本来就存在于 DOM 树中,就无需创建,直接拿来用就行
DOM 树会删掉插入前节点的位置,将它放在新的位置处:
function func10() {
let father = document.getElementById('app'); //父节点(待插入位置)
let node = document.getElementById('first'); //已经存在的节点
node.style.color = 'orange'; //给你们变个色
father.appendChild(node); //注意位置的变化
}
浏览器执行 func10()(原本的 ‘第一个节点’ 跑到最后一行去了):
补充:由于 appendChild 只能在尾部插入,为了实现在任意位置插入,我们可以使用 insertBefore
/* 源码:insertBefore<T extends Node>(newChild: T, refChild: Node | null): T;
看不懂?给你们翻译成人话:
父节点.insertBefore(待插入节点, 你希望插入到其中哪个节点之前)*/
function func11() {
let father = document.getElementById('app');
let referenceNode = document.getElementById('first');
//新节点和之前长的一样,这里不解释了
let newNode = document.createElement('dd');
newNode.id = 'forth';
newNode.innerText = '第四个节点';
newNode.style.marginLeft = '0';
newNode.style.color = 'orange';
//插入到 father 内,位置在 referenceNode 之前
father.insertBefore(newNode, referenceNode);
}
浏览器执行 func11():
至此,所有关于 DOM 树【获取、更新、删除、插入】操作都介绍完毕,操作的时候注意逻辑关系就可