WebApI(一)
WebApi
webapi_day01 获取元素
1.1webapi认知

DOM树 又称为文档树模型,把文档映射成树形结构,通过节点对象对其处理,处理的结果可以加入到当前的页面。
- 文档:一个页面就是一个文档,DOM中使用document表示
- 节点:网页中的所有内容,在文档树中都是节点(标签、属性、文本、注释等),使用node表示
- 标签节点:网页中的所有标签,通常称为元素节点,又简称为“元素”,使用element表示
1.2获取dom元素
document.querySelector
1.获取的是满足条件的第一个 一定只有一个
2.可以直接操
document.querySelectorAll
1.获取的是满足条件的所有DOM对象 返回的是一个集合
2.无法直接操作,必须通过下标才能操作
<body>
<div>我是一个盒子</div>
<div class="two">我是第二个盒子</div>
<ul>
<li>11</li>
<li>22</li>
<li>33</li>
</ul>
<script>
//querySelector 获取第一个元素
let dd = document.querySelector('div')
let two = document.querySelector('.two')
let three = document.querySelector('ul li:last-child')
//querySelectorAll 获取所有元素得到伪数组(集合),得通过下标操作
let all = document.querySelectorAll('ul li')
console.log(two)
console.log(three)
console.log(all)
for(let i = 0; i < all.length; i++){
console.log(all[i]);
}
</script>
</body>
1.2.1. 根据ID获取
语法:document.getElementById(id)
作用:根据ID获取元素对象
参数:id值,区分大小写的字符串
返回值:元素对象 或 null
案例代码
<body>
<div id="time">2019-9-9</div>
<script>
// 因为我们文档页面从上往下加载,所以先得有标签 所以我们script写到标签的下面
var timer = document.getElementById('time');
console.log(timer);
console.log(typeof timer);
// console.dir 打印我们返回的元素对象 更好的查看里面的属性和方法
console.dir(timer);
</script>
</body>
1.2.2. 根据标签名获取元素
语法:document.getElementsByTagName('标签名') 或者 element.getElementsByTagName('标签名')
作用:根据标签名获取元素对象
参数:标签名
返回值:元素对象集合(伪数组,数组元素是元素对象)
案例代码
<body>
<ul>
<li>知否知否,应是等你好久11</li>
<li>知否知否,应是等你好久22</li>
<li>知否知否,应是等你好久33</li>
<li>知否知否,应是等你好久44</li>
<li>知否知否,应是等你好久55</li>
</ul>
<ul id="nav">
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
<li>生僻字</li>
</ul>
<script>
// 1.返回的是 获取过来元素对象的集合 以伪数组的形式存储的
var lis = document.getElementsByTagName('li');
console.log(lis);
console.log(lis[0]);
// 2. 我们想要依次打印里面的元素对象我们可以采取遍历的方式
for (var i = 0; i < lis.length; i++) {
console.log(lis[i]);
}
// 3. element.getElementsByTagName() 可以得到这个元素里面的某些标签
var nav = document.getElementById('nav'); // 这个获得nav 元素
var navLis = nav.getElementsByTagName('li');
console.log(navLis);
</script>
</body>
注意:getElementsByTagName()获取到是动态集合,即:当页面增加了标签,这个集合中也就增加了元素。
1.3设置修改元素内容
innerText 只关注内容, 不关注标签
innerHTML 可以解析标签
使用: 不明确数据的安全性的情况下, 使用innerText
1.4设置修改元素属性
1.4.1 设置修改元素样式属性
style操作css
- 每一个DOM对象都有一个style属性,style属性的值是一个对象,里面存储了所有行内样式对应的键值对。
- 如果样式的名字带了-,比如background-color,到了style对象中,变成了驼峰命名法,backgroundColor(因为-在js中-会被解析成减号)
- style属性只能获取和设置行内样式,在类样式中定义的样式通过style获取不到。
- box.style.width = '100px'
操作类名(className)
- 由于class是关键字, 所以使用className去代替
- className是使用新值换旧值, 如果需要添加一个类,需要保留之前的类名
classList
1.添加:add(class1, class2, ...)
2,删除:remove(class1, class2, ...)
3.切换:toggle(class)
注意:toole根据原来元素是否有这个属性来操作的,若果有,则删除,如果没有,则添加
对比className和style、classList的区别:
- 修改大量样式的更方便、
- 修改不多样式的时候方便
- classList 是追加和删除不影响以前类名
1.4.2 设置修改表单属性
正常的有属性有取值的 跟其他的标签属性没有任何区别
获取: DOM对象.属性名
设置: DOM对象.属性名 = 新值
表单属性中添加就有效果,移除就没有效果,一律使用布尔值表示 如果为true 代表添加了该属性 如果是false 代表移除了该属性
常见表单属性 value、disabled、checked、selected
1.5 定时器

