1、操作DOM
1.1、什么DOM?
DOM(Document Object Model——文档对象模型):DOM是浏览器提供的一套专门用来操作网页内容的功能
DOM作用:开发网页内容特效和实现用户交互
DOM树是什么?
- 将 HTML 文档以树状结构直观的表现出来,称之为文档树或 DOM 树
- DOM树直观的体现了标签与标签之间的关系
DOM对象:浏览器根据HTML标签生成的 JS对象
- 所有的标签属性都可以在这个对象上面找到
- 修改这个对象的属性会自动映射到标签身上
DOM的核心思想:**把网页内容当做对象来处理!**它提供的属性和方法都是用来访问和操作网页内容的
1.2、获取DOM对象
前提条件:要操作一个Dom节点, 就必须要先获得这个Dom节点,通过JS选择页面中的HTML元素
打印对象:console.dir('对象名')
querySelector
选择匹配的第一个元素
返回值:CSS选择器匹配的第一个元素,一个 HTML Element对象。 如果没有匹配到,则返回null
具体语法:
<body>
<div>这是第一个盒子</div>
<div id="box">这是第二个盒子</div>
<ul>
<li>Java</li>
<li>JavaScript</li>
<li>Python</li>
</ul>
<script>
// 获取匹配的第一个元素
const div = document.querySelector('div')
console.log(div);
const box = document.querySelector('#box')
console.log(box);
const li = document.querySelector('ul li') // 只能获取第一个元素
console.dir(li) // Java
</script>
</body>
querySelectorAll
选择匹配的多个元素
返回值:CSS选择器匹配的NodeList 对象集合
具体语法:
<body>
<ul class="course">
<li>Java</li>
<li>JavaScript</li>
<li>Python</li>
</ul>
<script>
// 获取匹配的多个元素
const lis = document.querySelectorAll('.course li')
console.log(lis)
</script>
</body>
注:通过querySelector系列获得的元素得到的是一个伪数组
- 有长度有索引号的数组
- 但是没有 pop() push() 等数组方法
- 要想得到里面的每一个对象,则需要遍历(for)的方式获得
getElement系列
getElement系列方法获取DOM元素
<body>
<h1>我是h1标题</h1>
<div id="box">我是div盒子</div>
<ul>
<li class="item">1</li>
<li class="item">2</li>
<li class="item">3</li>
<li class="item">4</li>
</ul>
<input type="checkbox" name="hobbies"> code
<input type="checkbox" name="hobbies"> girl
<script>
let box = document.getElementById('box') // id
let items = document.getElementsByClassName('item') // 类
let h1 = document.getElementsByTagName('h1') // 标签名称
let hobbies = document.getElementsByName('hobbies') // 相同 name 属性
console.log(h1)
console.log(box)
console.log(items)
console.log(hobbies)
</script>
</body>
操作元素内容
- 对象.innerText 属性
- 对象.innerHTML 属性
DOM对象都是根据标签生成的,所以操作标签,本质上就是操作DOM对象!
<body>
<div class="box">我是一个div盒子</div>
<script>
// 获取元素
const box = document.querySelector('.box')
// 修改文本内容
console.log(box.innerText) // 获取文本内容
box.innerText = '<em>我是div标签 我可以布局网页</em>' // 不解析标签
box.innerHTML = '<em>我是div标签 我可以布局网页</em>' // 会解析标签
</script>
</body>
两者区别:
相同点:
- 都可以修改标签文本内容
不同点:
- innerText属性,只会显示纯文本,不解析标签
- 而innerHTML属性,既可以显示纯文本,也会解析标签,多标签建议使用模板字符
1.3、操作元素属性
元素常用属性
还可以通过 JS 设置/修改标签元素属性,常见的属性有: href、title、src 等
<body>
<img src="./images/1.jpg" title="漂亮可爱的小姐姐图片">
<button onclick="changeImg()">点我更换图片</button>
<script>
function changeImg() {
let arr = ['1.jpg', '2.jpg', '3.jpg', '4.jpg', '5.jpg', '6.jpg', '7.jpg', '8.jpg']
// 生成随机数
let ran = Math.floor(Math.random() * arr.length)
// 获取图像元素
const img = document.querySelector('img')
// console.log(img)
img.src = './images/' + arr[ran]
img.title = '漂亮可爱的小姐姐图片'
}
</script>
</body>
元素样式属性
通过 style 属性操作CSS
<body>
<div class="box"></div>
<script>
// 获取元素
const box = document.querySelector('.box')
// console.log(box)
// 修改样式属性 对象名.style.样式属性 = '值'
box.style.width = '350px'
box.style.height = '350px'
box.style.backgroundColor = 'orange'
</script>
</body>
注:
- CSS属性名写法为驼峰命名法
- 赋值的时候,需要的时候不要忘记加CSS单位
- 通常用于修改样式较少时使用,而且添加的是行内样式(权重较高)
通过类名(className) 操作CSS
如果修改的样式比较多,直接通过style属性修改比较繁琐,代码比较长,可以通过借助于CSS类名的形式
<style>
.circle {
width: 300px;
height: 300px;
background-color: sandybrown;
border-radius: 50%;
cursor: pointer;
}
.box {
color: white;
text-align: center;
line-height: 300px;
}
</style>
<body>
<div class="box">这是一个圆圈</div>
<script>
// 获取元素
const div = document.querySelector('div')
// 添加类名
div.className = 'box circle'
</script>
</body>
注:
- 由于class是关键字,所以使用className去代替
- className是使用新值换旧值(覆盖),如果需要添加一个类,需要保留之前的类
通过 classList 操作类控制CSS
为了解决className 容易覆盖以前的类名,可以通过classList方式追加和删除类名
<style>
.wrap {
display: flex;
justify-content: space-around;
width: 200px;
margin: 60px auto;
}
.item {
list-style: none;
width: 20px;
height: 20px;
background-color: gray;
text-align: center;
line-height: 20px;
border-radius: 50%;
color: white;
}
.active {
background-color: coral;
cursor: pointer;
}
</style>
<body>
<ul class="wrap">
<li class="item">1</li>
<li class="item">2</li>
<li class="item">3</li>
<li class="item">4</li>
<li class="item">5</li>
</ul>
<script>
// 获取元素
const item = document.querySelector('.item')
// 修改样式 追加一个类 add()
item.classList.add('active')
// 修改样式 移除一个类 remove()
item.classList.remove('active')
// 切换样式 切换一个类 toggle() 没有添加,有则移除
item.classList.toggle('active')
</script>
</body>
className与classList两者区别:
- className 会覆盖之前类名,如果需要以前的类名,在编写的时候还要手动加上!
- classList 是追加和删除,不影响之前类名,修改样式也比较方便
表单元素属性
- 获取:
DOM对象.属性名
- 设置:
DOM对象.属性名 = 新值
<input type="text" name="g-name" id="g-name" value="笔记本电脑">
<script>
// 获取元素
const gName = document.querySelector('#g-name')
// 获取值
console.log(gName.value) // 笔记本电脑
// 设置文本框的值
gName.value = '平板'
// 修改文本框 type 属性
gName.type = 'date'
</script>
表单属性中添加就有效果,移除就没有效果,一律使用布尔值表示,如果为true 代表添加了该属性,如果是false代表移除了该属性
例如: disabled、checked、selected
<input type="checkbox" name="hobbies"> code
<input type="checkbox" name="hobbies"> girl
<input type="checkbox" name="hobbies"> swim
<script>
// 获取元素
const ipts = document.querySelectorAll('input')
for (let i = 0; i < ipts.length; i++) {
console.log(ipts[i].checked) // 默认没有勾选,返回false
// 让三个多选框全部选中
ipts[i].checked = true
}
// 让第一个复选框为禁用状态
ipts[0].disabled = true
</script>
自定义属性
- 标准属性:也就是标签天生自带的属性,比如class id title等,可以直接使用点语法操作,比如:disabled、checked、selected
- 自定义属性:
- 在html5中推出来了专门的data-自定义属性
- 在标签上一律以data-开头
- 在DOM对象上一律以dataset对象方式获取
<ul>
<li data-gid="1001" data-hot="热卖商品">1</li>
<li data-gid="1002">2</li>
<li data-gid="1003">3</li>
</ul>
<script>
const li = document.querySelector('ul>li:first-child')
console.log(li.dataset)
console.log(li.dataset.gid) // 1001
console.log(li.dataset['hot']) // 热卖商品
</script>
1.4、定时器
网页中经常会需要一种功能:每隔一段时间需要自动执行一段代码,不需要我们手动去触发,例如:网页中的倒计时
定时器-间歇函数
开启定时器
具体语法:setInterval(函数,间隔时间)
<script>
function love() {
document.write(`<p>我爱你到天长地久</p>`)
}
let id = setInterval(love, 2000)
console.log(id) // 1
</script>
作用:
- 每隔一段时间都会调用这个函数
- 间隔时间单位是毫秒
注:
-
定时器调用函数名字的时候不需要加括号
-
定时器返回的是一个id数字
关闭定时器
一般不会刚创建就停止,而是满足一定条件才停止
具体语法:clearInterval(变量名)
<script>
function love() {
document.write(`<p>我爱你到天长地久</p>`)
}
let id = setInterval(love, 2000)
console.log(id) // 1
// 关闭定时器
clearInterval(id)
</script>
2、事件
2.1、何为事件?
事件是指用户在访问页面时,对页面内容做出的动作或者产生的行为
什么是事件监听?
就是让程序检测是否有事件产生,一旦有事件触发,就立即调用一个函数做出响应,也称为绑定事件或者注册事件
比如鼠标经过显示下拉菜单,比如点击可以播放轮播图等等
2.2、事件监听
具体语法:元素对象.addEventListener('事件类型',要执行的函数)
事件监听三要素:
- 事件源: 那个dom元素被触发了,要获取dom元素
- 事件类型: 用什么方式触发,比如鼠标单击 click、鼠标经过 mouseover 等
- 事件调用的函数: 要做什么事
<body>
<div class="box">
<img src="./images/jd.webp">
<div class="close">x</div>
</div>
<script>
// 点击 按钮 x 号,关闭广告
// 事件源:x 号
// 事件类型:点击
// 处理程序:关闭广告
let btn = document.querySelector('.close')
let box = document.querySelector('.box')
btn.addEventListener('click', function () {
box.style.display = 'none'
})
</script>
</body>
注:
- 事件类型必须要加引号,否则无效果
- 函数是点击之后再去执行,每次点击都会执行一次
事件监听版本
DOM L0:事件源.on事件 = function() { }
DOM L2:事件源.addEventListener(事件,事件处理函数)
<button>点我</button>
<script>
const btn = document.querySelector('button')
btn.onclick = function () {
alert(1)
}
// 会覆盖之前的点击事件,只要下面点击事件才会触发
btn.onclick = function () {
alert(2)
}
// 不会覆盖之前的点击事件,点击事件都会触发
btn.addEventListener('click', function () {
console.log(1)
})
btn.addEventListener('click', function () {
console.log(2)
})
</script>
两者区别:
- on方式会被覆盖,addEventListener方式可绑定多次,拥有事件更多特性,推荐使用
2.3、事件类型
- 鼠标事件
- click 鼠标点击
- mouseenter 鼠标经过
- mouseleave 鼠标离开
- 焦点事件
- focus 获得焦点
- blur 失去焦点
- 键盘事件
- keydown 键盘按下触发
- keyup 键盘抬起触发
- 文本事件
- input 用户输入事件
<div></div>
<script>
// 鼠标经过事件
div.addEventListener('mouseenter', function () {
console.log('轻轻地 我来了')
})
// 鼠标离开事件
div.addEventListener('mouseleave', function () {
console.log('正如轻轻的 我走了')
})
</script>
<div class="mi">
<input type="search" placeholder="小米笔记本">
<ul class="goods-list">
<li><a href="#">全部商品</a></li>
<li><a href="#">空调</a></li>
<li><a href="#">小米10S</a></li>
<li><a href="#">小米笔记本</a></li>
<li><a href="#">小米手机</a></li>
<li><a href="#">冰箱</a></li>
<li><a href="#">空调</a></li>
</ul>
</div>
<script>
// 获取元素
const input = document.querySelector('[type=search]')
const list = document.querySelector('.goods-list')
// 文本框获得焦点 显示下拉菜单
input.addEventListener('focus', function () {
list.style.display = 'block'
input.classList.add('active')
})
// 文本失去焦点 隐藏下拉菜单
input.addEventListener('blur', function () {
list.style.display = 'none'
input.classList.remove('active')
})
</script>
<input type="text" class="comment">
<script>
const comment = document.querySelector('.comment')
comment.addEventListener('keydown', function () {
console.log('键盘按下了...');
})
comment.addEventListener('keyup', function () {
console.log('键盘弹起了...');
})
</script>
<input type="text" class="uname" placeholder="请输入你的用户名">
<script>
const uname = document.querySelector('.uname')
uname.addEventListener('input', function () {
console.log(`用户输入了:${uname.value}`);
})
</script>
2.4、事件对象
事件对象是什么?
- 它是个对象,这个对象里有事件触发时的相关信息
应用场景:可以判断用户按下哪个键,比如按下回车键可以发布评论
如何获取事件对象?
- 在事件绑定的回调函数的第一个参数就是事件对象
- 一般命名为event、ev、e
具体语法:元素.addEventListener('事件类型',function(e){})
常用属性
- type
- 获取当前的事件类型
- clientX/clientY
- 获取光标相对于浏览器可见窗口左上角的位置
- offsetX/offsetY
- 获取光标相对于当前DOM元素左上角的位置
- key
- 用户按下的键盘键的值
- 现在不提倡使用keyCode
<input type="text">
<script>
const input = document.querySelector('input')
input.addEventListener('keyup', function (e) {
// console.log(e)
if (e.key === 'Enter') {
console.log('回车键被按下了');
}
})
</script>
环境对象
环境对象:指的是函数内部特殊的变量 this ,它代表着当前函数运行时所处的环境
作用:弄清楚this的指向,可以使代码更简洁
- 函数的调用方式不同,this 指代的对象也不同
- 【谁调用, this 就是谁】 是判断 this 指向的粗略规则
- 直接调用函数,其实相当于是 window.函数,所以 this 指代 window
<button>点我</button>
<script>
// 每个函数里面都有this对象 环境对象 普通函数里面this指向的是window
// function fn() {
// console.log(this);
// }
// window.fn()
// fn()
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
// console.log(this) // btn
this.style.color = 'orange'
})
</script>
回调函数
当一个函数作为参数来传递给另外一个函数的时候,这个函数就是回调函数
- 回调函数本质还是函数,只不过把它当成参数使用
- 使用匿名函数做为回调函数比较常见
<script>
function fn() {
console.log('回调函数...')
}
setInterval(fn, 1000)
</script>
2.5、事件流
事件流指的是事件完整的流动过程
假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
经验:捕获阶段是 从父到子, 冒泡阶段是从子到父
事件冒泡
当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡
<div class="father">
father
<div class="son">son</div>
</div>
<script>
const father = document.querySelector('.father')
const son = document.querySelector('.son')
father.addEventListener('click', function () {
alert('父元素被点击了')
})
son.addEventListener('click', function () {
alert('子元素被点击了')
})
</script>
- 当一个元素触发事件后,会依次向上调用所有父级元素的同类型事件
- 事件冒泡是默认存在的
阻止冒泡
因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素
- 若想把事件就限制在当前元素内,就需要阻止事件冒泡
前提:阻止事件冒泡需要拿到事件对象
具体语法:事件对象.stopPropagation()
<div class="father">
father
<div class="son">son</div>
</div>
<script>
son.addEventListener('click', function (e) {
alert('子元素被点击了')
// 阻止事件流动传播
e.stopPropagation()
})
</script>
注:此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效
在某些情况下需要阻止默认行为的发生,比如 阻止链接的跳转,表单域跳转
<form action="https://hua.com">
<input type="text" name="请输入你想要搜索的礼品">
<input type="submit">
</form>
<p>
<a href="https://www.baidu.com">百度一下 你就知道</a>
</p>
<script>
const form = document.querySelector('form')
form.addEventListener('submit', function (e) {
// 阻止默认行为
e.preventDefault()
})
const a = document.querySelector('a')
a.addEventListener('click', function (e) {
e.preventDefault()
})
</script>
事件解绑
on事件方式,直接使用null覆盖就可以实现事件的解绑
<button>点我</button>
<script>
const btn = document.querySelector('button')
btn.onclick = function () {
alert('onclick')
}
// L0 事件移除解绑
btn.onclick = null
</script>
addEventListener方式,必须使用:removeEventListener(事件类型, 事件处理函数, [获取捕获或者冒泡阶段])
<button>点我</button>
<script>
const btn = document.querySelector('button')
function sayHello() {
alert('addEventListener')
}
// 绑定事件
btn.addEventListener('click', sayHello)
// 解绑事件
btn.removeEventListener('click', sayHello)
</script>
注:匿名函数无法被解绑!
鼠标经过事件的区别
- mouseover 和 mouseout 会有冒泡效果
- mouseenter 和 mouseleave 没有冒泡效果 (推荐)
两种注册事件的区别:
- 传统on注册(L0)
- 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
- 直接使用null覆盖就可以实现事件的解绑
- 都是冒泡阶段执行的
- 事件监听注册(L2)
- 后面注册的事件不会覆盖前面注册的事件(同一个事件)
- 必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)
- 可以通过第三个参数去控制是在冒泡或者捕获阶段执行
- 匿名函数无法被解绑
2.6、事件委托
事件委托是利用事件流的特征解决一些开发需求的知识技巧
优点:减少注册次数,提高程序性能
原理:事件委托其实是利用事件冒泡的特点
- 给父元素注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件
事件对象.target
可以获得真正触发事件的元素
<ul>
<li>第1个child</li>
<li>第2个child</li>
<li>第3个child</li>
<li>第4个child</li>
<li>第5个child</li>
<li>第6个child</li>
<p>I not change color</p>
</ul>
<script>
// 点击每个小 li,当前 li 背景色变为橙色
// const lis = document.querySelectorAll('ul li')
// for (let i = 0; i < lis.length; i++) {
// lis[i].addEventListener('click', function () {
// lis[i].style.backgroundColor = 'orange'
// })
// }
// 利用事件委托方式 改写
// 获取父元素 将事件委托给父级
const ul = document.querySelector('ul')
ul.addEventListener('click', function (e) {
// console.dir(e.target) // 点击的对象
if (e.target.tagName === 'LI') {
e.target.style.backgroundColor = 'orange'
}
})
</script>
2.7、其他事件
页面加载事件
加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件
事件名:load
监听页面所有资源加载完毕:给 window 添加 load 事件
具体语法:window.addEventListener('load', function () {}
<script>
// 等待所有页面所有资源加载完毕 就会去执行回调函数
window.addEventListener('load', function () {
// 执行的操作
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
alert(1)
})
})
</script>
注:不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件
当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像等完全加载
事件名:DOMContentLoad
监听页面DOM加载完毕:给 document 添加 DOMContentLoaded 事件
具体语法:document.addEventListener('DOMContentLoaded', function () {}
<script>
// HTML标签加载完成 就会去执行回调函数
document.addEventListener('DOMContentLoaded', function () {
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
alert(2)
})
})
</script>
元素滚动事件
滚动条在滚动的时候持续触发的事件
应用场景:很多网页需要检测用户把页面滚动到某个区域后做一些处理, 比如固定导航栏,比如返回顶部
事件名:scroll,用于监听整个页面滚动
给 window 或 document 添加 scroll 事件
具体语法:window.addEventListener('scroll', function () {}
<script>
// 页面滚动事件
window.addEventListener('scroll', function () {
// 执行的操作
console.log('滚动条滚动了')
})
</script>
注:监听某个元素的内部滚动直接给某个元素加即可
页面滚动事件-获取位置
scrollLeft和scrollTop (属性),两个值是可读写的
- 获取被卷去的大小,也就是元素内容往左、往上滚出去看不到的距离
开发中,经常检测页面滚动的距离,比如页面滚动200像素,就可以显示一个元素,或者固定一个元素
HTML 文档返回对象为HTML元素:document.documentElement
<div>我是div</div>
<script>
const div = document.querySelector('div')
// 页面滚动事件
window.addEventListener('scroll', function () {
console.log(document.documentElement.scrollTop)
const scrollHeight = document.documentElement.scrollTop
if (scrollHeight >= 200) {
div.style.display = 'block'
} else {
div.style.display = 'none'
}
})
</script>
注:尽量在scroll事件里面获取被卷去的距离
scrollTo() 方法可把内容滚动到指定的坐标
具体语法:元素.scrollTo(x, y)
<script>
const backTop = document.querySelector('#backTop')
backTop.addEventListener('click', function () {
// document.documentElement.scrollTop = 0
// 让页面滚动到 y 轴 200像素的位置
window.scrollTo(0, 200)
})
</script>
页面尺寸事件
会在窗口尺寸改变的时候触发事件:resize
<script>
// 浏览器窗口大小发生变化时候触发的事件
window.addEventListener('resize', function () {
console.log(1);
})
</script>
获取元素宽高
- clientWidth和clientHeight
- 获取元素的宽高(不包含border,margin,滚动条,用于js获取元素大小,只读属性)
<script>
const div = document.querySelector('div')
console.log(div.clientWidth)
</script>
元素尺寸大小与位置
获取宽高:
获取元素的宽高包含border、padding,滚动条等,只读
- offsetWidth和offsetHeight
- 获取出来的是数值,方便计算
注:获取的是可视宽高, 如果盒子是隐藏的,获取的结果是0
获取位置:
- offsetLeft和offsetTop,是只读属性
- 获取元素距离自己定位父级元素的左、上距离
<style>
.box {
position: relative;
width: 200px;
height: 200px;
background-color: salmon;
margin: 100px;
}
.item {
width: 100px;
height: 100px;
margin: 50px;
background-color: sandybrown;
}
</style>
<body>
<div class="box">
<div class="item"></div>
</div>
<script>
const box = document.querySelector('.box')
const item = document.querySelector('.item')
console.log(box.offsetLeft) // 108
console.log(item.offsetLeft) // 58
</script>
</body>
3、DOM节点操作
3.1、日期对象
日期对象:用来表示时间的对象
作用:可以得到当前系统时间
用new 关键字创建对象时,一般将这个操作称为实例化
<script>
// 获取当前系统时间
const date = new Date()
console.log(date)
// 获取指定时间
const assignDate = new Date('2025 06-06 12:30:00')
console.log(assignDate)
</script>
日期对象方法
方法 | 作用 | 说明 |
---|---|---|
getFullYear() | 获得年份 | 获取四位年份 |
getMonth() | 获得月份 | 取值为 0 ~ 11 |
getDate() | 获取月份中的每一天 | 不同月份取值也不相同 |
getDay() | 获取星期 | 取值为 0 ~ 6 |
getHours() | 获取小时 | 取值为 0 ~ 23 |
getMinutes() | 获取分钟 | 取值为 0 ~ 59 |
getSeconds() | 获取秒 | 取值为 0 ~ 59 |
<p id="currentTime"></p>
<script>
// 动态显示当前时间
const currentTime = document.querySelector('#currentTime')
function getCurrentTime() {
let nowTime = new Date()
let year = nowTime.getFullYear(); // 年
let month = nowTime.getMonth() + 1; // 月
let date = nowTime.getDate(); // 日
let hour = nowTime.getHours(); // 时
let minutes = nowTime.getMinutes(); // 分
let seconds = nowTime.getSeconds(); // 秒
month = fillZero(month)
date = fillZero(date)
hour = fillZero(hour)
minutes = fillZero(minutes)
seconds = fillZero(seconds)
// console.log(year, month, date, hour, minutes, seconds)
nowTime = `${year}年${month}月${date}日 ${hour}时${minutes}分${seconds}秒`
currentTime.innerHTML = nowTime
}
// 补零操作函数
function fillZero(value) {
return value < 10 ? '0' + value : value
}
// 先手动调用一次,衔接定时器空窗期
getCurrentTime()
let timerId = setInterval(getCurrentTime, 1000)
</script>
<script>
const time = document.querySelector('.time')
const date = new Date()
time.innerHTML = date.toLocaleDateString() // 2022/3/15
time.innerHTML = date.toLocaleTimeString() // 13:45:55
time.innerHTML = date.toLocaleString() // 2022/3/15 13:46:05
</script>
时间戳
什么是时间戳?
- 是指1970年01月01日00时00分00秒起至现在的毫秒数,它是一种特殊的计量时间的方式
计算方式:
- 将来的时间戳 - 现在的时间戳 = 剩余时间毫秒数
- 剩余时间毫秒数 转换为 剩余时间的 年月日时分秒 就是 倒计时时间
三种方式获取时间戳
方式一:使用 getTime() 方法
方式二:简写 +new Date()
方式三:使用 Date.now()
- 无需实例化
- 但只能得到当前的时间戳, 而前面两种可以返回指定时间的时间戳
<div class="countdown">
<p class="next">今天是2222年2月22日</p>
<p class="title">距离实现远景目标还剩</p>
<div class="clock">
<p id="date">3000</p>
<span>天</span>
<p id="hour">00</p>
<span>:</span>
<p id="minutes">25</p>
<span>:</span>
<p id="scond">20</p>
</div>
<p class="tips">2035年01月01日 00:00:00</p>
</div>
<script>
// 获取元素
const next = document.querySelector('.next')
const time = new Date()
next.innerHTML = `今天是${time.getFullYear()}年${time.getMonth() + 1}月${time.getDate()}日`
// 先手动调用一次,补充定时器空窗期
getTwoTimestamps()
let timerId = setInterval(getTwoTimestamps, 1000)
// 获取当前时间与指定时间之间时间戳的毫秒数
function getTwoTimestamps() {
// 获得当前时间戳
const now = +new Date()
// 获取将来时间戳
const future = + new Date('2035-1-1 00:00:00')
// console.log(now, future)
// 得到剩余的时间戳 转换为秒
const seconds = (future - now) / 1000
// console.log(seconds)
const times = getDHSS(seconds)
// console.log(times)
document.querySelector('#date').innerHTML = times[0]
document.querySelector('#hour').innerHTML = times[1]
document.querySelector('#minutes').innerHTML = times[2]
document.querySelector('#scond').innerHTML = times[3]
}
// 通过秒数 计算天、小时、分钟、秒 函数
function getDHSS(seconds) {
let d = parseInt(seconds / 60 / 60 / 24); // 计算天数
let h = parseInt(seconds / 60 / 60 % 24) // 计算小时
let m = parseInt(seconds / 60 % 60); // 计算分数
let s = parseInt(seconds % 60); // 计算当前秒数
d = fillZero(d)
h = fillZero(h)
m = fillZero(m)
s = fillZero(s)
return [d, h, m, s]
}
// 补零操作函数
function fillZero(value) {
return value < 10 ? '0' + value : value
}
</script>
节点类型
DOM节点:DOM树里每一个内容都称之为节点
-
元素节点
- 所有的标签 比如 body、 div
- html 是根节点
-
属性节点
- 所有的属性 比如 href
-
文本节点
- 所有的文本
3.2、查
节点关系:根据标签的层次关系查找对应元素对象
- 父节点
- 子节点
- 兄弟节点
父节点查找
parentNode 属性:返回最近一级的父节点,找不到返回为null
具体语法:子元素.parentNode
<div class="grandpa">
grandpa
<div class="father">
father
<div class="son">child</div>
</div>
</div>
<script>
const son = document.querySelector('.son')
console.log(son.parentNode) // 返回dom对象
console.log(son.parentNode.parentNode) // 返回dom对象
</script>
子节点查找
childNodes属性:获得所有子节点、包括文本节点(空格、换行)、注释节点等
children 属性
- 仅获得所有元素节点
- 返回的还是一个伪数组
具体语法:子元素.children
<ul>
<li>这是第1个小li</li>
<li>这是第2个小li</li>
<li>这是第3个小li</li>
<li>这是第4个小li</li>
<li>这是第5个小li</li>
<li>这是第6个小li</li>
</ul>
<script>
const ul = document.querySelector('ul')
console.log(ul.children) // 伪数组
</script>
兄弟关系查找
nextElementSibling 属性:下一个兄弟节点
previousElementSibling 属性:上一个兄弟节点
<ul>
<li>这是第1个小li</li>
<li>这是第2个小li</li>
<li>这是第3个小li</li>
<li>这是第4个小li</li>
<li>这是第5个小li</li>
<li>这是第6个小li</li>
</ul>
<script>
const li4 = document.querySelector('ul>li:nth-child(4)')
console.log(li4.previousElementSibling) // 上一个兄弟 这是第3个小li
console.log(li4.nextElementSibling) // 下一个兄弟 这是第5个小li
</script>
3.3、增
很多情况下,需要在页面中增加元素,比如点击评论按钮,就可以在页面新增一条评论信息
一般情况下,新增节点,都会按照如下操作
- 创建一个新的节点
- 把创建的新的节点放入到指定的元素内部
即创造出一个新的网页元素,再添加到网页内,一般先创建节点,然后插入节点
创建元素节点方法:document.createElement('节点名称')
要想在界面显示并看到,还得插入到某个父元素中
添加到最后
插入到父元素的最后一个子元素: 父元素.appendChild(要插入的元素)
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
// 创建节点
const li = document.createElement('li')
// console.log(li)
li.innerHTML = '我是使用js创建的li'
const ul = document.querySelector('ul')
ul.appendChild(li) // 追加到父元素的末尾
</script>
添加到指定元素前面
插入到父元素中某个子元素的前面:父元素.insertBefore(要插入的元素,添加到那个元素前面)
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
</ul>
<script>
const li = document.createElement('li')
li.innerHTML = '我是会插队的li'
ul.insertBefore(li2, ul.children[0]) // 将新创建的li插入到ul第一个子元素前面
</script>
克隆节点
特殊情况下,新增节点,按照如下操作:
- 复制一个原有的节点
- 把复制的节点放入到指定的元素内部
具体语法:元素.cloneNode(布尔值)
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
const ul = document.querySelector('ul')
// 克隆节点
const liOne = ul.children[0].cloneNode(true)
// console.log(liOne)
// 追加
ul.appendChild(liOne)
</script>
cloneNode会克隆出一个跟原标签一样的元素,括号内传入布尔值
- 若为true,则代表克隆时会包含后代节点一起克隆
- 若为false,则代表克隆时不包含后代节点
- 默认为false
3.5、删
若一个节点在页面中已不需要时,可以删除它
在 JavaScript 原生DOM操作中,要删除元素必须通过父元素删除
具体语法:父元素.removeChild(子元素)
<ul>
<li>我是将要被移除的li</li>
</ul>
<script>
// 获取父节点
const ul = document.querySelector('ul')
// 删除 ul下面第一个li
ul.removeChild(ul.children[0])
</script>
注:
- 如不存在父子关系则删除不成功
- 删除节点和隐藏节点(display:none) 有区别的: 隐藏节点还是存在的,但是删除,则从html结构中删除节点
4、操作BOM
41、window对象
BOM(Browser Object Model ) 是浏览器对象模型,BOM提供了独立于内容的、可以与浏览器窗口进行互动的对象结构
- window对象是一个全局对象,也可以说是JavaScript中的顶级对象
- 像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是window的
- window对象下的属性和方法调用的时候可以省略window
JavaScript 内置的一个用来让代码延迟执行的函数,叫 setTimeout
具体语法:setTimeout(回调函数,延迟的毫秒数)
setTimeout 仅仅只执行一次,所以可以理解为就是把一段代码延迟执行,平时编写会省略window
<script>
// 延迟函数
let timer = setTimeout(function () {
console.log('5秒钟后我会自动执行一次');
}, 5000)
// 清除延时器
clearTimeout(timer)
</script>
注:
- 延时器需要等待,所以在它后面的代码先执行
- 每一次调用延时器都会产生一个新的延时器
两种定时器对比:执行的次数
- 延时函数:只执行一次(定时炸弹)
- 间歇函数:每隔一段时间就执行一次,除非手动清除(笛音雷)
JS 执行机制
JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事
这是因为 Javascript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作DOM 而诞生的。比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行。应该先进行添加,之后再删除
单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是:如果 JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉
为解决该问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许JavaScript 脚本创建多个线程。于是,JS 中出现了同步和异步
同步任务
同步任务都在主线程上执行,形成一个执行栈
异步任务
一般而言,异步任务有以下三种类型:
- 普通事件,如 click、resize 等
- 资源加载,如 load、error 等
- 定时器,包括 setInterval、setTimeout 等
异步任务:异步任务相关添加到任务队列中(任务队列也称为消息队列)
执行顺序
- 先执行执行栈中的同步任务
- 异步任务放入任务队列中
- 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行
<script>
console.log(1) // 执行栈
console.log(2) // 执行栈
// 任务队列
setTimeout(function () {
console.log(3)
}, 0)
console.log(4) // 执行栈
</script>
由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环(event loop)
location对象
location 的数据类型是对象,它拆分并保存了 URL 地址的各个组成部分
href属性
href 属性获取完整的 URL 地址,对其赋值时用于地址的跳转
<script>
console.log(location.href)
location.href = 'https://www.hua.com'
</script>
search属性
search 属性获取地址中携带的参数,符号 ?后面部分,通常和表单一起使用(且表单提交为get方式)
<form action="" method="get">
<input type="text" name="uname">
<input type="password" name="pwd">
<input type="submit" value="提交">
<input type="reset" value="重置">
</form>
<script>
console.log(location.search) // ?uname=admin&pwd=666666
</script>
hash属性
hash 属性获取地址中的啥希值,符号 # 后面部分
<a href="#/my/">我的</a>
<a href="#/my/friend">我的关注</a>
<a href="#/my/favorite">我的收藏</a>
<a href="#/download">下载</a>
<script>
console.log(location.hash) // #/my/favorite
</script>
经常用于不刷新页面,显示不同页面,比如 网易云音乐
reload方法
reload 方法用来刷新当前页面,传入参数 true 时表示强制刷新
<button>刷新</button>
<script>
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
location.reload() // f5刷新页面
location.reload(true) // ctrl + f5 强制刷新
})
</script>
其他方法
<script>
console.log(location.host) // 设置或返回主机名和当前URL的端口号
console.log(location.hostname) // 设置或返回当前URL的主机名
console.log(location.protocol) // 协议
console.log(document.title) // 获取文档标题
document.title = '开启探索之旅' // 设置文档标题
</script>
history对象
history 的数据类型是对象,主要管理历史记录, 该对象与浏览器地址栏的操作相对应,如前进、后退、历史记录等
<script>
history.forward() // 加载下一个url
history.back() // 加载前一个url
history.go(1) // 前进 等价于 history.forward()
history.go(-1) // 后退 等价于 history.back()
</script>
navigator对象
navigator的数据类型是对象,该对象下记录了浏览器自身的相关信息
通过 userAgent 检测浏览器的版本及平台
<script>
// 检测 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 = 'file:///E:/VSCodeProject/Project/m_hua/index.html'
}
})()
</script>
4.2、本地存储
- 数据存储在用户浏览器中
- 设置、读取方便、甚至页面刷新不丢失数据
- 容量较大,sessionStorage和localStorage约 5M 左右
本地存储分类
localStorage
可以将数据永久存储在本地(用户的电脑),除非手动删除,否则关闭页面也会存在
特性:
- 可以多窗口(页面)共享(同一浏览器可以共享)
- 以键值对的形式存储使用
存储:localStorage.setItem(key, value)
查询:localStorage.getItem(key)
删除:localStorage.removeItem(key)
<script>
let name = 'admin'
let pwd = '666666'
// 键值对
localStorage.setItem('uname', name)
localStorage.setItem('upwd', pwd)
console.log(localStorage.getItem(uname)) // admin
localStorage.removeItem('uname')
</script>
注:本地存储只能存储字符串数据类型!
浏览器查看本地存储数据
sessionStorage
特性:
- 生命周期为关闭浏览器窗口
- 在同一个窗口(页面)下数据可以共享
- 以键值对的形式存储使用
存储复杂数据类型
本地只能存储字符串,无法存储复杂数据类型
解决方案:需要将复杂数据类型转换成JSON字符串,在将其存储到本地
对象转JSON串:JSON.stringify(复杂数据类型)
JSON串转对象:JSON.parse(JSON字符串)
<script>
const user = {
uid: 1001,
uname: 'admin',
pwd: 'abc666',
email: 'admin@qq.com',
gender: '女'
}
// 存储复杂数据类型
// localStorage.setItem(user.uid, user)
// console.log(localStorage.getItem(user.uid))
// 需要将其转换为 json 字符串 对象转json字符串
const userJson = JSON.stringify(user)
localStorage.setItem(user.uid, userJson)
// json字符串
console.log(userJson)
// json字符串转对象
const u1 = JSON.parse(userJson)
// 对象
console.log(u1)
</script>
5、正则表达式
5.1、什么是正则表达式?
正则表达式(Regular Expression)是用于匹配字符串中字符组合的模式。在 JavaScript中,正则表达式也是对象
通常用来查找、替换那些符合正则表达式的文本,许多语言都支持正则表达式
应用场景:
- 验证表单(匹配):用户名表单只能输入英文字母、数字或者下划线
- 过滤敏感词(替换):过滤掉页面内容中的一些敏感词
- 提取内容(提取):从字符串中获取想要的特定部分
5.2、正则使用
- 定义规则
- 查找
定义正则表达式语法:const 变量名 = /表达式/
其中/ /
是正则表达式字面量
test()
判断是否有符合规则的字符串:regObj.test(被检测的字符串)
test() 方法 用来查看正则表达式与指定的字符串是否匹配
<script>
const str = '昨夜西风凋碧树,独上高楼,望尽天涯路'
// 定义规则
const reg = /东风/
// 是否匹配
console.log(reg.test(str)) // false
</script>
如果正则表达式与指定的字符串匹配 ,返回true,否则false
exec()
检索(查找)符合规则的字符串:regObj.exec(被检测的字符串)
exec() 方法 在一个指定字符串中执行一个搜索匹配
<script>
const str = '昨夜西风凋碧树,独上高楼,望尽天涯路'
// 定义规则
const reg = /西风/
// 是否匹配
console.log(reg.exec(str)) // 返回数组
</script>
如果匹配成功,exec() 方法返回一个数组,否则返回null
5.3、元字符
普通字符:大多数的字符仅能够描述它们本身,这些字符称作普通字符,也就是说普通字符只能够匹配字符串中与它们相同的字符
元字符(特殊字符)
是一些具有特殊含义的字符,可以极大提高了灵活性和强大的匹配功能
- 比如,规定用户只能输入英文26个英文字母,普通字符的话 abcdefghijklm……
- 换成元字符写法: [a-z]
元字符分类
- 边界符(表示位置,开头和结尾,必须用什么开头,用什么结尾)
- 量词 (表示重复次数)
- 字符类 (比如 \d 表示 0~9)
边界符
正则表达式中的边界符(位置符)用来提示字符所处的位置,主要有两个字符
边界符 | 描述 |
---|---|
^ | 匹配字符串的开始 |
$ | 匹配字符串的结束 |
<script>
// 必须以a开头 z结尾 中间不能包含任何内容 包含任何内容都会返回 false
console.log(/^az$/.test('az')) // true
console.log(/^az$/.test('abz')) // false
console.log(/^az$/.test('aazz')) // false
</script>
如果 ^ 和 $ 在一起,表示必须是精确匹配
量词
量词用来设定某个模式出现的次数
量词 | 描述 |
---|---|
* | 匹配前一项零次或更多次 |
+ | 匹配前一项一次或更多次 |
? | 匹配前一项零次或一次 |
{n} | 匹配前一项n次 |
{n,} | 匹配前一项n次或更多次 |
{n,m} | 匹配前一项n次到m次 |
<script>
// 零次或更多次
console.log(/^a+z$/.test('aaaaaaz')) // true
// 零次和一次
console.log(/^a?z$/.test('z')) // true
console.log(/^a?z$/.test('aaz')) // false
// 1到3次 最少一次 最多三次
console.log(/^a{1,3}z$/.test('aaaz')) // true
console.log(/^a{1,3}z$/.test('aaaaz')) // false
</script>
字符类
预定义:指的是 某些常见模式的简写方式
后面的字符串只要包含 abc 中任意一个字符,都返回 true
[ ] 匹配字符集合
<script>
console.log(/^[adc]$/.test('a')) // true
console.log(/^[adc]$/.test('d')) // true
console.log(/^[adc]$/.test('c')) // true
console.log(/^[adc]$/.test('ad')) // false
</script>
使用连字符 - 表示一个范围
- [a-z] 表示 a 到 z 26个英文字母都可以
- [a-zA-Z] 表示大小写都可以
- [0-9] 表示 0~9 的数字都可以
[ ] 里面加上 ^ 取反符号
- [^A-Z] 匹配除了大写字母以外的字符
- 注意必须要写到中括号里面,否则语义发生变化
字符类 | 描述 |
---|---|
\s | 任何空白字符 |
\S | 任何非空白字符 |
\d | 匹配数字字符,等价[0-9] |
\D | 除了数字之外的任何字符,等价于[^0-9] |
\w | 匹配一个数字、下划线或字母字符,等价于[A-Za-z0-9_] |
\W | 除了数字、下划线或字母字符,等价于[^a-zA-z0-9_] |
<script>
const arr = ['15736369663', '18718888898', '15800022263', '15726269663', '15800000056', '15999999766']
// 匹配以 157开头 中间0-9数字(只能出现五次) 663结尾的号码
const reg = /^157\d{5}663$/
let newArr = []
for (let i = 0; i < arr.length; i++) {
if (reg.test(arr[i])) {
newArr.push(arr[i])
}
}
console.log(newArr)
</script>
5.4、修饰符
修饰符约束正则执行的某些细节行为,如是否区分大小写、是否支持多行匹配等
具体语法:/表达式/修饰符
- i 是单词 ignore 的缩写,正则匹配时字母不区分大小写
- g 是单词 global 的缩写,匹配所有满足正则表达式的结果
replace 替换
具体语法:字符串.replace(/正则表达式/,'替换的文本')
<script>
const str = 'php是世界上最好的语言 快来学习PHP吧!'
const newStr = str.replace(/php/ig, 'Java')
console.log(newStr)
</script>