JavaScript | DOM API 操作元素
一、WebAPI 背景知识
JS 分成三个大的部分:
- ECMAScript: 基础语法部分
- DOM API: 操作页面结构
- BOM API: 操作浏览器
要想写实际的程序,光会语言是不够的,还需要掌握相关的 "生态”,配套的库 / 框架
对于在浏览器上运行 JS 来说,最最核心的库,就是 DOM API
就是浏览器给 JS 提供的原生接口
基于这样的接口,就可以针对页面的上的元素进行操作了
DOM => 文档 对象 模型
HTML 中,会把每个 html 标签,都视为是一个 JS 中可以操作的对象,操作这个对象就可以影响到界面的显示
浏览器给 JS 提供的 API 非常丰富,也有很多组
DOM API
BOM APl
其实还有一些其他的
websocket APl,canvas APl…
统称为 WebAPI,我们主要是介绍DOM
原生的DOM API 能用,但是并不方便
因此除了原生的 API 之外,也存在了大量的第三方库 / 框架,其中 jquery 就是最知名的一个库
Vue,React 等前端框架,本质上还是对 DOM 的封装
jQuery
jQuery是一套跨浏览器的JavaScript库,简化HTML与JavaScript之间的操作。
特点是轻量级,丰富的DOM选择器,事件、样式、动画支持,Ajax操作支持,可扩展性强
Bootstrap
boostrap是一套追求一致性的框架。
特点是跨设备,跨浏览,响应布局,支持html5 css3,支持less动态样式扩展
jQuery UI
jquery ui是jquery对桌面端的扩展,可以通过可视化界面进行配置。
API:
-
API 是一个更广义的概念,而 WebAPI 是一个更具体的概念,特指 DOM+BOM
-
所谓的 API 本质上就是一些现成的函数 / 对象,让程序猿拿来就用,方便开发
相当于一个工具箱,只不过程序猿用的工具箱数目繁多,功能复杂
DOM:
-
DOM 全称为 Document Object Model.
-
W3C 标准给我们提供了一系列的函数, 让我们可以操作:
- 网页内容
- 网页结构
- 网页样式
DOM 树:
- 一个页面的结构是一个树形结构,称为 DOM 树
重要概念:
-
文档: 一个页面就是一个 文档,使用 document 表示.
-
元素:页面中所有的标签都称为 元素,使用 element 表示.
-
节点:网页中所有的内容都可以称为 节点,(标签节点,注释节点,文本节点,属性节点等),使用 node表示
这些文档等概念在 JS 代码中就对应一个个的对象
所以才叫 “文档对象模型”
二、获取元素
1、querySelector
- 要想操作页面上的元素,就需要先拿到对应的 JS 对象
- DOM 中提供了一组 API 能够获取到网页的元素,最重要的两个:
querySelector
,querySelectorAll
其实是一个 document 这样的对象的属性
页面中的全局对象,一个页面加载好了,就会自动生成一个全局变量,就叫做 document,这里面就有一些属性和方法,让我们来操作页面的内容
<div class="one"> one</div><div class="two"> two</div><ul> <li>three </li></ul><script> // querySelector 参数就是一个 CSS 的选择器 let div = document.querySelector('.one'); console.log(div); // id 选择器 let obj = document.querySelector('#two'); console.log(obj); // 复合选择器 let obj2 = document.querySelector('ul li'); console.log(obj2); </script>
页面结果:
one
two
- three
2、querySelectAll
<ul> <li>aaa</li> <li>bbb</li> <li>ccc</li></ul><script> let obj2 = document.querySelector('ul li'); console.log(obj2); </script>
当 querySelector 的参数的选择器,匹配到了多个元素的时候,此时返回的对象,就是匹配结果中的第一个元素
像这种情况,如果我们想把这些 li 都选中,就需要使用 querySelectAll
,querySelectAll 返回的是一个 “数组” ,就包含了所有被选中的元素
let obj2 = document.querySelectorAll('ul li');
但是准确的说,querySelectorAll
返回的不是一个真正的原生数组,而是一个对象,只不过这个对象,有 length,也能够通过下标的方式来访问内部元素
这样的对象使用起来和数组非常相似 (一模一样),称为 “伪数组”
三、事件
1、什么是事件
- JS 要构建动态页面,就需要感知到用户的行为
- 用户对于页面的一些操作(点击,选择,修改等),操作都会在浏览器中产生一个个事件,被 JS 获取到,从而进行更复杂的交互操作
- 浏览器就是一个哨兵,在侦查敌情(用户行为),一旦用户有反应(触发具体动作),哨兵就会点燃烽火台的狼烟(事件),后方就可以根据狼烟来决定下一步的对敌策略
JS 中的很多代码,都是通过 “事件” 来触发的
事件就是浏览器对于用户的操作行为进行了一个 “统称” (准确的说,事件也不一定全是用户操作产生的,但是大部分是的)
例如,鼠标在页面上移动,就会产生一个鼠标移动事件
再例如,鼠标在页面某个位置点击,就会产生一个鼠标点击事件。再例如,鼠标滚轮,来滚动页面,就会产生一组滚动事件。再例如,用户按下键盘的某个按键,也会产生一个键盘事件。再例如,用户修改浏览器窗口大小,也会产生一个窗口大小改变事件
JS 干的一个主要工作,就是在不同的事件中,进行不同的处理
2、事件三要素
- 事件源 :哪个HTML元素产生的事件.
- 事件类型 :鼠标移动 / 鼠标点击 / 键盘事件 / 窗口大小改变事件…
- 事件的处理程序 :当事件产生之后,执行什么样的 JS 代码,进一步如何处理,往往是一个回调函数
3、点击事件
<!-- 事件源:button --><button>这是一个按钮</button><script> let button = document.querySelector('button'); // 事件类型:onclick 事件处理函数:function button.onclick = function() { alert('hello'); }</script>
称此函数为回调函数:这个函数不会立即调用,而是在合适的实际,被 库 / 框架 自动的调用
另一种写法:这个写法,就把页面 (HTML) 写的更乱,我们期望 结构,样式,行为,能够分离
<button onclick="f()">这是一个按钮</button><script> function f() { alert('hello'); }</script>
四、操作元素
操作 = 获取+修改
- 操作元素内容
- 操作元素的属性
- 操作元素的样式
1、操作元素内容
通过 对象 里面的一个属性 innerHTML 来实现 (元素里面包含的 html 代码是什么样子的)
1.1、 打印内容
<div id="screen">hello world</div><button id="btn">这是一个按钮</button><script> let btn = document.querySelector('#btn'); btn.onclick = function() { let screen = document.querySelector('#screen'); console.log(screen.innerHTML); }</script>
当我们点击多次按钮,可以看到,在控制台上,并没有显示多条数据,而是显示了个数字,控制台默认下会把相同的打印合并成一条的,可以设置在控制台中对相似消息进行分组
1.2、获取内容按钮
<div id="screen"> <ul> <li>aaa</li> <li>aaa</li> <li>aaa</li> </ul></div><button id="btn">获取内容按钮</button>
1.3、 修改内容按钮
<div id="screen"> <ul> <li>aaa</li> <li>aaa</li> <li>aaa</li> </ul></div><button id="btn">修改内容按钮</button><script> let btn = document.querySelector('#btn'); btn.onclick = function() { let screen = document.querySelector('#screen'); screen.innerHTML = '修改后的内容
' }</script>
1.4、点击加一
div 显示整数,一个按钮,每次点击这个按钮,就让里面的整数+1
注意: 这里,innerHTML 得到的是 string 类型,要想进行数字相加,就需要把字符串转成整数
let val = screen.innerHTML;console.log(typeof(val)); // string
val = parseInt(val);
Java 里的 parselnt 是 Integer 类的成员方法。此处 JS 中的 parselnt 相当于是一个全局的函数
由于 JS 是动态类型,转换成数字之后,仍然可以赋值给 valval,就从 string => number 类型
<div id="screen"> 0</div><button id="plus">+</button><script> let plusBtn = document.querySelector('#plus'); plusBtn.onclick = function() { // 1、获取 screen 的值 let screen = document.querySelector('#screen'); let val = screen.innerHTML; val = parseInt(val); // 2、将这个值 + 1 val = val + 1; // 3、把新值写回去 screen.innerHTML = val; }</script>
1.5、input 点击加一
<input type="text" id="screen">
input 作为一个单标签(单身狗),不配拥有 innerHTML,此处是通过 input 的 value 属性,来获取到内部的内容的
<input type="text" id="screen" value="0"><button id="plus">+</button><script> let plusBtn = document.querySelector('#plus'); plusBtn.onclick = function() { // 1、获取 screen 的值 let screen = document.querySelector('#screen'); let val = screen.value; val = parseInt(val); // 2、将这个值 + 1 val = val + 1; // 3、把新值写回去 screen.value = val; }</script>
2、操作元素的属性
2.1、点击切换图片
通过 dom 对象 . 属性名 就可以进行操作了
<img src="picture1.jpg" alt=""><script> let img = document.querySelector('img'); img.onclick = function() { console.log(img.src); // 打印 src 属性的内容 if (img.src.indexOf('picture1.jpg') >= 0) { img.src = 'picture2.jpg'; } else if (img.src.indexOf('picture2.jpg') >= 0) { img.src = 'picture1.jpg'; } }</script>
一个 HTML 标签里,能写哪些属性,就同样可以通过 JS 中的 DOM 对象来获取到一样的属性,
可以通过 console.dir
这个方法,打印出一个 dom 对象的全部属性和值
console.dir(img);
3、获取/修改表单元素属性
表单 (主要是指 input 标签) 的以下属性都可以通过 DOM 来修改
value
: input 的值.disabled
: 禁用checked
: 复选框会使用selected
: 下拉框会使用type
: input 的类型(文本, 密码, 按钮, 文件等)
这些属性,都属于表单元素专有的属性
3.1、点击计数
使用一个输入框输入初始值(整数). 每次点击按钮, 值 + 1
<input type="text" id="text" value="0"><input type="button" id="btn" value='点我+1'> <script>var text = document.querySelector('#text');var btn = document.querySelector('#btn');btn.onclick = function () {var num = +text.value;console.log(num);num++;text.value = num;}</script>
- input 具有一个重要的属性 value, 这个 value 决定了表单元素的内容
- 如果是输入框, value 表示输入框的内容, 修改这个值会影响到界面显式; 在界面上修改这个值也会影响到代码中的属性
- 如果是按钮, value 表示按钮的内容. 可以通过这个来实现按钮中文本的替换
3.2、切换按钮的文本
假设这是个播放按钮, 在 “播放” - “暂停” 之间切换
<input type="button" value="播放"><script> let input = document.querySelector('input'); input.onclick = function() { if (input.value == '播放') { input.value = '暂停'; } else if (input.value == '暂停') { input.value = '播放'; } }</script>
3.3、全选 / 取消全选按钮
实现一个全选效果,主要是操作 input 的 checked 属性
- 点击全选按钮,则选中所有选项
- 只要某个选项取消,则自动取消全选按钮的勾选状态
<input type="checkbox" id="all"> 全选 <br><input type="checkbox" class="girl"> 薛宝钗 <br><input type="checkbox" class="girl"> 林黛玉 <br><input type="checkbox" class="girl"> 王熙凤 <br><input type="checkbox" class="girl"> 贾探春 <br><script> // 1、获取元素 let all = document.querySelector('#all'); let girls = document.querySelectorAll('.girl'); // // 2、给 all 注册点击事件 all.onclick = function() { for (let i = 0; i < girls.length; i++) { // all.checked 就是 all 这个复选框的选中状态 girls[i].checked = all.checked; } } // 3、针对每个 girl 注册点击事件,实现对于 all 的取消操作 for (let i = 0; i < girls.length; i++) { girls[i].onclick = function() { all.checked = checkGirls(girls); } } function checkGirls(girls) { // 判断是不是所有的 girl 都被选中了 for (let i = 0; i < girls.length; i++) { if (!girls[i].checked) { // 只要有一个是未选中,all 就是未选中状态 return ''; } } // 遍历完,所有都是选中状态,就让 all 也是选中状态 return 'checked'; }</script>
4、操作元素样式
本质上也是操作元素属性
style
对应行内样式 (直接把样式写到 style 里面)className
/classList
对应 内部样式 / 外部样式,应用了一个 / 一组 CSS 类名
4.1、点击加字体大小
let div = document.querySelector('div');div.onclick = function() { // 1、获取当前的字体大小 console.log(div.style.fontSize); }
注意:
1、CSS 不区分大小 写,一般不用驼峰。JS 不能支持 –
作为变量名,不能使用脊柱。所有的 CSS 属性都是同样的规则映射过去的
2、当前这里得到 fontSize 是一个字符串,要想相加,就得转成整数
‘20px’ => parselnt => 20
parselnt 转换的时候,会从头往后去转换,遇到非数字字符 ‘px’ ,就转换停止了
3、当修改CSS属性值的时候,一定要注意单位!! 如果单位不合适 / 遗漏,就会失效
<div style="font-size: 20px;">这是一个文本</div><script> let div = document.querySelector('div'); div.onclick = function() { // 1、获取当前的字体大小 console.log(parseInt(div.style.fontSize)); let fontSize = parseInt(div.style.fontSize); // 2、在当前字体大小的基础上,多增加 5px fontSize += 5; div.style.fontSize = fontSize + 'px'; }</script>
在 HTML 中,表示类名的属性,就是 class
但是在 JS 里,属性名变成了 className
/ classList
,为什么不直接使用 class
这个名字?
class
在 JS 中也是一个关键字 ( JS ES6 版本以上,也引入了类这个概念)
如果要修改的样式比较多,通过 style 来修改就麻烦了,可以直接借助 CSS 类来修改
4.2、夜间模式
<style> .light { background-color: #fff; color: #000; } .dark { background-color: #000; color: #fff; }</style><div class="light" style="height: 300px;">这是一段话</div><button>关灯</button><script> let div = document.querySelector('div'); let button = document.querySelector('button'); button.onclick = function() { if (div.className == 'light') { div.className = 'dark'; button.innnerHTML = '开灯'; } else if (div.className == 'dark') { div.className = 'light'; button.innerHTML = '关灯'; } }</script>
5、操作节点
针对元素操作,其实是操作元素的属性 (元素本身没有发生改变)
针对节点操作,其实是新增节点 / 删除节点
5.1、新增节点
分成两个步骤:
创建元素节点
把元素节点插入到 dom 树中
第一步相当于生了个娃, 第二步相当于给娃上户口
创建新节点:
使用 createElement
方法来创建一个元素,options 参数暂不关注
<script> let newDiv = document.createElement('div'); // 创建标签 newDiv.id = 'newDiv'; newDiv.className = 'one'; newDiv.innerHTML = 'hello'; console.log(newDiv);</script>
此处创建的节点,并没有被挂在 dom 树上,因此浏览器页面中,是显示不出来的
上面介绍的只是创建元素节点, 还可以使用:
createTextNode 创建文本节点
createComment 创建注释节点
createAttribute 创建属性节点
我们以 createElement 为主即可
把节点挂在dom树上:
使用 appendChild
把节点插入到某个节点插入到指定节点的最后一个孩子之后
<div class="container"></div><script> let newDiv = document.createElement('div'); // 创建标签 newDiv.id = 'newDiv'; newDiv.className = 'one'; newDiv.innerHTML = 'hello'; console.log(newDiv); let container = document.querySelector('.container'); container.appendChild(newDiv);</script>
还可以使用 insertBefore 将节点插入到指定节点之前
- var insertedNode = parentNode.insertBefore(newNode, referenceNode);
- insertedNode 被插入节点(newNode)
- parentNode 新插入节点的父节点
- newNode 用于插入的节点
- referenceNode newNode 将要插在这个节点之前
如果 referenceNode 为 null 则 newNode 将被插入到子节点的末尾.
注意: referenceNode 引用节点不是可选参数
<div class="container"><div>11</div><div>22</div><div>33</div><div>44</div></div><script>var newDiv = document.createElement('div');newDiv.innerHTML = '我是新的节点';var container = document.querySelector('.container');console.log(container.children);container.insertBefore(newDiv, container.children[0]);</script>
注意1: 如果针对一个节点插入两次, 则只有最后一次生效(相当于把元素移动了)
<div class="container"><div>11</div><div>22</div><div>33</div><div>44</div></div><script>var newDiv = document.createElement('div');newDiv.innerHTML = '我是新的节点';var container = document.querySelector('.container');console.log(container.children);// 此处的 children 里有 4 个元素container.insertBefore(newDiv, container.children[0]);// 此处的 children 里有 5 个元素(上面新插了一个), 0 号元素是 新节点,// 1 号元素是 11, 2号节点是 22, 所以是插入到 22 之前.container.insertBefore(newDiv, container.children[2]);</script>
注意2: 一旦一个节点插入完毕, 再针对刚刚的节点对象进行修改, 能够同步影响到 DOM 树中的内容
<div class="container"><div>11</div><div>22</div><div>33</div><div>44</div></div><script>var newDiv = document.createElement('div');newDiv.innerHTML = '我是新的节点';var container = document.querySelector('.container');console.log(container.children);container.insertBefore(newDiv, container.children[0]);// 插入完毕后再次修改 newDiv 的内容newDiv.innerHTML = '我是新节点2';</script>
5.2、删除节点
删除节点,removeChild
方法来实现
得先拿到父节点,然后再拿到待删除的子节点,通过 removeChild
就能删除了
- oldChild = element.removeChild(child);
- child 为待删除节点
- element 为 child 的父节点
- 返回值为该被删除节点
- 被删除节点只是从 dom 树被删除了, 但是仍然在内存中, 可以随时加入到 dom 树的其他位置.
- 如果上例中的 child节点 不是 element 节点的子节点,则该方法会抛出异常
<div class="container"></div><button>删除 div</button><script> let newDiv = document.createElement('div'); // 创建标签 newDiv.id = 'newDiv'; newDiv.className = 'one'; newDiv.innerHTML = 'hello'; console.log(newDiv); let container = document.querySelector('.container'); container.appendChild(newDiv); let button = document.querySelector('button'); button.onclick = function() { container.removeChild(newDiv); // 前面已经获取了 }</script>
五、代码案例
1、猜数字
Math.random 得到的 [0,1) 随机的 小数 ,如何生成 1 - 100 呢?
先 *100,得到 [0,100) 之间的小数。然后向下取整Math.floor
,也就是直接舍弃小数部分
<button id="resetBtn">重新开始一局游戏</button> <br><span>要猜的数字:</span><input type="text"><button id="guessBtn">猜</button> <br><span>结果:</span> <span id="result"></span> <br><span>已经猜的次数:</span> <span id="guessCount">0</span> <script> // 1、需要用到的元素 let resetBtn = document.querySelector('#resetBtn'); let input = document.querySelector('input'); let guessBtn = document.querySelector('#guessBtn'); let resultSpan = document.querySelector('#result'); let guessCountSpan = document.querySelector('#guessCount'); // 2、生成一个 1 - 100 的随机数 let toGuess = Math.floor(Math.random() * 100) + 1; console.log(toGuess); // 3、实现点击 猜 按钮的逻辑 guessBtn.onclick = function() { // 1) 读取 input 输入的内容,转成整数 if (input.value == '') { return; } let curNum = parseInt(input.value); // 2) 判断大小 给出提示 if (curNum < toGuess) { resultSpan.innerHTML = '低了'; resultSpan.style.color = 'red'; } else if (curNum > toGuess) { resultSpan.innerHTML = '高了'; resultSpan.style.color = 'red'; } else { resultSpan.innerHTML = '猜对了'; resultSpan.style.color = 'green' ; } // 3) 更新猜的次数 let guessCount = parseInt(guessCountSpan.innerHTML); guessCountSpan.innerHTML = guessCount + 1; } // 4、实现 reset 操作,开始游戏 resetBtn.onclick = function() { // 让页面刷新即可 // location 是和 document 并列关系的对象,用来控制页面的链接/地址,通过 reload 操作就可以刷新页面 location.reload(); }</script>
2、表白墙
<style> * { margin: 0; padding: 0; box-sizing: border-box; } .container { width: 100%; } h3 { text-align: center; padding: 30px 0; /* 上下内边距 20,左右为 0 */ font-size: 24px; } p { text-align: center; color: #999; padding: 10px 0; } .row { width: 400px; height: 50px; margin: 0 auto; display: flex; justify-content: center; align-items: center; } .row span { width: 60px; font-size: 20px; } .row input { width: 300px; height: 40px; line-height: 40px; font-size: 20px; text-indent: 0.5em; /* 去掉输入框的轮廓线 */ outline: none; } .row #submit { width: 200px; height: 40px; font-size: 20px; line-height: 40px; margin: 0 auto; color: white; background-color: orange; /* 去掉边框 */ border: none; border-radius: 10px; } /* 按下的效果 */ .row #submit:active { background-color: grey; }</style><div class="container"> <h3>表白墙</h3> <p>输入后点击提示,会将信息显示在表格中</p> <div class="row"> <span>谁:</span> <input type="text"> </div> <div class="row"> <span>对谁:</span> <input type="text"> </div> <div class="row"> <span>说:</span> <input type="text"> </div> <div class="row"> <button id="submit">提交</button> </div></div><script> // 当用户点击 submit,就会获取 input 中的内容,把内容构造成一个 div,插入页面末尾 let submitBtn = document.querySelector('#submit'); submitBtn.onclick = function() { // 1、获取 2 个 input let inputs = document.querySelectorAll('input'); let from = inputs[0].value; let to = inputs[1].value; let msg = inputs[2].value; if (from == '' || to == '' || msg == '') { // 用户还未填写完毕 return; } // 2、生成一个新的 div,内容就是 input 中的内容,新的 div 加到页面中 let div = document.createElement('div'); div.innerHTML = from + ' 对 ' + to + ' 说 ' + msg; div.className = 'row'; // 应用 row 的样式 let container = document.querySelector('.container'); container.appendChild(div); // 3、清空之前输入框的内容 for (let i = 0; i < inputs.length; i++) { inputs[i].value = ''; } }</script>
刚才咱们写的表白墙程序,是通过一些 div.row 来保存咱们提交的消息,这些 div.row,是挂在, DOM,树上,就是在内存中的,容易失去的
一旦页面刷新 / 关闭了,此时,之前内存中保存的数据,就没了
为了解决上述的数据容易丢失问题,有以下解决方案:
-
可以把提交的数据,保存在浏览器本地 (浏览器提供了 localStorage / indexDB 这样的机制,能够实现本地存储),本质上,是通过浏览器,把你要存的数据,存到当前电脑的磁盘上
问题:只有我在自己的电脑上能看到,别人看不到 -
可以把提交的数据,通过网络通信,传输给服务器,由服务器进行保存
- 服务器保存在内存里
- 服务器保存在文件中
- 服务器保存在数据库里
3、待办事项
<style> * { margin: 0; padding: 0; box-sizing: border-box; } .container { width: 800px; margin: 0 auto; display: flex; } .todo, .done { width: 50%; height: 100%; } .container h3 { height: 50px; text-align: center; line-height: 50px; background-color: #333; color: #fff; } .nav { width: 800px; height: 100px; margin: 0 auto; display: flex; align-items: center; } .nav input { width: 600px; height: 50px; } .nav button { width: 200px; height: 50px; border: none; background-color: orange; color: #fff; } .row { height: 50px; display: flex; align-items: center; } .row input { margin: 0 10px; } .row span { width: 300px; } .row button { width: 50px; height: 40px; }</style><div class="nav"> <input type="text"> <button>新建任务</button></div><div class="container"> <div class="todo"> <h3>未完成</h3> <div class="row"> <input type="checkbox"> <span>任务</span> <button>删除</button> </div> </div> <div class="done"> <h3>已完成</h3> </div></div><script> // 一、实现新增任务 let addTaskButton = document.querySelector('.nav button'); addTaskButton.onclick = function () { // 1. 获取到任务内容的输入框 let input = document.querySelector('.nav input'); // 2. 获取到输入框内容 let taskContent = input.value; // 3. 根据内容新建一个元素节点 let row = document.createElement('div'); row.className = 'row'; let checkbox = document.createElement('input'); checkbox.type = 'checkbox'; let span = document.createElement('span'); span.innerHTML = taskContent; let button = document.createElement('button'); button.innerHTML = '删除'; row.appendChild(checkbox); row.appendChild(span); row.appendChild(button); // 4. 把新节点插入到 todo 中 let todo = document.querySelector('.todo'); todo.appendChild(row); // 二、点击复选框后将元素放到 "已完成" // 在事件回调函数中使用 this 能够获取到当前处理事件的元素. // 通过 this.parentNode 属性能够获取到当前元素的父元素. // 点击 checkbox 时, 会先修改 value , 再触发点击事件 // 修改 addTaskButton.onclick // 5. 给 checkbox 注册点击事件 checkbox.onclick = function () { // var row = this.parentNode; // 注意! 是先触发 checked 为 true, 然后再调用 onclick 函数 if (this.checked) { var target = document.querySelector('.done'); } else { var target = document.querySelector('.todo'); } target.appendChild(row); } // 三、点击删除按钮删除该任务 // 6. 给删除按钮注册点击事件 button.onclick = function () { let row = this.parentNode; let grandParent = row.parentNode; grandParent.removeChild(row); } }</script>