倒计时案例
<textarea name="" id="" cols="30" rows="10">
用户注册协议
欢迎注册成为京东用户!在您注册过程中,您需要完成我们的注册流程并通过点击同意的形式在线签署以下协议,请您务必仔细阅读、充分理解协议中的条款内容后再点击同意(尤其是以粗体或下划线标识的条款,因为这些条款可能会明确您应履行的义务或对您的权利有所限制)。
【请您注意】如果您不同意以下协议全部或任何条款约定,请您停止注册。您停止注册后将仅可以浏览我们的商品信息但无法享受我们的产品或服务。如您按照注册流程提示填写信息,阅读并点击同意上述协议且完成全部注册流程后,即表示您已充分阅读、理解并接受协议的全部内容,并表明您同意我们可以依据协议内容来处理您的个人信息,并同意我们将您的订单信息共享给为完成此订单所必须的第三方合作方(详情查看
</textarea>
<br>
<button class="btn">我已经阅读用户协议(6)</button>
<script>
let btn = document.querySelector('button')
btn.disabled = true
let num = 6
let dsq = setInterval(function(){
num--
btn.innerHTML = `我已经阅读用户协议(${num})`
if(num === 0){
btn.disabled = false
clearInterval(dsq)
btn.innerHTML = `我已经阅读用户协议`
}
},1000)
</script>
webapi_day02 事件基础
2.1 事件监听

DOM LO级事件监听
- 给元素设置它们的
onxxx属性(e.g., onclick)
对于 DOM0 级事件监听 ,它只能监听冒泡阶段
2.1) 常见的页面事件监听
| 事件名 | 事件描述 |
|---|---|
| onload | 当页面或图像被完成加载 |
| onunload | 当用户退出页面 |
2.2) 常见的鼠标事件监听
| 事件名 | 事件描述 |
|---|---|
| onclick | 当鼠标单击某个元素 |
| ondblclick | 当鼠标双击某个元素 |
| onmousedown | 当鼠标按键在某个元素上按下 |
| onmouseup | 当鼠标按键在某个元素上松开 |
| onmousemove | 当鼠标按键在某个元素上移动 |
| onmouseenter | 当鼠标移动到某个元素上 (进入到某个元素区域时) |
| onmouseleave | 当鼠标离开某个元素 |
其中onmouseenter与onmouseover类似,onmouseleave与onmouseout类似;
它们两者的区别:
onmouseenter与onmouseleave事件不支持冒泡,另外两个支持事件冒泡;
所以,onmouseenter与onmouseleave搭配使用,onmouseover与onmouseout搭配使用
2.3) 常见的键盘事件监听
| 事件名 | 事件描述 |
|---|---|
| onkeypress | 当键盘上的某个按键被按下 (系统按键无法识别 e.g.,F1 这类的按键) |
| onkeydown | 当键盘上的某个按键被按下 (系统按键可以识别,并且先于onkeypress发生) |
| onkeyup | 当键盘上的某个按键被松开 |
2.4) 常见的表单事件监听
| 事件名 | 事件描述 |
|---|---|
| oninput | 当用于正在修改表单域的内容 |
| onchange | 当用户改变了表单域的内容 |
| onfocus | 当元素获得焦点 (e.g.,tab键或鼠标点击) |
| onblur | 当元素失去焦点 |
| onsubmit | 当表单被提交 |
| onreset | 当表单被重置 |
DOM2 级事件监听
DOM2 级事件监听:
EventTarget.addEventListener()方法将指定的监听器注册到EventTarget上,当该对象触发指定的事件时,指定的回调函数就会被执行。
它有着几种写法,想了解别的写法可以看看 MDN 官方文档,这里介绍的写法如下:
- type:表示监听事件类型的字符串
- listener:事件监听函数
- useCapture:当写
true时,进行的是事件捕获阶段,默认为false,事件冒泡阶段
EventTarget.addEventListener(type, listener, useCapture);
1
对于常用的type:即把常用的 DOM0 级的onxxx的on去掉后就可以了,即原来的事件名。
例如:
target.addEventListener('click', () => {
console.log("我被点击了");
});
// 这里第三个参数没写,默认监听冒泡阶段,如果要监听捕获阶段,写上 true
2.2案例
2.2.1 随机点名案例
<h2>随机点名</h2>
<div class="box">
<span>名字是:</span>
<div class="qs">这里显示姓名</div>
</div>
<div class="btns">
<button class="start">开始</button>
<button class="end">结束</button>
</div>
<script>
// 数据数组
let arr = ['马超', '黄忠', '赵云', '关羽', '张飞']
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
// 1. 获取元素 两个按钮 + div
// 一定不要忘记加点 因为里面写css类选择器
let start = document.querySelector('.start')
let end = document.querySelector('.end')
let qs = document.querySelector('.qs')
// timer 要是全局变量
let timer = 0
// random 要是全局变量
let random = 0
//定义一个变量防止多次点击
let isclick = true
// 2. 给开始按钮注册事件
start.addEventListener('click', function () {
//进行if判断,当值用户多次点击,造成时间重复
if(isclick){
isclick=false
// 随机抽数据--- 快速不断的抽取 间歇函数定时器
timer = setInterval(function () {
random = getRandom(0, arr.length - 1)
qs.innerHTML = arr[random]
}, 25)
// 如果到了最后一个,就禁用两个按钮
if (arr.length === 1) {
start.disabled = end.disabled = true
}
}
})
// 3. 给结束按钮注册事件 本质是停止定时器
end.addEventListener('click', function () {
isclick = true
// 停止定时器
clearInterval(timer)
// 删除数组元素
arr.splice(random, 1)
// console.log(arr)
})
</script>
2.2.2 模拟小米搜索框
<div class="mi">
<input type="search" placeholder="小米笔记本">
<ul class="result-list">
<li><a href="#">全部商品</a></li>
<li><a href="#">小米11</a></li>
<li><a href="#">小米10S</a></li>
<li><a href="#">小米笔记本</a></li>
<li><a href="#">小米手机</a></li>
<li><a href="#">黑鲨4</a></li>
<li><a href="#">空调</a></li>
</ul>
</div>
<script>
// 1. 获取元素 input
let search = document.querySelector('input')
let list = document.querySelector('.result-list')
// 2. 事件监听 获得光标事件 focus
search.addEventListener('focus', function () {
// 显示下拉菜单
list.style.display = 'block'
// 文本框变色
this.classList.add('search')
})
// 3. 事件监听 失去光标事件 blur
search.addEventListener('blur', function () {
// 隐藏下拉菜单
list.style.display = 'none'
// 文本框去色
this.classList.remove('search')
})
</script>
2.2.3 针对表单案例
<table>
<tr>
<th class="allCheck">
<input type="checkbox" name="" id="checkAll"> <span class="all">全选</span>
</th>
<th>商品</th>
<th>商家</th>
<th>价格</th>
</tr>
<tr>
<td>
<input type="checkbox" name="check" class="ck">
</td>
<td>小米手机</td>
<td>小米</td>
<td>¥1999</td>
</tr>
<tr>
<td>
<input type="checkbox" name="check" class="ck">
</td>
<td>小米净水器</td>
<td>小米</td>
<td>¥4999</td>
</tr>
<tr>
<td>
<input type="checkbox" name="check" class="ck">
</td>
<td>小米电视</td>
<td>小米</td>
<td>¥5999</td>
</tr>
</table>
<script>
// 1. 获取元素 全选 和 ck 小复选框
let all = document.querySelector('#checkAll')
let cks = document.querySelectorAll('.ck')
let span = document.querySelector('span')
// 2. 事件监听 全选按钮
all.addEventListener('click', function () {
// console.log(all.checked) // true false
// 我们需要做的就是把 all.checked 给下面三个小按钮
// 因为三个按钮在伪数组里面,我们需要遍历的方式,挨着取出来,依次给值
for (let i = 0; i < cks.length; i++) {
cks[i].checked = all.checked
}
// 当我们的全选按钮处于选中状态,则可以改为取消
if (all.checked) {
// console.log('要改')
span.innerHTML = '取消'
} else {
span.innerHTML = '全选'
}
})
// 3. 小按钮的做法 同时给多个元素绑定相同事件
for (let i = 0; i < cks.length; i++) {
// 绑定事件
cks[i].addEventListener('click', function () {
// console.log(11)
// 只要点击任何一个小按钮,都要遍历所有的小按钮
for (let j = 0; j < cks.length; j++) {
// 都来看看是不是有人没有选中
if (cks[j].checked === false) {
// 如果有false 则退出循环 结束函数
all.checked = false
span.innerHTML = '全选'
break
}else{
all.checked = true
span.innerHTML = '取消'
}
}
// 当我们的循环结束,如果代码走到这里,说明没有false,都被选中了,则全选按钮要选中
// all.checked = true
// span.innerHTML = '取消'
})
}
</script>
<div>
<input type="text" id="total" value="1" readonly>
<input type="button" value="+" id="add">
<input type="button" value="-" id="reduce" disabled>
<script>
// 1. 获取元素 三个
let total = document.querySelector('#total')
let add = document.querySelector('#add')
let reduce = document.querySelector('#reduce')
// 2. 点击加号 事件侦听
add.addEventListener('click', function () {
// console.log(typeof total.value)
// total.value = total.value + 1
// i++ 隐式转换
// i = i + 1
total.value++
reduce.disabled = false
})
// 3. 点击减号 事件侦听
reduce.addEventListener('click', function () {
total.value--
if (total.value <= 1) {
reduce.disabled = true
}
})
</script>
</div>
2.3 高阶函数,this

