WebAPI
一、DOM
1. 作用和分类
作用:就是使用JS去操作html和浏览器
分类:DOM(文档对象模型)、BOM(浏览器对象模型)
2. 什么是DOM
DOM (Document Object Model —— 文档对象模型)是用来呈现以及与任意HTML 或XML文档交互的API
DOM作用:
开发网页内容特效实现用户交互
3. DOM树
DOM树是什么?
将HTML文档以树状结构直观的表现出来,我们称之为文档树或DOM树
描述网页内容关系的名词
作用:文档树直观的体现了标签与标签之间的关系
4. DOM 对象
DOM对象:浏览器根据html标签生成的js对象
所有的标签属性都可以在这个对象上面找到
修改这个对象的属性会自动隐射到标签身上
DOM的核心思想
把网页内容当做对象来处理
document对象
是DOM里提供的一个对象
所有他提供的属性和方法都是用来访问和操作网页内容的
例如:(document.write())
网页作用的内容都在document里面
5. 获取元素
5.1 选择匹配元素
// 语法:
document.querySelector('css选择器')
参数:
包含一个或多个有效的CSS选择器字符串
返回值:
CSS选择器匹配的第一个元素,一个HTMLElement对象。如果没有匹配到,则返回null
5.2 选择匹配多个元素
// 语法:
document.querySelectorAll('css选择器')
- 参数:
包含一个或多个有效的css选择器字符串
- 返回值:
css选择器匹配的NodeList对象集合
通过querySelectorAll获取过来的是一个伪数组:
有长度有索引号的数组
但是没有pop() push()等数组方法
5.3 获取元素方式
// 根据id获取一个元素
document.getElementById('nav')
// 根据 标签获取一类元素 获取页面所有div
document.getElementsByTagName('div')
// 根据 类名获取元素 获取页面所有类名为w的
document.getElementsByClassName('w')
二、样式操作案例
1.修改元素内容
1.1 元素.innerText属性
将文本内容添加 /更新到任意标签位置
文本中包含的标签不会被解析
// 1. 获取标签(元素)
let box = document.querySelector('div')
// 2. 修改标签(元素)内容 box是对象 innerText 属性
// 对象.属性 = 值
box.innerText = '有点意思~'
1.2 元素.innerHTML 属性
将文本内容添加 / 更新到任意标签位置
文本中的标签会被解析
// 1. 获取元素
let box = document.querySelector('div')
// 2. 修改标签
box.innerHTML = '<strong>美和易思</strong>'
1.3 三者区别
document.write() 方法 只能追加到body中
元素.innerText属性 只识别内容,不能解析标签
元素.innerHTML 属性 能够解析标签
推荐用(innerHTML)
// 随机点名
// 1. 获取元素
let box = document.querySelector('div')
// 2. 得到随机名字
// 随机函数
function getRandom (min,max) {
return Math.floor(Math.random() * max - min + 1) + min
}
let arr = ['关羽','张飞','刘备','马超','黄忠','曹操','赵云','貂蝉']
let random = getRandom(0,arr.length - 1)
// 3. 写入标签内部
box.innerHTML = arr[random]
arr.splice(arr[random],1)
2. 修改元素常用属性
还可以通过js设置 / 修改标签元素属性,比如通过src更换图片
最常见的属性比如:href、title、src
语法:
对象.属性 = 值
// 1. 获取元素
let pic = document.querySelector('img')
// 2. 修改元素属性 src
pic.src = './images/3.jpg'
pic.title = '这是一张图片'
// 随机图片
<img src="./images/course01.png" alt="">
<script>
// 1.获取图片元素
let pic = document.querySelector('img')
// 2. 随机图片
// 随机函数
function getRandom (min,max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
let num = getRandom(1,6)
// 3. 完成src属性赋值
pic.src = `./images/course0${num}.png`
</script>
3. 设置 / 修改元素样式属性
还可以通过js设置 / 修改标签元素的样式属性
比如通过轮播图小圆点自动更换样式颜色
点击按钮可以滚动图片,这是移动的图片的位置left等等
3.1 通过style属性操作css
语法;
对象.style.样式属性 = 值
<div> </div>
<script>
// 1. 获取元素
let box = document.querySelector('div')
// 2. 修改元素属性
box.style.backgroundColor = '#333'
box.style.width = '800px'
box.style.height = '500px'
box.innerHTML = '疾风亦有归途'
box.style.color = 'pink'
box.style.marginRight = '100px'
</script>
注意:
- 修改样式通过style属性引出
- 如果属性有-连接符,需要转换为小驼峰命名法
- 赋值的时候,需要的时候不要忘记加css单位
3.2 通过类名(className) 操作CSS
如果修改样式比较多,直接通过style属性修改比较繁琐,我们可以借助与css类名的形式
// 语法:
元素.className = 'active'
注意:
- 由于class是关键字,所以使用className去代替
- className是使用新值换旧值,如果需要添加一个类,需要保留之前的类名
<!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>
.box {
margin: 0 auto;
width: 300px;
height: 300px;
background-color: pink;
text-align: center;
line-height: 500px;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
// 1. 获取元素
let box = document.querySelector('div')
// 2. 修改元素属性
box.className = 'box'
</script>
</body>
</html>
3.3 通过classlist 操作类控制CSS
为了解决className容易覆盖以前的类名,我们可以通过classList方式追加和删除类名
// 追加一个类
元素.classList.add('类名')
// 删除一个类
元素.classList.remove('类名')
// 切换一个类
元素.classList.toggle('类名')
// 1. 获取元素
let box = document.querySelector('div')
// 添加
box.classList.add('active')
// 删除
box.classList.remove('one')
// 切换
box.classList.toggle('one')
4. 修改表单
表单很多情况,也需要修改属性,比如点击眼睛,可以看到密码,本质是把表单类型转换为文本框
<input type="text" value="请输入">
<button disabled>点击</button>
<input type="checkbox" name="" id="" class="check">
<script>
// 1. 获取元素
let input = document.querySelector('input')
let btn = document.querySelector('button')
let checkbox = document.querySelector('.check')
// 2. 修改元素属性
input.value = '账号'
input.type = 'password'
// 3. 按钮是否禁用
btn.disabled = true
btn.disabled = false
// 4. 复选框默认勾选
checkbox.checked = true
</script>
5. 定时器间歇函数
定时器函数可以开启和关闭定时器
1.开启定时器
setInterval(函数,间隔时间)
作用:每隔一段时间调用这个函数
间隔时间单位是毫秒
2.关闭定时器
let 变量名 = setInterval(函数,间隔时间)
clearInterval(变量名)
// 用户注册倒计时
<button class="btn" disabled>我已经阅读用户协议(6)</button>
<script>
// 1. 获取元素
let btn = document.querySelector('.btn')
// 2. 用一个变量计数
let i = 5
// 开启定时器
let timer = setInterval(function () {
i--
btn.innerHTML = `我已经阅读用户协议(${i})`
if (i === 0) {
clearInterval(timer)
// 开启按钮
btn.disabled = false
btn.innerHTML = `我同意该协议`
}
},1000)
</script>
// 焦点图案
<script>
let data = [
{
imgSrc: 'images/b01.jpg',
title: '挑战云歌单,欢迎你来'
},
{
imgSrc: 'images/b02.jpg',
title: '田园日记,上演上京记'
},
{
imgSrc: 'images/b03.jpg',
title: '甜蜜攻势再次回归'
},
{
imgSrc: 'images/b04.jpg',
title: '我为歌狂,生为歌王'
},
{
imgSrc: 'images/b05.jpg',
title: '年度校园主题活动'
},
{
imgSrc: 'images/b06.jpg',
title: 'pink老师新歌发布,5月10号正式推出'
},
{
imgSrc: 'images/b07.jpg',
title: '动力火车来到西安'
},
{
imgSrc: 'images/b08.jpg',
title: '钢铁侠3,英雄镇东风'
},
{
imgSrc: 'images/b09.jpg',
title: '我用整颗心来等你'
},
]
// 1. 获取元素
let pic = document.querySelector('.pic')
let text = document.querySelector('.text')
// 2. 设置初始变量
let i = 0
// 3. 开启定时器
setInterval(function() {
i++
pic.src = data[i].imgSrc
text.innerHTML = data[i].title
// 判断边界
if(i === data.length - 1) {
i = -1
}
},1000)
</script>
三、事件
1. 事件
1.1 什么是事件?
事件是在编程时系统内容发生的动作或者发生的事情
比如用户在网页上点击一个按钮
1.2 什么是事件监听
就是让程序检测是否有事件产生,一旦有事件触发,就立即调用一个函数做出响应,也称为注册事件
1.3 语法:
元素.addEventListener('事件',要执行的函数)
1.4 事件监听三要素:
- 事件源:那个dom元素被事件触发了,要获取dom元素
- 事件:用什么方式触发,比如鼠标单击click、鼠标经过mouseover等
- 事件调用的函数:要做什么事
<button>点击</button>
<script>
// 1.获取元素
let btn = document.querySelector('button')
// 2. 注册事件
btn.addEventListener('click' , function(){
alert('被点击了')
})
</script>
1.5 注意:
- 事件类型要加引号
- 函数是点击之后再去执行,每次点击都会执行一次
// 点名按钮
// 数据数组
let arr = ['马超', '黄忠', '赵云', '关羽', '张飞']
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
// 1. 获取元素
let qs = document.querySelector('.qs')
let start = document.querySelector('.start')
let end = document.querySelector('.end')
let random = 0
let timer = 0
// 2. 绑定开始事件
start.addEventListener('click',function(){
// 开启定时器随机抽取数据
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(){
// 关闭定时器
clearInterval(timer)
// 删除元素
arr.splice(random,1)
})
四、事件类型
1. 事件类型
1.1 鼠标触发事件
- click 鼠标点击
- mouseenter 鼠标经过
- mouseleave 鼠标离开
1.2 表单获得光标
- focus 获得焦点
- blur 失去焦点
let search = document.querySelector('input')
let list = document.querySelector('.result-list')
// 2. 绑定获取焦点事件
search.addEventListener('focus',function (){
// 显示下拉菜单
list.style.display = 'block'
// 文本框变色
search.className = 'search'
})
// 3. 绑定失去
search.addEventListener('blur',function(){
// 隐藏下拉菜单
list.style.display = 'none'
// 取消文本框颜色
search.classList.remove('search')
})
1.3 键盘触发
- keydown 键盘按下触发
- keyup 键盘抬起触发
1.4 表单输入触发
- input 用户输入事件
// 1. 获取元素
let area = document.querySelector('#area')
let useCount = document.querySelector('.useCount')
// 2. 绑定键盘输入事件
area.addEventListener('input',function(){
useCount.innerHTML = area.value.length
})
2. 多个元素绑定事件
// 1. 获取元素
let all = document.querySelector('#checkAll')
let cks = document.querySelectorAll('.ck')
let span = document.querySelector('span')
// 2. 绑定点击事件
all.addEventListener('click',function(){
// 遍历伪数组依次赋值
for(let i = 0; i < cks.length; i++) {
cks[i].checked = all.checked
}
if(all.checked === true) {
span.innerHTML = '取消'
} else {
span.innerHTML = '全选'
}
})
// 3.给多个元素绑定事件
for(let i = 0; i < cks.length; i++){
cks[i].addEventListener('click',function(){
for(let j = 0; j < cks.length; j++){
if(cks[j].checked === false){
all.checked = false
return
}
}
all.checked = true
})
}
// 购物车案例
<div>
<input type="text" id="total" value="1" readonly>
<input type="button" value="+" id="add">
<input type="button" value="-" id="reduce" >
<script>
// 1. 获取元素
let total = document.querySelector('#total')
let add = document.querySelector('#add')
let reduce = document.querySelector('#reduce')
// 2. 点检加号事件监听
add.addEventListener('click',function(){
total.value++
if(total.value > 1){
reduce.disabled = false
}
})
// 3. 点击减号事件监听
reduce.addEventListener('click',function(){
total.value--
if(total.value <= 1){
reduce.disabled = true
}
})
</script>
</div>
3. 高阶函数
- 高阶函数可以别简单理解为函数的高级应用,JavaScript中函数可以被当成[值] 来对待,基于这个特性实现函数的高级应用
- [值] 就是JavaScript中的数据,如数值、字符串、布尔、对象
1. 回调函数
如果将函数A做为参数传递给函数B时,我们称函数A为回调函数
简单理解:当一个函数当做参数来传递给另一个函数的时候,这个函数就是回调函数
// 回调函数
function fn() {
}
setInterval(fn,1000)
2. 环境对象
环境对象指的是函数内部特殊的变量this,它代表着当前函数运行时所处的环境
作用:弄清楚this的指向,可以让我们代码更简洁
函数的调用方式不同,this指代的对象也不同
[谁调用,this就是谁] 是判断this指向的粗略规则
3. 排他思想
当前元素为A状态,其他元素为B状态
使用:
- 干掉所有人 :使用for循环
- 复活他自己:通过this或者下标找到自己或者对应的元素
// 排他思想
// 1. 获取元素
let btn = document.querySelectorAll('button')
// 2. 监听事件
for(let i = 0; i < btn.length; i++){
btn[i].addEventListener('click',function(){
this.style.backgroundColor = 'pink'
for(let j = 0; j < btn.length; j++){
btn[j].style.backgroundColor = ""
}
this.style.backgroundColor = 'pink'
})
}
// 1.获取元素
let lis = document.querySelectorAll('.tab .tab-item')
let divs = document.querySelectorAll('.products .main')
// 2. 事件监听
for(let i = 0; i < lis.length; i++) {
lis[i].setAttribute('index',i)
lis[i].addEventListener('click',function(){
// 拿到当前active类移除掉
document.querySelector('.tab .active').classList.remove('active')
this.classList.add('active')
// 底部模块隐藏
let index = this.getAttribute('index')
for (let j = 0; j < divs.length; j++){
divs[j].style.display = 'none'
}
divs[index].style.display = 'block'
})
}
4. 优化
// 1.获取元素
let lis = document.querySelectorAll('.tab .tab-item')
let divs = document.querySelectorAll('.products .main')
// 2. 事件监听
for(let i = 0; i < lis.length; i++) {
lis[i].addEventListener('click',function(){
// 拿到当前active类移除掉
document.querySelector('.tab .active').classList.remove('active')
this.classList.add('active')
// 底部模块隐藏
document.querySelector('.products .active').classList.remove('active')
// 对应事件加上active
divs[i].classList.add('active')
})
}
五、节点操作
1. DOM节点
DOM树里每一个内容都称之为节点
1.1 节点类型
元素节点
所有的标签比如body、div
html是根节点
属性节点
所有的属性比如 href
文本节点
所有的文本
其他
2. 查找父节点
let son = document.querySelector('.son')
son.addEventListener('click',function(){
this.parentNode.style.display = 'none'
})
3. 查找子节点
3.1子节点查找:
childNodes
获得所有子节点、包括文本特点(空格、换行)、注释节点等
children(重点)
仅获得所有元素节点
返回的还是一个伪数组
父元素.children
// 1. 获取元素
let btn = document.querySelector('button')
let ul = document.querySelector('ul')
// 2. 事件监听
btn.addEventListener('click',function(){
for(let i = 0; i < ul.children.length; i++){
ul.children[i].style.color = 'pink'
}
ul.children[0].style.color = 'red'
})
4. 兄弟关系查找
4.1 下一个兄弟节点
nextElementSibling属性
4.2 上一个兄弟节点
previousElementSibling属性
// 1.获取元素
let btn = document.querySelector('button')
let tow = document.querySelector('.two')
// 2. 给按钮绑定点击事件
btn.addEventListener('click',function(){
tow.nextElementSibling.style.color = 'red'
tow.previousElementSibling.style.color = 'pink'
})
5. 增加节点
5.1 创建节点
即创造出一个新的网页元素,再添加到网页内,一般先创建节点,然后插入节点
创建元素节点的方法:
document.createElement(‘标签名’)
// 1. 创建信的标签节点
let li = document.createElement('li')
5.2 追加节点
- 想要在界面看到,还得插入到某个父元素中
插入到父元素的最后一个子元素:
父元素.appendChild(要插入的元素)
let ul = document.querySelector('ul')
let lis = document.querySelector('li:first-child')
// 1. 创建节点
let li = document.createElement('li')
li.innerHTML = '我是一个小li'
// 2.追加节点
ul.appendChild(li)
li.style.color = 'pink'
lis.style.color = 'red'
// 插入到父元素中某个子元素的前面
父元素.insertBefore(要插入的元素,在那个元素前面)
// 追加节点案例
// 1. 获取元素
let ul = document.querySelector('ul')
// 2. 循环添加li
for(let i = 0; i < data.length; i++){
// 3. 创建节点
let li = document.createElement('li')
// 5. 追加内容
li.innerHTML = `
<img src="${data[i].src}" alt="">
<h4>
${data[i].title}
</h4>
<div class="info">
<span>高级</span> • <span> ${data[i].num}</span>人在学习
</div>
`
// 4. 给父元素追加节点
ul.appendChild(li)
6. 克隆节点
// 克隆一个已有的元素节点
元素.cloneNode(布尔值)
cloneNode会克隆出一个跟原标签一样的元素,括号内传入布尔值
若为true,则代表克隆时会包含后代节点一起克隆
若为false,则代表克隆时不包含后代节点
默认为false
// 1. 获取元素
let ul = document.querySelector('ul')
let li = document.querySelector('li')
let newLi = li.cloneNode()
let newUl = ul.cloneNode(true)
// 2. 追加到ul里面
ul.appendChild(newUl)
7.删除节点
- 若一个节点在页面中已经不需要时,可以删除他
- 在JavaScript原生DOM操作中,要删除元素必须通过父元素删除
// 语法:
父元素.removeChild(要删除的元素)
注意:
- 如不存在父子关系则删除不成功
- 删除节点和隐藏节点(display:none)有区别的:隐藏节点还是存在的,但是删除,则从html中删除节点
// 1.获取元素
let btn = document.querySelector('button')
let ul = document.querySelector('ul')
// 2.注册点击事件删除节点
btn.addEventListener('click',function(){
ul.removeChild(ul.children[0])
})
六、事件对象
1. 实例化
在代码中发现了new关键字时,一般将这个操作称为实例化
// 获得当前时间
let date = new Date()
// 获得指定时间
let date = new Date('2022-7-1 18:30:00')
2. 时间对象方法
方法 | 作用 | |
---|---|---|
getFullYear() | 获得年份 | 获取四位年份 |
getMonth() | 获得月份 | 取值为0 ~ 11 |
getDate() | 获取月份中的每一天 | 不同月份取值也不相同 |
getDay() | 获取星期 | 取值为0 ~ 6 |
getHours() | 获取小时 | 取值为0 ~ 23 |
getMinutes() | 获取分钟 | 取值为0 ~ 59 |
getSeconds() | 获取秒 | 取值为0 ~ 59 |
let arr = ['星期一','星期二','星期三','星期四','星期五','星期六','星期天']
let box = document.querySelector('div')
getTimer() // 先调用一次解决1秒空白
setInterval(getTimer,1000)
function getTimer (){
// 实例化时间对象
let date = new Date()
// 获取当前时间
let year = date.getFullYear()
let month = date.getMonth() + 1
let day = date.getDate()
// 获取时分秒
let hour = date.getHours()
let min = date.getMinutes()
let sec = date.getSeconds()
let day1 = date.getDay()
box.innerHTML = `今天是:${year}年${month}月${day}日 ${hour}:${min}:${sec} ${arr[day1]}`
}
3. 时间戳
3.1 什么是时间戳
是指2018年01月01日00时00分00秒起至现在的秒数,他是一个特殊的计量时间的方式
3.2 获取时间戳
// 1.使用getTime()方法
let date = new Date()
console.log(date.getTimer())
// 2. 简写+new Date()
console.log(+new Date())
// 3. 使用Date().now()
console.log(Date.now())
// 无需实例化
// 但是只能得到当前的时间戳,而前面两种可以返回指定时间的时间戳
3.3 倒计时
// 5. 获取元素
let hour = document.querySelector('#hour')
let minutes = document.querySelector('#minutes')
let scond = document.querySelector('#scond')
let tips = document.querySelector('.tips')
let next = document.querySelector('.next')
getTimer()
setInterval(getTimer,1000)
function getTimer(){
// 1.获取现在的时间戳
let now = +new Date()
// 2. 获取指定时间的时间戳
let last = +new Date('2022-7-1 21:00:00')
// 3. 计算剩余时间
let count = (last - now) / 1000
// 4. 转换为时分秒
let h = parseInt(count / 60 / 60 % 24)
h = h < 10 ? '0' + h : h
let m = parseInt(count / 60 % 60)
m = m < 10 ? '0' + m : m
let s = parseInt(count % 60)
s = s < 10 ? '0' + s : s
hour.innerHTML = h
minutes.innerHTML = m
scond.innerHTML = s
}
Time()
setInterval(Time,1000)
function Time (){
// 6 获取当前时间
let date = new Date()
let hour = date.getHours()
let min = date.getMinutes()
min = min < 10 ? '0' + min : min
let sec = date.getSeconds()
sec = sec < 10 ? '0' + sec : sec
tips.innerHTML = `现在是${hour}:${min}:${sec}`
}
Dya()
function Dya(){
let date = new Date()
let yaer = date.getFullYear()
let month = date.getMonth() + 1
let day = date.getDate()
next.innerHTML = `今天是${yaer}年${month}月${day}日`
}
3.4 总和案例–微博发布信息
// 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. 获取元素
let textarea = document.querySelector('textarea')
let useCount = document.querySelector('.useCount')
let btn = document.querySelector('#send')
let list = document.querySelector('#list')
// 2. 给文本框绑定键盘输入事件
textarea.addEventListener('input',function(){
useCount.innerHTML = this.value.length
})
// 3. 给发布按钮绑定点击事件
btn.addEventListener('click',function(){
if(textarea.value.trim() === ''){
textarea.value = ''
useCount.innerHTML = 0
return alert('内容不能为空!')
}
// 随机换头像图片
function getRandom (min,max){
return Math.floor(Math.random() * (min,max + 1)) + min
}
let random = getRandom(0,dataArr.length - 1)
// 创建一个li追加到ul后面
let li = document.createElement('li')
// 随机获取数组里面的内容
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().toLocaleDateString()}</p>
</div>
<div class="content">${textarea.value}</div>
<span class="the_del">X</span>
`
let del = li.querySelector('.the_del')
del.addEventListener('click',function(){
list.removeChild(li)
})
// 追加li
list.insertBefore(li,list.children[0])
// 发布完清空文本框
textarea.value = ''
useCount.innerHTML = 0
})
// 4. 绑定回车事件
textarea.addEventListener('keyup',function(e){
if(e.key === 'Enter'){
btn.click()
}
})
综合案例
// 1.获取元素
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 totalPrice = document.querySelector('.total-price')
// 获取商品件数
let totalCount = document.querySelector('#totalCount')
// tbody 获取过里
let carBody = document.querySelector('#carBody')
// 2.给加号绑定点击事件添加操作
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
// 计算总价 = 单价 * 数量
totals[i].innerHTML = parseInt(prices[i].innerHTML) * inputs[i].value + '¥'
// 计算现在的总额 调用
result()
})
// 3.给减号绑定点击事件
reduces[i].addEventListener('click',function(){
// 点击后对应输入框里的值自减
inputs[i].value--
// 计算总价 = 单价 * 数量
totals[i].innerHTML = parseInt(prices[i].innerHTML) * inputs[i].value + '¥'
// 判断数值是否小于0
if(inputs[i].value <= 0){
reduces[i].disabled = true
}
// 计算现在的总额 调用
result()
})
// 4.删除模块
dels[i].addEventListener('click',function(){
// 要删除的是当前元素爸爸的爸爸
carBody.removeChild(this.parentNode.parentNode)
result()
})
}
// 计算总价
function result(){
// 获取商品件数
let totalCount = document.querySelector('#totalCount')
// 小计 = 单价 * 数量
let totals = document.querySelectorAll('.total')
let sum = 0
let num = 0
for(let i = 0; i < totals.length; i++){
sum += parseInt(totals[i].innerText)
num += parseInt(inputs[i].value)
}
totalPrice.innerText = sum + '¥'
totalCount.innerText = num
}
result()
4. 重绘和回流(面试点)
4.1 浏览器是如何进行页面渲染的?
- 解析(Parser)HTML,生成DOM树(DOM Tree)
- 同时解析(Parser) CSS,生成样式规则(Style Rules)
- 根据DOM树和样式规则,生成渲染数(Render Tree)
- 进行布局Layout(回流 / 重排 ):根据生成的渲染树,得到节点的几何信息(位置,大小)
- 进行绘制Painting(重绘):根据计算和获取的信息进行整个页面的绘制
- Display:展示在页面上
4.2 回流(重排)
当Render Tree中部分或者全部元素的尺寸、结构、布局等发生改变时,浏览器就会重新渲染部分或全部文档的过程称为回流
4.3 重绘
由于节点(元素)的样式的改变并不影响他在文档流中的位置和文档布局时(比如:color、background-color、outline等)称为重绘
重绘不一定引起回流,而回流一定会引起重绘
七、事件高级
1. 事件对象
1.1事件对象是什么?
- 也是个对象,这个对象里有事件触发时的相关信息
- 例如:鼠标点击事件中,事件对象就存了鼠标点在那个位置等信息
1.2 如何获取
- 在事件绑定的回调函数的第一个参数就是事件对象
- 一般命名为event、ev、e
// 事件对象
元素.addEventListener('click',function(e){
})
1.3 部分常有属性:
type:获取当前的事件类型
clientX/clientY:获取光标相对于浏览器可见窗口左上角的位置
offsetX/offsetY:获取光标相对于当前DOM元素左上角的位置
key:用户按下的键盘键的值
现在不提倡使用keyCode
// 图片跟随鼠标移动
// 1. 获取元素
let pic = document.querySelector('img')
document.addEventListener('mousemove',function(e){
pic.style.left = e.pageX + 'px'
pic.style.top = e.pageY + 'px'
})
2. 事件流
事件流指的是事件完整执行过程中的流动路径
说明:假设页面里有个div,当触发事件时,会经历连个阶段,分别是捕获阶段、冒泡阶段
简单来说:捕获阶段是从父到子 冒泡阶段是从子到父
2.1 冒泡事件概念:
当一个元素的事件被触发时,同样的事件将会在该元素的作用祖先元素中依次被触发。这一过程被称为事件冒泡
简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的同名事件
冒泡事件是默认存在的
let father = document.querySelector('.father')
let son = document.querySelector('.son')
father.addEventListener('click',function(){
alert('我是爸爸')
})
son.addEventListener('click',function(){
alert('我是儿子')
})
document.addEventListener('click',function(){
alert('我是爷爷')
})
2.2 事件捕获概念
从DOM的根元素开始去执行对应的事件(从外到里)
事件捕获需要写对应代码才能看到效果
DOM.addEventListener(事件类型,事件处理函数,是否使用事件捕获机制)
说明:
- addEventListener第三个参数传入true代表是捕获阶段触发(很少使用)
- 若传入false代表冒泡阶段触发,默认就是false
- 若是用LO事件监听,则只有冒泡阶段,没有捕获
let father = document.querySelector('.father')
let son = document.querySelector('.son')
father.addEventListener('click',function(),true{
alert('我是爸爸')
})
son.addEventListener('click',function(),true{
alert('我是儿子')
})
document.addEventListener('click',function(),true{
alert('我是爷爷')
})
3. 阻止事件流动
- 因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素
- 若想把事件就限制在当前元素内,就需要阻止事件流动
- 阻止事件流动需要拿到事件对象
事件对象.stopPropagation()
let father = document.querySelector('.father')
let son = document.querySelector('.son')
father.addEventListener('click',function(e){
alert('我是爸爸')
e.stopPropagation()
})
son.addEventListener('click',function(e){
alert('我是儿子')
e.stopPropagation()
})
document.addEventListener('dblclick',function(){
alert('我是爷爷')
})
3.1 鼠标经过事件
mouseenter和mouseleave 没有冒泡效果(推荐)
4. 阻止默认行为,比如链接点击不跳转,表单域的跳转
// 语法:
e.preventDefault()
5. 两种注册事件的区别:
5.1 传统on注册(L0)
- 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
- 直接使用null覆盖偶就可以实现事件的解绑
- 都是冒泡阶段执行的
5.2 事件监听注册(L2)
- 语法:addEventListener(事件类型,事件处理函数,是否使用捕获)
- 后面注册的事件不会覆盖前面注册的事件(同一个事件)
- 可以通过第三个参数去确定是在冒泡或者捕获阶段执行
- 必须使用removeEventListener(事件类型 ,事件处理函数,获取捕获或者冒泡阶段)
- 匿名函数无法被解绑
<button>点击</button>
<button class="btn">点我</button>
<script>
let btn = document.querySelector('button')
let btn2 = document.querySelector('.btn')
// 1. 绑定事件
btn.onclick = function(){
alert('第一次')
}
btn.onclick = function(){
alert('第二次')
}
// 解绑事件
btn.onclick = null
// 2. 绑定事件
btn2.addEventListener('click',fun)
function fun(){
alert(11)
}
// 解绑事件
btn2.removeEventListener('click',fun)
6. 事件委托
事件委托是利用事件流的特征解决一些开发需求的知识技巧
优点:给父级元素加事件(可以提高性能)
原理:事件委托其实是利用事件冒泡的特点,给父元添加事件,子元素可以触发
实现:事件对象.target可以获得真正触发事件的元素
let ul = document.querySelector('ul')
ul.addEventListener('click',function(e){
e.target.style.color = 'pink'
})
6.1动态创建表格
// 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')
// 1. 获取录入按钮
let add = document.querySelector('.add')
// 2. 获取表单元素
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 = ''
for(let i = 0; i < arr.length; i++){
// 1. 创建tr
let tr = document.createElement('tr')
// 2. tr里面放内容
tr.innerHTML = `
<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>
`
// 3. 把tr追加给today 父元素.appendChild(子元素)
tbody.appendChild(tr)
// 复原表单所有数据
uname.value = age.value = salary.value = ''
gender.value = '男'
city.value = '北京'
}
}
render()
add.addEventListener('click',function(){
// 获取表单里面的值然后追加给数组
arr.push({
// 得到数组最后一条数据加一
stuId: arr[arr.length - 1].stuId + 1,
uname: uname.value,
age: age.value,
gender: gender.value,
salary: salary.value,
city: city.value
})
// 重新渲染
render()
})
// 删除操作 使用事件委托
tbody.addEventListener('click',function(e){
if(e.target.tagName === 'A'){
arr.splice(e.target.id,1)
// 删除完之后重新渲染
render()
}
})
八、网页特效
1. 手风琴效果
// 1. 获取元素
let lis = document.querySelectorAll('li')
for(let i = 0; i < lis.length; i++){
// 2. 绑定鼠标经过事件
lis[i].addEventListener('mouseenter',function(){
// 排他思想,先让所有的都变成100最后给自己加800
for(let j = 0; j < lis.length; j++){
lis[j].style.width = '100px'
}
this.style.width = '800px'
})
// 3. 绑定鼠标离开事件
lis[i].addEventListener('mouseleave',function(){
for(let i = 0; i < lis.length; i++){
lis[i].style.width = '240px'
}
})
}
2. 滚动事件和加载事件
2.1 滚动事件
当页面进行滚动时触发的事件
为什么要学?
很多网页需要检测用户把页面滚动到某个区域后做一些处理,比如固定导航栏,比如返回发哦顶部
事件名:scroll
监听整个页面的滚动
// 页面滚动事件
window.addEventListener('scroll',function(){
// 执行的操作
})
给window或document添加scroll事件
2.2 加载事件
加载外部资源(如图片,外联CSS和JavaScript等)加载完毕时触发的事件
为什么要学?
- 有些时候需要等页面资源全部处理完了做一些事情
- 老代码喜欢把script写在head中,这时候直接找dom元素找不到
事件名:load
window.addEventListener('load',function(){
// 执行的操作
})
注意:
不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件
2.3 DOMContentLoaded
- 当初始的HTML文档被完全加载的和解析完成后,DOMContentLoaded事件被触发,而无需等待样式表、图像等完全加载
- 事件名:DOMContentLoaded
- 监听页面DOM加载完毕:给document添加DOMContentLoaded事件
document.addEventListener('DOMContentLoaded',function(){
// 执行的操作
})
3 元素大小和位置
3.1 scroll家族
使用场景:
我们想要页面滚动一段距离,比如100px,就让某些元素显示隐藏,那我们怎么知道,页面滚动了100像素呢?
就可以使用scroll来检查页面滚动距离
获取宽高:
- 获取元素的内容总宽高(不包含滚动条)返回值不带单位
- scrollWidth和scrollHeight
获取位置:
- 获取元素内容往左、往上滚出去看不到的距离
- scrollLeft和scrollTop
- 这两个属性是可以修改的
3.2 检测页面滚动的距离
语法:
document.documentElement.scrollTop
3.2.1 彷新浪案例
// 1. 获取元素
let backtop = document.querySelector('.backtop')
// 2. 绑定页面滚动事件
window.addEventListener('scroll',function(){
let num = document.documentElement.scrollTop
if(num >= 500) {
backtop.style.display = 'block'
} else {
backtop.style.display = 'none'
}
})
// 2.点击链接返回顶部
backtop.children[1].addEventListener('click',function(){
document.documentElement.scrollTop = 0
})
3.2.2 彷京东固定头部
let sk = document.querySelector('.sk')
let header = document.querySelector('.header')
window.addEventListener('scroll',function(){
if(document.documentElement.scrollTop >= sk.offsetTop) {
header.style.top = 0
} else {
header.style.top = '-80px'
}
})
3.3 offset家族
使用场景
通过js的方式,得到元素在页面中的的位置
获取宽高:
- 获取元素的自身宽高、包含元素自身设置的宽高、padding、border
- offsetWidth和offsetHeight
获取位置:
- 获取元素距离自己定位父级元素的左、上距离
- offsetLeft和offsetTop 注意是只读属性
3.3.1 电梯导航案例
// 1. 获取元素
let items = document.querySelectorAll('.item')
let neirong = document.querySelectorAll('.neirong')
// 2. 点击左侧aside模块 谁高亮
for(let i = 0; i < items.length; i++){
items[i].addEventListener('click',function(){
// 找到上一个元素active移除他
document.querySelector('.aside .active').classList.remove('active')
// 点击谁谁添加
this.classList.add('active')
document.documentElement.scrollTop = neirong[i].offsetTop
})
}
3.4 client家族
获取宽高
- 获取元素的可见部分宽高(不包含边框,滚动条)
- clientWidth和clientHeight
会在窗口尺寸改变的时候触发事件:
window.addEventListener('resize',function(){
// 执行的代码
})
检测屏幕宽度:
window.addEventListener('resize',function(){
lew w = document.documentElement.clientWidth
console.log(w)
})
4. 综合案例(轮播图)
// 轮播图开始
// 1.需求鼠标经过小图标 当前图标高亮 添加类
// 获取元素
let lis = document.querySelectorAll('.indicator li')
// 获取图片元素
let piclis = document.querySelectorAll('.slides ul li')
// 获取图片文字元素
let text = document.querySelector('.extra h3')
// 获取右侧点击按钮
let next = document.querySelector('.next')
// 获取左侧点击按钮
let prev = document.querySelector('.prev')
// 获取大盒子元素
let main = document.querySelector('.main')
for (let i = 0; i < lis.length; i++){
// 给小图标绑定鼠标经过事件
lis[i].addEventListener('mouseenter',function(){
// 选出唯一的那个类删除他
document.querySelector('.indicator .active').classList.remove('active')
// 给当先鼠标经过加上类
this.classList.add('active')
// 2. 需求大图跟随小图标变化
document.querySelector('.slides ul .active').classList.remove('active')
piclis[i].classList.add('active')
text.innerHTML = `第${i + 1}张图的描述信息`
// 4. 解决一个bug,点击右侧按钮可以实现播放下一张,但鼠标经过前面的会乱序
index = i
})
}
// 3. 右侧按钮播放效果
let index = 0
// 给右侧按钮绑定点击事件
next.addEventListener('click',function(){
index++
// if (index === lis.length){
// index = 0
// }
index = index % lis.length
common()
})
// 5. 左侧按钮播放效果
prev.addEventListener('click',function(){
index--
if(index < 0){
index = lis.length - 1
}
common()
})
// 6. 应为左右侧按钮有大量相同的操作可以封装一个函数 common
function common (){
// 选出小图标
document.querySelector('.indicator .active').classList.remove('active')
// 给当先鼠标经过加上类
lis[index].classList.add('active')
// 选出大图片
document.querySelector('.slides ul .active').classList.remove('active')
piclis[index].classList.add('active')
text.innerHTML = `第${index + 1}张图的描述信息`
}
// 7. 开启定时器实现自动播放
let timer = setInterval(function(){
// 自动调用右侧点击事件
next.click()
},2000)
// 8. 关闭定时器
main.addEventListener('mouseenter',function(){
clearInterval(timer)
})
main.addEventListener('mouseleave',function(){
timer = setInterval(function(){
// 自动调用右侧点击事件
next.click()
},2000)
})
九、Window对象
1. BOM(浏览器模型)
1.1 BOM(Browser Object Model)是浏览器对象模型
- window是浏览器内置中的全局对象,我们所学习的所有Web APIs 的知识内容都是 基于window对象实现的
- window对象不包含了navigator、location、document、history、screen 5个属性,即所谓的BOM(浏览器对象模型)
- document是实现DOM的基础,他其实是依附于window的属性
- 依附于window对象的所有属性和方法,使用时可以省略window
2. 定时器-延迟函数
JavaScript内置的一个用来让代码延迟执行的函数,叫setTimeout
语法:
setTimeout(回调函数,等待的毫秒数)
setTimeout仅仅只执行一次,所有可以理解为就是一段代码延迟执行,平时忽略Windows
// 图片2秒自动关闭
<img src="./images/ad.png" alt="">
<script>
let img = document.querySelector('img')
setTimeout(function(){
img.style.display = 'none'
},2000)
</script>
// 利用递归模仿setinterval
let div = document.querySelector('div')
function fn (){
div.innerHTML = new Date().toLocaleString()
setTimeout(fn,1000)
}
fn()
3 . js执行机制
3.1 Js是单线程
JavaScript语言的一大特点是单线程,也就是说,同一个事件只能做一件事。这是因为JavaScript这门脚本语言诞生的使命所致——JavaScript是为了处理页面中用户的交互,以及操作DOM而诞生的。比如我们对某个DOM元素进行添加和删除操作,不能同时进行。一个先进行添加,之后在删除
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是:如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉
3.2 同步和异步
为了解决这个问题,利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JavaScript脚本创建多个线程。于是,JS中出现了同步和异步
同步
前一个任务结束后再去执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。比如做饭的同步做法:我们先烧水煮饭,等水开了(10分钟后),再去切菜,炒菜
异步
你在做一件事情,因为这件事情会花费很长的时间,在做这件事情的同时,你还可以去处理其他事情。比如做饭的异步做法,我们在烧水的同时,利用这10分,去切菜,炒菜
3.3 执行顺序
- 先执行执行栈中的同步任务
- 异步任务放入任务队列中
- 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进行执行栈,开始执行。
由于主线程不断的重复获取任务、执行任务、再获取任务、再获取任务、在执行,所有这种机制被称为事件循环
4. location对象
location的数据类型是对象,他拆分并保存了URL地址的各个组成部分
4.1 常用方法和属性:
- href属性获取完整的URL地址,对其赋值时用于地址的跳转
- search属性获取地址携带的参数,符号?后面部分
- hash属性获取地址中的哈希值,符号#后面部分
- reload方法用来刷新当前页面,传入参数true时表示强制刷新
4.2 location.href的使用
<a href="https://www.itcast.cn/">支付成功<span>5</span>秒钟后跳转首页</a>
<script>
let a = document.querySelector('a')
let num = 5
let timer = setInterval(function(){
num--
a.innerHTML = `支付成功<span>${num}</span>秒钟后跳转首页`
if(num === 0){
clearInterval(timer)
location.href = 'https://www.itcast.cn/'
}
},1000)
4.2.1 location.search
属性当用和方法:
search属性获取地址中携带的参数,符号?后面部分
console.log(location.search)
4.2.2 location.hash
常用属性和方法:
hash属性获取地址中的哈希值,符号#后部分
console.log(location.hash)
后期vue路由的铺垫,经常用于不刷新页面,显示不 同页面,比如网页云音乐
4.2.3 location.reload
常用属性和方法:
reload方法用来刷新当前页面,传入参数true时表示强制刷新
let btn = document.querySelector('button')
btn.addEventListener('click',function(){
// reload() 刷新方法 有本地缓存 强制刷新 ctrl + f5
location.reload(true)
})
5. navigator对象
通过userAgent检测浏览器的版本及平台
// 检测 userAgent(浏览器信息)
!(function () {
const userAgent = navigator.userAgent
// 验证是否为Android或iPhone
const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
// 如果是Android或iPhone,则跳转至移动站点
if (android || iphone) {
location.href = 'http://m.itcast.cn'
}
})()
6. histroy对象
history的数据类型是对象,该对象与浏览器地址栏的操作相对应,如前进,后退,历史记录等
history对象方法 | 作用 |
---|---|
back() | 可以后退功能 |
forward() | 前进功能 |
go(参数) | 前进后退功能参数如果是1前进1个页面如果是-1后退1一个页面 |
十、swiper插件
1. swiper插件的基本使用
1.1 插件
插件:就是别人写好的一些代码,我们只需要复制对应的代码,就可以直接实现对应的效果
学习插件的基本过程:
- 熟悉官网,了解这个插件可以完成什么需求 https://www.swiper.com.cn/
- 看在线演示,知道找到符合自己需求的demo https://www.swiper.com.cn/demo/index.html
- 查看基本使用流程 https://www.swiper.com.cn/usage/index.html
- 查看API文档,去配置自己的插件 https://www.swiper.com.con/api/index.html
- 注意:多个swiper同时使用的时候,类名需要区分
2. 本地存储
2.1 本地存储特性
随着互联网的快速发展,基于网页的应用越来越普遍,同时也变的越来越复杂,为了满足各种各样的需求,会经常性在本地存储池大量的数据,HTML5规范提出了相关的解决方案
- 数据存储在用户浏览器中
- 设置、读取方便、甚至页面刷新不丢失数据
- 容量较大,sessionStorage和localStorage约5M左右
2.2 localStorage
- 声明周期永久生效,除非手动删除,否者关闭页面也会存在
- 可以多窗口(页面)共享(同一浏览器可以共享)
- 以键值对的形式存储使用
存储数据:
localStorage.setItem(key,value)
获取数据:
localStorage.getItem(key)
删除数据
localStorage.removeItem(‘uname’)
2.3 存储复杂数据类型
本地只能存储字符串,无法存储复杂数据类型,需要将复杂数据类型转换成JSON字符串,在存储到本地
JSON.stringify(复杂数据类型)
将复杂数据类型转换成JSON字符串 (存储本地存储中)
JSON.parse(JSON字符串)
将JSON字符串转换成对象 (取出时候使用)
let obj = {
uname: '刘德华',
age: 20,
sex: '男',
addrsss: '香港'
}
//(1)复杂数据类型一定要转换为JSON JSON.stringify()
localStorage.setItem('obj',JSON.stringify(obj))
// (2) 取数据用JSON.parse() 将字符串转换为对象
console.log(JSON.parse(localStorage.getItem('obj')));
3. 本地存储综合案例
// 封装一个读取数据的函数
function getLocalDate() {
let date = localStorage.getItem('date')
if(date) {
return JSON.parse(date)
} else {
let arr = [
// 1. 准备好数据后端的数据
{ 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: '北京' },
]
// 写到本地存储里
localStorage.setItem('date',JSON.stringify(arr))
}
}
// 获取父元素 tbody
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() {
// 读取本地存储数据然后渲染
let arr = getLocalDate()
// 先干掉以前的数据 让tbody 里面原来的tr 都没有
tbody.innerHTML = ''
// 在渲染新的数据
// 根据数据的条数来渲染增加 tr
for (let i = 0; i < arr.length; i++) {
// 1.创建tr
let tr = document.createElement('tr')
// 2.tr 里面放内容
tr.innerHTML = `
<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>
`
// 3.把tr追加给 tobdy 父元素.appendChild(子元素)
tbody.appendChild(tr)
}
}
// 页面加载就调用函数
render()
add.addEventListener('click', function () {
// 先读取最新存储数据
let arr = getLocalDate()
// 获得表单里面的值 之后追加给 数组 arr 用 push方法
arr.push({
// 得到数组最后一条数据的学号 1003 + 1
stuId: arr[arr.length - 1].stuId + 1,
uname: uname.value,
age: age.value,
gender: gender.value,
salary: salary.value,
city: city.value
})
// 存储到本地
localStorage.setItem('date',JSON.stringify(arr))
// 重新渲染我们的函数
render()
// 复原所有的表单数据
uname.value = age.value = salary.value = ''
gender.value = '男'
city.value = '北京'
})
// 删除操作, 删除的也是数组里面的数据 , 但是我们用事件委托
tbody.addEventListener('click', function (e) {
// 读取本都的数据
let arr = getLocalDate()
// alert(11)
// 我们只能点击了链接 a ,才会执行删除操作
// 那我们怎么知道你点击了a呢?
// 俺们只能点击了链接才能做删除操作
// console.dir(e.target.tagName)
if (e.target.tagName === 'A') {
// alert('你点击了链接')
// 删除操作 删除 数组里面的数据 arr.splice(从哪里开始删,1)
// 我要得到a的id 需要
// console.log(e.target.id)
arr.splice(e.target.id, 1)
// 保存到本地
localStorage.setItem('date',JSON.stringify(arr))
// 重新渲染我们的函数
render()
}
})
4. 自定义属性
由程序员自带的属性 比如class id title等,可以直接使用点语法操作
- getAttribute(‘属性名’) // 获取自定义属性
- setAttribute(‘属性名’,‘属性值’) // 设置自定义属性
- removeAttribute(‘属性名’) // 删除自定属性
1. data-自定义属性:
传统的自定义属性没有专门的定义规则,开发者随意定值,不够规范,所有在html5中突出来了专门的date-自定义属性在标签上一律以data-开头
在DOM对象上一律以dataset对象方式获取
十一、正则表达式
1. 正则表达式的作用
1.1 什么是正则表达式
- 正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在 JavaScript中,正则表达式也是对象
- 通常用来查找、替换那些符合正则表达式的文本,许多语言都支持正则表达式。
1.2正则表达式的使用场景:
- 例如验证表单:用户名表单只能输入英文字母、数字或者下划线, 昵称输入框中可以输入中文(匹配)
- 比如用户名: /1{3,16}$/
- 过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等
1.3 小节
正则表达式是什么?
是用于匹配字符串中字符组合的模式
正则表达式有什么作用?
- 表单验证(匹配)
- 过滤敏感词(替换)
- 字符串提取我们想要的部分(提取)
2. 语法
2.1 定义正则表达式语法
let 变量名 = /表达式/
-
其中 / / 是正则表达式的字面量
-
比如:
let reg = /前端/
2.2 判断是否有符合规则的字符串:
test() 方法 用来查看正则表达式与指定的字符串是否匹配
- 比如:
// 要检测的字符串
let str = '学好前端,成就高薪就业'
// 1. 定义规则
let reg = /前端/
// 2. 检测方法
let s = reg.test(str)
console.log(s);
- 如果正则表达式与指定的字符串匹配,返回true,否者false
2.3 检索(查找)符合规则的字符串:
exec() 方法 在一个指定字符串中执行一个搜索匹配
- 比如:
// 要检测的字符串
let str = '学好前端,成就高薪就业'
// 1. 定义规则
let reg = /前端/
// 2. 检测方法
let st = reg.exec(str)
console.log(st);
- 如果匹配成功,exec()方法返回一个数组,否者返回null
3. 元字符
- 普通字符:
大多数的字符仅能够描述他们本身,这些字符称作为普通字符,例如所有的字母和数字。
也就是说普通字符只能匹配字符串中与他们相同的字符
- 元字符(特殊字符)
是一些具有特殊含义的字符,可以极大提高了灵活性和强大的匹配功能
- 比如,规定用户只能输入英文26个英文字母,普通字符的话abcdefghij
- 但是换成元字符写法:[a-z]
- 参考文档:
- MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions
- 正则测试工具: http://tool.oschina.net/regex
3.1 元字符分类:
- 边界符(表示位置,开头和结尾,必须用什么开头,用什么结尾)
- 量词(表示重复次)
- 字符类(比如\d 表示0~9)
- 边界符
正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符
边界符 | 说明 |
---|---|
^ | 表示匹配行首的文本(以谁开始) |
$ | 表示匹配行尾的文本(以谁结束) |
如果^和$在一起,表示必须是精确匹配
console.log(/哈/.test('哈')); //trur
console.log(/二哈/.test('二哈')); // true
console.log(/二哈/.test('很二哈哈')); // true
console.log('-----------------------------------');
// ^ 开头
console.log(/^二哈/.test('很二哈哈')); // false
console.log(/^二哈/.test('二哈哈哈')); // true
console.log('-----------------------------------');
// $结尾
console.log(/^二哈$/.test('二哈很傻')); // false
console.log(/^二哈$/.test('二哈二哈')); // false
console.log(/^二哈$/.test('二哈')); // true
- 量词
量词用来设定某个模式出现的次数
量词 | 说明 |
---|---|
* | 重复零次或更多次 |
+ | 重复一次或更多次 |
? | 重复零次或一次 |
{n} | 重复n次 |
{n,} | 重复n次或更多次 |
{n,m} | 重复n到m次 |
注意: 逗号左右两侧千万不要出现空格
// 量词 * 类似 >= 0次
console.log(/^哈$/.test('哈')); // true
console.log(/^哈*$/.test('')); // true
console.log(/^哈*$/.test('哈哈')); // true
console.log(/^哈*$/.test('二哈很傻')); // false
console.log(/^哈*$/.test('哈很傻')); // false
console.log(/^哈*$/.test('哈很哈')); // false
console.log('-----------------------------------');
// 量词 + 类似 >= 1次
console.log(/^哈$/.test('哈')); // true
console.log(/^哈+$/.test('')); // false
console.log(/^哈+$/.test('哈哈')); // true
console.log(/^哈+$/.test('二哈很傻')); // false
console.log(/^哈+$/.test('哈很傻')); // false
console.log(/^哈+$/.test('哈很哈')); // false
console.log('-----------------------------------');
// 量词 ? 类似 0 || 1
console.log(/^哈?$/.test('')); // true
console.log(/^哈?$/.test('哈')); // true
console.log(/^哈?$/.test('哈哈')); // false
console.log(/^哈?$/.test('二哈很傻')); // false
console.log(/^哈?$/.test('哈很傻')); // false
console.log(/^哈?$/.test('哈很哈')); // false
// 量词{n} 写几, 就必须出现几次
console.log(/^哈{4}$/.test('哈')); // false
console.log(/^哈{4}$/.test('哈哈')); // false
console.log(/^哈{4}$/.test('哈哈哈')); // false
console.log(/^哈{4}$/.test('哈哈哈哈')); // true
console.log(/^哈{4}$/.test('哈哈哈哈哈')); // false
console.log('-----------------------------------');
// 量词{n,} >= n
console.log(/^哈{4,}$/.test('哈')); // false
console.log(/^哈{4,}$/.test('哈哈')); // false
console.log(/^哈{4,}$/.test('哈哈哈')); // false
console.log(/^哈{4,}$/.test('哈哈哈哈')); // true
console.log(/^哈{4,}$/.test('哈哈哈哈哈')); // true
console.log('-----------------------------------');
// 量词{n,m} 逗号左右两侧千万不能有空格 >=n && <= margin
console.log(/^哈{4,6}$/.test('哈')); // false
console.log(/^哈{4,6}$/.test('哈哈')); // false
console.log(/^哈{4,6}$/.test('哈哈哈')); // false
console.log(/^哈{4,6}$/.test('哈哈哈哈')); // true
console.log(/^哈{4,6}$/.test('哈哈哈哈哈')); // true
console.log(/^哈{4,6}$/.test('哈哈哈哈哈哈')); // true
console.log(/^哈{4,6}$/.test('哈哈哈哈哈哈哈')); // false
- 字符类
(1)[ ]里面加上 - 连字符
- 使用连字符 - 表示一个范围
console.log(/^[a-z]$/.test('c')) // true
- 比如
- [a-z] 表示a到z 26个英文字母都可以
- [a-zA-Z] 表示大小写都可以
- [0-9] 表示0~9的数字都可以
- 案例
腾讯QQ号:^[1-9][0-9]{4,}$(腾讯QQ号从10000开始)
(2)[ ]里面加上^取反符号
- 比如
- [^a-z] 匹配除了小写字母以外的字符
- 注意要写到中括号里面
(3). 匹配除了换行符之外的任意单个字符
- 预定义:指的是某些常见模式的简写方式
预定义类 | 说明 |
---|---|
\d | 匹配0~9之间的任一数字,相当于[0-9] |
\D | 匹配所有0-9以外的字符,相当于[^0-9] |
\w | 匹配任意的字母、数字和下划线,相当于[A-Za-z0-9] |
\w | 除所有字母、数字和下划线以外的字符,相当于[^A-Za-z0-9] |
\s | 匹配空格(包括换行符、制表符、空格符等),相等于[\t\r\n\v\f] |
\S | 匹配非空格的字符,相当于[^\t\r\n\v\f] |
日期格式:^\d{4}-\d{1,2}-\d{1,2}
4. 修饰符
修饰符约束正则执行的某些细节行为,如是否区分大小写、是否支持多行匹配等
- 语法:
/表达式/修饰符
- i是单词ignore的缩写,正则匹配时字母不区分大小写
- g是单词global的缩写,匹配所有满足真正表达式的结果
- 替换 replace替换
字符串.replace(/正则表达式/,'替换的文本')
console.log(/^java$/.test('java')); // true
console.log(/^java$/i.test('JAVA')); // true
console.log(/^java$/i.test('java')); // true
let str = 'java是一门编程语言,学完JAVA工资很高'
let re = str.replace(/java/ig,'前端')
console.log(re);
5. change事件
内容发生了变化
<input type="text">
<script>
let input = document.querySelector('input')
input.addEventListener('change',function(){
alert('111')
})
</script>
6. 综合案例
<script>
// 1.发送短息验证
let code = document.querySelector('.code')
let falg = true // 节流阀
// 1.1 绑定点击事件
code.addEventListener('click',function(){
if (falg) {
let i = 5
falg = false
// 点击完毕后立即触发
code.innerHTML = `0${i}之后重新获取`
let timer = setInterval(function(){
i--
code.innerHTML = `0${i}之后重新获取`
if(i === 0){
clearInterval(timer)
code.innerHTML = `重新获取`
falg = true
}
},1000)
}
})
// 2.验证用户名
// 2.1 获取用户名表单
let username = document.querySelector('[name=username]')
// 2.2 绑定change事件
username.addEventListener('change',verifyName)
// 2.3 封装verifyName函数
function verifyName (){
let span = username.nextElementSibling
// 2.4 定义规则
let reg = /^[a-zA-Z0-9-_]{6,10}$/
if(!reg.test(username.value)) {
span.innerHTML = '输入不合法,请输入6~10位'
return false
}
// 2.5 合法的
span.innerHTML = ''
return true
}
// 3. 验证手机号
// 3.1 获取手机号表单
let phone = document.querySelector('[name=phone]')
// 3.2 绑定change事件
phone.addEventListener('change',verifyPhone)
// 3.3 封装verifyPhone函数
function verifyPhone () {
let span = phone.nextElementSibling
// 3.4 定义规则
let reg = /^1(3\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\d|9[0-35-9])\d{8}$/
if(!reg.test(phone.value)) {
span.innerHTML = '请输入11位电话号码'
return false
}
// 合法的电话号码
span.innerHTML = ''
return true
}
// 4. 验证码表单
// 4.1 获取验证码表单
let codeInput = document.querySelector('[name=code]')
// 4.2 绑定change事件
codeInput.addEventListener('change',verifyCode)
// 4.3 封装verifyCode函数
function verifyCode () {
let span = codeInput.nextElementSibling
// 4.4 定义规则
let reg = /^\d{6}$/
if(!reg.test(codeInput.value)) {
span.innerHTML = '请输入6位数字'
return false
}
// 4.5合法的验证码
span.innerHTML = ''
return true
}
// 5.密码表单
// 5.1 获取密码表单
let password = document.querySelector('[name=password]')
// 5.2 绑定change事件
password.addEventListener('change',verifyPassword)
// 5.3 封装verifyPassword函数
function verifyPassword () {
let span = password.nextElementSibling
// 5.4 定义规则
let reg = /^[a-zA-Z0-9-_]{6,20}$/
if(!reg.test(password.value)) {
span.innerHTML = '请输入6~20位由字母数字下划线的密码'
return false
}
// 5.5 合法的密码
span.innerHTML = ''
return true
}
// 6. 密码再次验证
// 6.1 获取确认密码表单
let confirm = document.querySelector('[name=confirm]')
// 6.2 绑定change事件
confirm.addEventListener('change',verifyPwd)
// 6.3 封装verifyPwd函数
function verifyPwd () {
let span = confirm.nextElementSibling
// 判断和密码框的值是否相等
if(password.value !== confirm.value) {
span.innerHTML = '您两次输入的密码不正确'
return false
}
// 6.4 合法的
span.innerHTML = ''
return true
}
// 7. 我同意
let queren = document.querySelector('.icon-queren')
// 绑定点击事件
queren.addEventListener('click',function(){
queren.classList.toggle('icon-queren2')
})
// 8. 提交模块
let form = document.querySelector('form')
// 8.1 绑定提交事件
form.addEventListener('submit',function(e) {
// 8.2 判断是否勾选我同意模块
if(!queren.classList.contains('icon-queren2')) {
alert('请勾选同意协议')
// 阻止提交
e.preventDefault()
}
// 8.3 依次判断上方的表单是否通过,如果有一个没有通过就阻止
if(!verifyName()) e.preventDefault()
if(!verifyPhone()) e.preventDefault()
if(!verifyCode()) e.preventDefault()
if(!verifyPassword()) e.preventDefault()
if(!verifyPwd()) e.preventDefault()
})
</script>
~~~js
!reg.test(phone.value)) {
span.innerHTML = '请输入11位电话号码'
return false
}
// 合法的电话号码
span.innerHTML = ''
return true
}
// 4. 验证码表单
// 4.1 获取验证码表单
let codeInput = document.querySelector('[name=code]')
// 4.2 绑定change事件
codeInput.addEventListener('change',verifyCode)
// 4.3 封装verifyCode函数
function verifyCode () {
let span = codeInput.nextElementSibling
// 4.4 定义规则
let reg = /^\d{6}$/
if(!reg.test(codeInput.value)) {
span.innerHTML = '请输入6位数字'
return false
}
// 4.5合法的验证码
span.innerHTML = ''
return true
}
// 5.密码表单
// 5.1 获取密码表单
let password = document.querySelector('[name=password]')
// 5.2 绑定change事件
password.addEventListener('change',verifyPassword)
// 5.3 封装verifyPassword函数
function verifyPassword () {
let span = password.nextElementSibling
// 5.4 定义规则
let reg = /^[a-zA-Z0-9-_]{6,20}$/
if(!reg.test(password.value)) {
span.innerHTML = '请输入6~20位由字母数字下划线的密码'
return false
}
// 5.5 合法的密码
span.innerHTML = ''
return true
}
// 6. 密码再次验证
// 6.1 获取确认密码表单
let confirm = document.querySelector('[name=confirm]')
// 6.2 绑定change事件
confirm.addEventListener('change',verifyPwd)
// 6.3 封装verifyPwd函数
function verifyPwd () {
let span = confirm.nextElementSibling
// 判断和密码框的值是否相等
if(password.value !== confirm.value) {
span.innerHTML = '您两次输入的密码不正确'
return false
}
// 6.4 合法的
span.innerHTML = ''
return true
}
// 7. 我同意
let queren = document.querySelector('.icon-queren')
// 绑定点击事件
queren.addEventListener('click',function(){
queren.classList.toggle('icon-queren2')
})
// 8. 提交模块
let form = document.querySelector('form')
// 8.1 绑定提交事件
form.addEventListener('submit',function(e) {
// 8.2 判断是否勾选我同意模块
if(!queren.classList.contains('icon-queren2')) {
alert('请勾选同意协议')
// 阻止提交
e.preventDefault()
}
// 8.3 依次判断上方的表单是否通过,如果有一个没有通过就阻止
if(!verifyName()) e.preventDefault()
if(!verifyPhone()) e.preventDefault()
if(!verifyCode()) e.preventDefault()
if(!verifyPassword()) e.preventDefault()
if(!verifyPwd()) e.preventDefault()
})
</script>
a-z0-9_- ↩︎