this指向:https://blog.csdn.net/m0_55990909/article/details/124752030
webapi_day03 节点操作
3.1 节点操作
3.1.1 父级节点
node.parentNode
<body>
<div class="grandfather">
<div class="father">
<span class="son">×</span>
</div>
</div>
<script>
// 1. 以往获取节点方法
var son= document.querySelector('.son');
var father = document.querySelector('.father');
var grandfather = document.querySelector('.grandfather');
// 1. 利用节点操作获取父节点方法
console.log(son.parentNode);
//得到的是离元素最近的父级节点 注意:如果找不到父节点就返回为 null
</script>
</body>
3.1.2 子节点
node.childNodes // 获取所有的子节点 包含 元素节点 文本节点等等
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
var ul = this.document.querySelector('ul');
console.log(ul.childNodes);
</script>
parentNode.children // 非标准 children是一个只读属性,返回所有子元素的节点。它只返回元素结点,其他结点均不返回。
虽热children是非标准,但是得到了各个浏览器的支持,因此我们可以放心使用。
| node.children (获取所有的子元素节点)(常用) |
|---|
| node.children[i] (获取第i-1个子元素节点)(常用,没有兼用性问题) |
| node.firstChild(第一个子节点 不管是文本节点还是元素节点) |
| node.lastChild(最后一个子节点 不管是文本节点还是元素节点) |
| node.firstElementChild(第一个子元素节点,ie9才支持) |
| node.lastElementChild(最后一个子元素节点,ie9才支持) |
<body>
<ul>
<li>son1</li>
<li>son2</li>
<li>son3</li>
</ul>
<script>
// 以往获取节点的方法
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
// 1. 子节点 childNodes 所有的子节点 包含 元素节点 文本节点等等
console.log(ul.childNodes);
// 2. 子节点 childNodes[i] 获取第i-1个子节点
console.log(ul.childNodes[0].nodeType);
console.log(ul.childNodes[1].nodeType);
console.log(ul.childNodes[2].nodeType);
// 3. children 获取所有的子元素节点(常用)
console.log(ul.children);
// 4. children[i] 获取第i-1个子元素节点(常用)
console.log(ul.children[0]);
// 5. node.firstChild 获取第一个子节点 不管是文本节点还是元素节点
console.log(ul.firstChild.nodeType);
// 6. node.lastChild 获取最后一个子节点 不管是文本节点还是元素节点
console.log(ul.lastChild.nodeType);
// 7. node.firstElementChild 获取第一个子元素节点,ie9才支持
console.log(ul.firstElementChild);
// 8. cnode.lastElementChild 获取最后一个子元素节点,ie9才支持
console.log(ul.lastElementChild);
</script>
</body>
3.1.3 兄弟结点
获取上一个/下一个兄弟节点,包含文本节点、元素节点等。
node.nextSibling // 下一个兄弟节点
node.previousSibling // 上一个兄弟节点
获取上一个/下一个兄弟节点,只获取元素节点。
node.nextElementSibling // 上一个兄弟元素节点
node.previousElementSibling // 上一个兄弟元素节点
<body>
<div>brother1</div>
<span>brother2</span>
<script>
var div = document.querySelector('div');
// 1.nextSibling 下一个兄弟节点 包含元素节点或者 文本节点等等
console.log(div.nextSibling);
// 2.previousSibling 上一个兄弟节点 包含元素节点或者 文本节点等等
console.log(div.previousSibling);
// 3. nextElementSibling 得到下一个兄弟元素节点,如果不存在,则返回null
console.log(div.nextElementSibling);
// 4. previousElementSibling 得到上一个兄弟元素节点,如果不存在,则返回null
console.log(div.previousElementSibling);
</script>
</body>
3.2 元素创建及操作
3.2.1 创建添加节点
创建节点:document.createElement(‘tagName’)
添加节点:
1.node.appendChild(child) 其中node 父级 child 是子级 后面追加元素,此操作类似数组中的push
2.node.insertBefore(child, 指定元素) 该方法是将节点添加到指定元素的前面
具体实例如下所示
<body>
<ul>
<li>添加节点</li>
</ul>
<script>
// 1. 创建节点元素节点
var li = document.createElement('li');
// 2. 添加节点方法1 node.appendChild(child) node 父级 child 是子级 后面追加元素 类似于数组中的push
var ul = document.querySelector('ul');
ul.appendChild(li);
// 3. 添加节点方法2 node.insertBefore(child, 指定元素);该方法是将节点添加到指定元素的前面
var lili = document.createElement('li');
ul.insertBefore(lili, ul.children[0]);
// 4. 我们想要页面添加一个新的元素 : 1. 创建元素 2. 添加元素
</script>
</body>
12345678910111213141516
运行结果如下

3.2.2 删除节点操作
删除节点:node.removeChild(child)
具体实例如下图所示
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
// 1.获取元素
var ul = document.querySelector('ul');
// 2. 删除元素 node.removeChild(child)
ul.removeChild(ul.children[1]);
console.log(ul);
</script>
</body>
1234567891011121314
运行结果如下


3.2.3 复制节点操作
浅拷贝:node.cloneNode() 浅拷贝的意思是只复制标签不复制里面的内容
深拷贝:node.cloneNode(true) 深拷贝的意思是复制标签以及里面的内容
具体实例如下图所示
<body>
<ul>
<li>1</li>
</ul>
<script>
var ul = document.querySelector('ul');
// 1. node.cloneNode(); 括号为空或者里面是false 浅拷贝 只复制标签不复制里面的内容
// 2. node.cloneNode(true); 括号为true 深拷贝 复制标签复制里面的内容
var li1 = ul.children[0].cloneNode();
var li2 = ul.children[0].cloneNode(true);
ul.appendChild(li1);
ul.appendChild(li2);
console.log(ul);
</script>
</body>
123456789101112131415
运行结果如下


3.2.4 拓展(常见的节点定性操作)
使用 document.createNode()动态创建元素节点后,无法再使用html定义元素节点的id、类名、内容等。这时就需要通过节点操作,来进行动态添加。
1.添加类名
node.className = 'content' ; // 等价于↓
<div class='content'></div>
2.添加id
node.id = 'content'; // 等价于↓
<div class='content'></div>
3.添加内容
node.innerHtml = '这是一个元素标签'; // 等价于↓
<div>这是一个元素标签</div>
3.3 日期对象
3.3.1 实例化日期对象

3.3.2 具体方法
获取集体年月日

//获取日期对象
var oDate = new Date();
//获取年
var year = oDate.getFullYear();
//获取月 0--11
var month = oDate.getMonth() + 1; //范围0-11 后面加1
//获取日
var day = oDate.getDate();
//获取星期几
var weekDay = oDate.getDay();
//获取小时 0--59
var hour = oDate.getHours();
//获取分 0--59
var min = oDate.getMinutes();
//获取秒 0--59
var sec = oDte.getSeconds();
//获取毫秒 0--999
var ms = oDate.getUTCMilliseconds();
1、在创建日期对象的时候设置时间
new Date = new(年,月,日,时,分,秒)
//获取日期对象
var oDate = new Date();
var oDate = new Date(2020,6,23,12,23,34)
2、形式2 new Date("年-月-日 时:分:秒");
//获取日期对象
var oDate = new Date();
var oDate = new Date("2021-12-25 21:14:30")
3、将日期对象直接转为时间字符串 根据本地时间格式,把 Date 对象的时间部分转换为字符串。
//获取日期对象
var oDate = new Date();
var sDate = oDate.toLocaleDateString();
3.3.3 时间戳
1.获取当前日期的时间戳。
//唯一性,一般用来时间戳来生成id
var timeStamp = oDate.getTime();
2.简写 利用隐式转换 +new Date()
console.log(+new Date);
console.log(+new Date('2022-12-03 12:00:00'));
3.Date.now() 只能得到当前的时间戳
console.log(Date.now());
3.3.4 案例倒计时
<div class="countdown">
<p class="next">今天是2021年8月28日</p>
<p class="title">下班倒计时</p>
<p class="clock">
<span id="hour">00</span>
<i>:</i>
<span id="minutes">25</span>
<i>:</i>
<span id="scond">20</span>
</p>
<p class="tips">
下班时间 18:00:00
</p>
</div>
<script>
let hour = document.querySelector('#hour')
let minutes = document.querySelector('#minutes')
let scond = document.querySelector('#scond')
let next = document.querySelector('.next')
time()
setInterval(time, 1000)
function time() {
// 1. 得到现在的时间戳
let now = +new Date()
// 2. 得到指定时间的时间戳
let last = +new Date('2022-12-02 18:00:00')
// 3. (计算剩余的毫秒数) / 1000 === 剩余的秒数
let count = (last - now) / 1000
// console.log(count)
// 4. 转换为时分秒
// h = parseInt(总秒数 / 60 / 60 % 24) // 计算小时
let h = parseInt(count / 60 / 60 % 24)
h = h < 10 ? '0' + h : h
// m = parseInt(总秒数 / 60 % 60); // 计算分数
let m = parseInt(count / 60 % 60)
m = m < 10 ? '0' + m : m
// s = parseInt(总秒数 % 60); // 计算当前秒数
let s = parseInt(count % 60);
s = s < 10 ? '0' + s : s
// console.log(h, m, s)
hour.innerHTML = h
minutes.innerHTML = m
scond.innerHTML = s
//改写头部时间显示
let date = new Date()
let arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
let year = date.getFullYear()
let month = date.getMonth()+1
let date1 = date.getDate()
let hour1 = date.getHours()
let min = date.getMinutes()
let sec = date.getSeconds()
let day = date.getDay()
next.innerHTML = `今天是 ${year}年${month}月${date1}日<br> ${hour1}:${min}:${sec} ${arr[day]}`
}
</script>
效果如下:

3.4 综合案例 微博发布
<div class="w">
<!-- 操作的界面 -->
<div class="controls">
<img src="./images/9.6/tip.png" alt="" /><br />
<!-- maxlength 可以用来限制表单输入的内容长度 -->
<textarea placeholder="说点什么吧..." id="area" cols="30" rows="10" maxlength="200"></textarea>
<div>
<span class="useCount" id="useCount">0</span>
<span>/</span>
<span>200</span>
<button id="send">发布</button>
</div>
</div>
<!-- 微博内容列表 -->
<div class="contentList">
<ul id="list"></ul>
</div>
</div>
<!-- 添加了hidden属性元素会直接隐藏掉 -->
<!-- <li hidden>
<div class="info">
<img class="userpic" src="./images/9.6/03.jpg" />
<span class="username">死数据:百里守约</span>
<p class="send-time">死数据:发布于 2020年12月05日 00:07:54</p>
</div>
<div class="content">死数据:111</div>
<span class="the_del">X</span>
</li> -->
<script>
// maxlength 是一个表单属性, 作用是给表单设置一个最大长度
// 模拟数据
let dataArr = [
{ uname: '司马懿', imgSrc: './images/9.5/01.jpg' },
{ uname: '女娲', imgSrc: './images/9.5/02.jpg' },
{ uname: '百里守约', imgSrc: './images/9.5/03.jpg' },
{ uname: '亚瑟', imgSrc: './images/9.5/04.jpg' },
{ uname: '虞姬', imgSrc: './images/9.5/05.jpg' },
{ uname: '张良', imgSrc: './images/9.5/06.jpg' },
{ uname: '安其拉', imgSrc: './images/9.5/07.jpg' },
{ uname: '李白', imgSrc: './images/9.5/08.jpg' },
{ uname: '阿珂', imgSrc: './images/9.5/09.jpg' },
{ uname: '墨子', imgSrc: './images/9.5/10.jpg' },
{ uname: '鲁班', imgSrc: './images/9.5/11.jpg' },
{ uname: '嬴政', imgSrc: './images/9.5/12.jpg' },
{ uname: '孙膑', imgSrc: './images/9.5/13.jpg' },
{ uname: '周瑜', imgSrc: './images/9.5/14.jpg' },
{ uname: '老夫子', imgSrc: './images/9.5/15.jpg' },
{ uname: '狄仁杰', imgSrc: './images/9.5/16.jpg' },
{ uname: '扁鹊', imgSrc: './images/9.5/17.jpg' },
{ uname: '马可波罗', imgSrc: './images/9.5/18.jpg' },
{ uname: '露娜', imgSrc: './images/9.5/19.jpg' },
{ uname: '孙悟空', imgSrc: './images/9.5/20.jpg' },
{ uname: '黄忠', imgSrc: './images/9.5/21.jpg' },
{ uname: '百里玄策', imgSrc: './images/9.5/22.jpg' },
]
// 需求1:检测用户输入字数
// 1. 注册input事件
// 2. 将文本的内容的长度赋值给对应的数值
// 3. 表单的maxlength属性可以直接限制在200个数之间
let textarea = document.querySelector('textarea')
let useCount = document.querySelector('.useCount')
// 发布按钮
let send = document.querySelector('#send')
// ul
let ul = document.querySelector('#list')
textarea.addEventListener('input', function () {
// console.log(this.value.length)
useCount.innerHTML = this.value.length
})
// 需求2: 输入不能为空
// 点击button之后判断
// 判断如果内容为空,则提示不能输入为空, 并且直接return 不能为空
// 防止输入无意义空格, 使用字符串.trim()去掉首尾空格
// console.log(' str')
// console.log(' str '.trim())
// 并将表单的value值设置为空字符串
// 同时下面红色为设置为0
send.addEventListener('click', function () {
if (textarea.value.trim() === '') {
// 并将表单的value值设置为空字符串
textarea.value = ''
// 同时下面红色为设置为0
useCount.innerHTML = 0
return alert('内容不能为空')
}
// 随机数
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
let random = getRandom(0, dataArr.length - 1)
// 需求3: 新增留言 写到send 的里面
// 创建一个小li,然后里面通过innerHTML追加数据
let li = document.createElement('li')
// 随机获取数据数组里面的内容, 替换newNode的图片和名字以及留言内容
li.innerHTML = `
<div class="info">
<img class="userpic" src=${dataArr[random].imgSrc}>
<span class="username">${dataArr[random].uname}</span>
<p class="send-time"> ${new Date().toLocaleString()} </p>
</div>
<div class="content">${textarea.value}</div>
<span class="the_del">X</span>
`
// 需求4:删除留言 放到追加的前面
// 在事件处理函数里面获取点击按钮, 注册点击事件
// (易错点: 必须在事件里面获取, 外面获取不到)
// 删除对应的元素(通过this获取对应的那条需要删除的元素)
// 教你一招: 放到追加进ul的前面,这样创建元素的同时顺便绑定了事件,赞~~
// 使用 li.querySelector()
let del = li.querySelector('.the_del')
del.addEventListener('click', function () {
// 删除操作 点击的是X 删除的小li 父元素.removeChild(子元素)
ul.removeChild(li)
})
// 利用时间对象将时间动态化 new Date().toLocaleString()
// 追加给 ul 用 父元素.insertBefore(子元素, 那个元素的前面)
ul.insertBefore(li, ul.children[0])
// 需求5:重置
// 将表单域内容重置为空
// 将userCount里面的内容重置为0
textarea.value = ''
// 同时下面红色为设置为0
useCount.innerHTML = 0
let del1 = document.querySelectorAll('.the_del')
console.log(del1);
})
</script>
运行结果
3.5 重绘和回流




webapi_day04 事件高级
4.1 事件对象
什么是事件对象
eventTarget.onclick = function(event) {}
eventTarget.addEventListener('click', function(event) {})
12
这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt ,现在一般写 e。
官方解释:event 对象代表事件的状态,比如键盘按键的状态、鼠标的位置、鼠标按钮的状态。
简单理解:事件发生后,跟事件相关的一系列信息数据的[集合]都放到这个对象里面,这个对象就是事件对象 event,它有很多属性和方法。
| 事件对象属性和方法 | 说明 |
|---|---|
| e.target | 返回触发事件的对象(标准) |
| e.srcElement | 返回触发事件的对象(非标准) |
| e.type | 返回事件的类型,eg、click |
| e.cancelBubble | 该属性阻止冒泡(非标准) |
| e.returnValue | 该属性阻止默认事件/行为(非标准)eg、阻止链接跳转 |
| e.preventDefult() | 该属性阻止默认事件/行为(标准)eg、阻止链接跳转 |
| e.stopPropagation() | 阻止冒泡(标准) |

4.2 事件流

4.2.1 事件冒泡


4.2.2 事件捕获

<div class="father">
<div class="son"></div>
</div>
<script>
let fa = document.querySelector('.father')
let son = document.querySelector('.son')
fa.addEventListener('click', function () {
alert('我是爸爸')
}, true)
son.addEventListener('click', function () {
alert('我是儿子')
}, true)
document.addEventListener('click', function () {
alert('我是爷爷')
}, true)
// btn.onclick = function() {}
//执行顺序由外到里
</script>
4.2.3 阻止冒泡

阻止默认事件(默认行为)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<a href="https://www.baidu.com/">百度</a>
<script>
var a = document.querySelector('a');
// a.addEventListener('click', function(e) {
// e.preventDefault();
// })
a.onclick = function(e) {
// e.preventDefault();
// 低版本浏览器
// e.returnValue;
return false; // 只限于传统的注册方式(return后面的不执行)
alert(12); // 不执行
}
</script>
</body>
</html>
12345678910111213141516171819202122232425
效果:
点击“百度”没有反应,不会跳转。
取消冒泡的2种方法
- e.stopPropagation()
- e.cancelBubble = true; (IE 6-8 )
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.father {
height: 300px;
width: 300px;
background-color: pink;
position: relative;
}
.son {
height: 150px;
width: 150px;
background-color: purple;
position: absolute;
top: 75px;
left: 75px;
text-align: center;
}
</style>
</head>
<body>
<div class="father">
<div class="son">儿子</div>
</div>
<script>
var father = document.querySelector('.father');
var son = document.querySelector('.son');
// 阻止事件冒泡
son.addEventListener('click', function(e) {
alert('阻止冒泡');
// e.stopPropagation();
e.cancelBubble = true;
})
son.addEventListener('click', function() {
alert('son!');
})
father.addEventListener('click', function() {
alert('father');
})
</script>
</body>
</html>
mouseover 和 mouseout 会有冒泡效果
mouseenter 和 mouseleave 没有冒泡效果(推荐)
4.2.4 两种注册时间的区别

<button>点击</button>
<script>
let btn = document.querySelector('button')
// 1.l0 on
// 多次相同的事件,只执行最后一次
// btn.onclick = function () {
// alert('第一次')
// }
// btn.onclick = function () {
// alert('第二次')
// }
// 解绑事件
// btn.onclick = null
// 2. addEventListener
btn.addEventListener('click', add)
function add() {
alert('第一次')
}
// btn.addEventListener('click', function () {
// alert('第二次')
// })
btn.removeEventListener('click', add)
4.3 事件委托
事件委托就是把原本需要绑定在子元素上的事件(onclick、onkeydown 等)委托给它的父元素,让父元素来监听子元素的冒泡事件,并在子元素发生事件冒泡时找到这个子元素。

事件委托是利用事件的冒泡原理来实现的,大致可以分为三个步骤:
- 确定要添加事件元素的父级元素;
- 给父元素定义事件,监听子元素的冒泡事件;
- 使用 event.target 来定位触发事件冒泡的子元素。
注意:使用事件委托时,并不是说把事件委托给随意一个父元素就行。因为事件冒泡的过程也需要消耗时间,距离越远,所需的时间也就越长,所有最好在直接父元素上使用事件委托。
假如我们要为 ul 列表下的每个 li 标签添加点击事件,如果不使用事件委托,最简单的办法就是使用循环来为每个 li 标签绑定事件,示例代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<ul id="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
window.onload = function(){
var the_ul = document.getElementById('list');
var the_li = the_ul.getElementsByTagName('li');
for( var i=0; i < the_li.length; i++ ){
the_li[i].onclick = function(){
console.log(this.innerHTML)
}
}
}
</script>
</body>
</html>
通过上面的代码可以看出,要为每个 li 标签绑定点击事件,首先需要找到 ul 标签,然后通过 ul 标签找到所有 li 标签, 最后在通过遍历所有 li 标签来绑定事件。若使用事件委托的话,就会简单很多,示例代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JavaScript</title>
</head>
<body>
<ul id="list">
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
</ul>
<script>
window.onload = function(){
var the_ul = document.getElementById('list');
the_ul.onclick = function(e){
console.log(e.target.innerHTML)
}
}
</script>
</body>
</html>
<body>
<ul>
<li>我是第1个小li</li>
<li>我是第2个小li</li>
<li>我是第3个小li</li>
<li>我是第4个小li</li>
<li>我是第5个小li</li>
</ul>
<script>
// 不要每个小li注册事件了 而是把事件委托给他的爸爸
// 事件委托是给父级添加事件 而不是孩子添加事件
let ul = document.querySelector('ul')
ul.addEventListener('click', function (e) {
// alert('我点击了')
// 得到当前的元素
// console.log(e.target)
e.target.style.color = 'red'
})
</script>
4.3.1 e.target的使用(与this的区别)
1、this 是事件绑定的元素, 这个函数的调用者(绑定这个事件的元素);
2、e.target 是事件触发的元素;
3、this:哪个元素绑定了这个点击事件,就返回哪个元素;e.target:点击了哪个元素,就返回哪个元素(操作某特定元素触发的改事件,那么返回这个特定元素)。
注意:通常情况下 terget 和 this 是一致的,但在事件冒泡时(父子元素有相同事件,单击子元素,父元素的事件处理函数也会被触发执行),this指向的是父元素,因为它是绑定事件的元素对象,而target指向的是子元素,因为他是触发事件的那个具体元素对象。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<ul>
<li>刘能</li>
<li>赵四</li>
<li>谢广坤</li>
</ul>
<script>
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
console.log(this); // ul
console.log(e.target);
})
</script>
</body>
</html>
1234567891011121314151617181920212223
效果:
当我点击了刘能,this和e.target分别返回的是ul和li

4.4 购物车案例
let adds = document.querySelectorAll('.add')
let reduces = document.querySelectorAll('.reduce')
let dels = document.querySelectorAll('.del')
let inputs = document.querySelectorAll('.count-c input')
let prices = document.querySelectorAll('.price')
let totals = document.querySelectorAll('.total')
let totalresult = document.querySelector('.total-price')
let totalNum = document.querySelector('#totalCount')
let tbody = document.querySelector('#carBody')
let all = document.querySelector('#all')
let scks = document.querySelectorAll('.s_ck')
//1.利用for循环绑定事件
for(let i = 0; i < adds.length; i++){
totals[i].innerText = prices[i].innerText
//点击增加数量
adds[i].addEventListener('click',function(){
//点击谁,让谁对应的输入框自增
inputs[i].value++
reduces[i].disabled = false
//小计模块
//利用parseInt 把后面符号去掉
totals[i].innerHTML = parseInt(prices[i].innerText) * inputs[i].value +'¥'
result()
})
//点击减少数量
reduces[i].addEventListener('click',function(){
//点击谁,让谁对应的输入框自增
inputs[i].value--
//进行if判断,等于1 禁用按钮
if(inputs[i].value <= 1){
this.disabled = true
}
//小计模块
//利用parseInt 把后面符号去掉
totals[i].innerHTML = parseInt(prices[i].innerText) * inputs[i].value +'¥'
result()
})
//删除操作
dels[i].addEventListener('click',function(){
tbody.removeChild(this.parentNode.parentNode)
result()
})
}
//2.封装函数,计算总价
function result(){
let scks = document.querySelectorAll('.s_ck')
let totals = document.querySelectorAll('.total')
let totalNum = document.querySelector('#totalCount')
let sum = 0
let num = 0
for(let i = 0; i < totals.length; i++){
if(scks[i].checked){
sum += parseInt(totals[i].innerText)
num += parseInt(inputs[i].value)
}
}
totalresult.innerHTML = sum + '¥'
totalNum.innerHTML = num
}
//全选
all.addEventListener('click',function(){
for(let i = 0; i < scks.length; i++){
scks[i].checked = all.checked
result()
}
// 当我们的全选按钮处于选中状态,则可以改为取消
if (all.checked) {
// console.log('要改')
all.nextElementSibling.innerHTML = '取消'
} else {
all.nextElementSibling.innerHTML = '全选'
}
})
//小按钮
for(let i = 0; i < scks.length; i++){
scks[i].addEventListener('click',function(){
result()
for(let j = 0; j < scks.length; j++){
if(scks[j].checked === false){
all.checked = false
all.nextElementSibling.innerHTML = '全选'
return
}else{
all.checked = true
all.nextElementSibling.innerHTML = '取消'
}
}
})
}
</script>

4.5 动态创建表格案例
<h1>新增学员</h1>
<div class="info">
姓名:<input type="text" class="uname">
年龄:<input type="text" class="age">
性别: <select name="gender" id="" class="gender">
<option value="男">男</option>
<option value="女">女</option>
</select>
薪资:<input type="text" class="salary">
就业城市:<select name="city" id="" class="city">
<option value="北京">北京</option>
<option value="上海">上海</option>
<option value="广州">广州</option>
<option value="深圳">深圳</option>
<option value="曹县">曹县</option>
</select>
<button class="add">录入</button>
</div>
<h1>就业榜</h1>
<table>
<thead>
<tr>
<th>学号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>薪资</th>
<th>就业城市</th>
<th>操作</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<script>
// 1. 准备好数据后端的数据
let arr = [
{ stuId: 1001, uname: '欧阳霸天', age: 19, gender: '男', salary: '20000', city: '上海' },
{ stuId: 1002, uname: '令狐霸天', age: 29, gender: '男', salary: '30000', city: '北京' },
{ stuId: 1003, uname: '诸葛霸天', age: 39, gender: '男', salary: '2000', city: '北京' },
]
let tbody = document.querySelector('tbody')
//添加数据按钮
let add= document.querySelector('.add')
//获取各个元素
let uname = document.querySelector('.uname')
let age = document.querySelector('.age')
let gender = document.querySelector('.gender')
let salary = document.querySelector('.salary')
let city = document.querySelector('.city')
function render() {
//先干掉以前的数据
tbody.innerHTML = ''
//根据数据的条数增加tr
for(let i = 0; i < arr.length; i++){
//创建tr
let tr = document.createElement('tr')
tr.innerHTML = `<tr>
<td>${arr[i].stuId}</td>
<td>${arr[i].uname}</td>
<td>${arr[i].age}</td>
<td>${arr[i].gender}</td>
<td>${arr[i].salary}</td>
<td>${arr[i].city}</td>
<td>
<a href="javascript:" id = "${i}">删除</a>
</td>
</tr> `
// tr.children[6].children[0].addEventListener('click',function() {
// alert('11')
// })
tbody.appendChild(tr)
//复原所有的表单数据
uname.value = age.value = salary.value = ''
gender.value = '男'
city.value = '北京'
}
}
render()
add.addEventListener('click',function () {
//获取id
let id = arr[arr.length - 1].stuId
id++
//获取表单的值,追加到数组arr
arr.push({
stuId:id,
uname:uname.value,
age: age.value,
gender:gender.value,
salary:salary.value,
city:city.value
})
console.log(arr);
//重新渲染数据
render()
})
//删除操作,删除的也是数组里面的数据 用事件委托
tbody.addEventListener('click',function (e) {
//点击a标签进行删除
if(e.target.tagName === 'A'){
//删除数据 arr.splice()
arr.splice(e.target.id,1)
render()
}
})
</script>


浙公网安备 33010602011771号