本文是基于 B 站 pink 老师前端 JavaScript 课程整理的学习笔记
Dom获取&属性操作
变量声明
由前文(前端基础JavaScript 笔记)知变量声明有三个 var let 和 const
现在我们建议:const 优先
原因:
- const 语义化更好
- 很多变量我们声明的时候就知道他不会被更改了(值有变化就改用 let ,比如 i++里的i)
- 实际开发中也是,比如react框架,基本用const
但是数组和对象可以将 let 改为 const.
对于引用数据类型,const声明的变量,里面存的不是值,不是值,是地址!
为什么const声明的对象可以修改里面的属性?
- 因为对象是引用类型,里面存储的是地址,只要地址不变,就不会报错
- 建议数组和对象使用 const 来声明
什么时候使用let声明变量?
- 如果基本数据类型的值或者引用类型的地址发生变化的时候,需要用let
- 比如 一个变量进行加减运算,比如 for循环中的 i++
1.Web API 基本认知
1.1 作用和分类
作用: 就是使用 JS 去操作 html 和浏览器
分类:DOM (文档对象模型)、BOM(浏览器对象模型)
1.2 什么是DOM
DOM(Document Object Model——文档对象模型)是浏览器提供的一套专门用来操作网页内容 的功能,用于开发网页内容特效和实现用户交互。
1.3 DOM树
- 将 HTML 文档以树状结构直观的表现出来,我们称之为文档树或 DOM 树
- 描述网页内容关系的名词
- 作用:文档树直观的体现了标签与标签之间的关系
1.4 DOM对象(重要)
DOM对象:浏览器根据html标签生成的 JS对象(DOM对象)
DOM的核心思想:把网页内容当做对象来处理
document 对象:
- 是 DOM 里提供的一个对象
- 所以它提供的属性和方法都是用来访问和操作网页内容的 。例:document.write()
- 网页所有内容都在document里面
<div>一段话</div>
<!-- <div>在HTML里叫标签,在这里叫DOM对象 -->
2.获取DOM对象
2.1 根据CSS选择器来获取DOM元素 (重点)
2.1.1.选择匹配的第一个元素
document.querySelector('css选择器')
参数: 包含一个或多个有效的CSS选择器 字符串(必须是字符串,也就是必须加引号)
返回值: CSS选择器匹配的第一个元素,一个 HTMLElement对象。 如果没有匹配到,则返回null。
能直接操作修改
2.1.2. 选择匹配的多个元素
document.querySelectorAll('css选择器')
例如
document.querySelectorAll('ul li')
参数: 包含一个或多个有效的CSS选择器字符串(必须是字符串,也就是必须加引号)
返回值: CSS选择器匹配的NodeList 对象集合
不能直接操作修改(获取的是数组)
得到的是一个伪数组:
- 长度有索引号的数组
- 是没有 pop() push() 等数组方法 想要得到里面的每一个对象,则需要遍历(for)的方式获得。
哪怕只有一个元素,通过querySelectAll() 获取过来的也是一个伪数组,里面只有一个元素而已
2.2 其他获取DOM元素方法(了解)
//根据id获取一个元素
document.getElementById('nav')
//根据标签获取一类元素 获取页面索引的div
document.getElementsByTagName('div')
//根据类名获取元素 获取页面所有类名为W
document.getElementsByClassName('W')
3.操作元素内容
3.1.对象.innerText 属性
将文本内容添加/更新到任意标签位置, 显示纯文本,不解析标签
<div class="box">文字</div>
<script>
const box = document.querySelector('.box')
console.log(box.innerText) //获取文字内容
box.innerText = '新内容' //修改文字内容
</script>
3.2.对象.innerHTML 属性
将文本内容添加/更新到任意标签位置 ,会解析标签,多标签建议使用模板字符
<div class="box">文字</div>
<script>
const box = document.querySelector('.box')
console.log(box.innerText) //获取文字内容
box.innerHTML = '<strong>新内容</strong>' //修改文字内容
</script>
4.操作元素属性
4.1 操作元素常用属性
还可以通过 JS 设置/修改标签元素属性,比如通过 src更换 图片
最常见的属性比如: href、title、src 等
对象.属性=值
const picture = document.querySelector('img')
picture.title = 'JavaScript笔记'
案例:随机图片
<body>
<img src="./images/1.webp" alt="">
<script>
// 取到 N ~ M 的随机整数
function getRandom(N, M) {
return Math.floor(Math.random() * (M - N + 1)) + N
}
// 1. 获取图片对象
const img = document.querySelector('img')
// 2. 随机得到序号
const random = getRandom(1, 6)
// 3. 更换路径
img.src = `./images/${random}.webp`
</script>
</body>
4.2 操作元素样式属性
还可以通过 JS 设置/修改标签元素的样式属性。比如通过 轮播图小圆点自动更换颜色样式 ,点击按钮可以滚动图片,这是移动的图片的位置 left 等等
4.2.1.通过 style 属性操作CSS
对象.style.样式属性 = '值'
<body>
<div class="box"></div>
<script>
// 1. 获取元素
const box = document.querySelector('.box')
//2. 修改样式属性 对象.style.样式属性 = '值' 别忘了跟单位
box.style.width = '300px'
// 多组单词的采取 小驼峰命名法
box.style.backgroundColor = 'hotpink'
box.style.border = '2px solid blue'
box.style.borderTop = '2px solid red'
</script>
</body>
页面刷新,页面随机更换背景图片
。。。。。。
<style>
body{
background: url('../photo/1.jpg') no-repeat top center/cover;
}
</style>
</head>
<body>
<script>
let num=Math.floor(Math.random()*7)+1
document.body.style.backgroundImage = `url(../photo/${num}.jpg)`
</script>
</body>
4.2.2. 操作类名(className) 操作CSS
使用:修改的样式比较多,直接通过style属性修改比较繁琐
元素.className = '类名'
// 1. 获取元素
const div = document.querySelector('div')
// 2.添加类名 class 是个关键字 我们用 className
div.className = 'nav box' //保证前面的类不会失效
由于class是关键字, 所以使用className去代替
className是使用新值换旧值, 如果需要添加一个类,需要保留之前的类名
4.2.3. 通过 classList 操作类控制CSS
为了解决className 容易覆盖以前的类名,我们可以通过classList方式追加和删除类名
//追加一个类
元素.classList.add('类名')
//删除一个类
元素.classList.remove('类名')
//切换一个类
元素.classList.toggle('类名')
<body>
<div class="box active">文字</div>
。。。。。。
// 1. 获取元素
const box = document.querySelector('.box')
// 2. 修改样式
// 2.1 追加类 add() 类名不加点,并且是字符串
box.classList.add('active')
// 2.2 删除类 remove() 类名不加点,并且是字符串
box.classList.remove('box')
// 2.3 切换类 toggle() 有该类就删掉,没有就加上
box.classList.toggle('active')
使用 className 和classList的区别?
- 修改大量样式的更方便
- 修改不多样式的时候方便
- classList 是追加和删除不影响以前类名
4.3 操作表单元素 属性
获取: DOM对象.属性名
设置: DOM对象.属性名 = 新值
//获取元素
const uname = document.querySelector('input')
//获取值 获取表单里的值 用 表单.value
console.log(uname.value) //不能用innerHTML
//设置表单的值
uname.value = '文本'
表单属性中添加就有效果,移除就没有效果,一律使用布尔值表示 如果为true 代表添加了该属性 如果是false 代表移除了该属性,比如: disabled、checked、selected
//获取
const ipt = document.querySelector('input')
//默认为false,不选中
ipt.checked = true
//获取
const button = document.querySelector('botton')
//默认为false,不禁用
button.disabled = true
4.3.自定义属性
标准属性: 标签天生自带的属性 比如class id title等, 可以直接使用点语法操作比如: disabled、checked、 selected
自定义属性:
- 在html5中推出来了专门的data-自定义属性
- 在标签上一律以data-开头
- 在DOM对象上一律以dataset对象方式获取
<div data-id="yi" data-spm="...">1</div>
<script>
const one = document.querySelector('div')
console.log(one.dataset) //获取全部的自定义属性值
console.log(one.dataset.id) //yi
</script>
5.定时器-间歇函数
网页中经常会需要一种功能:每隔一段时间需要自动执行一段代码,不需要我们手动去触发,例如:网页中的倒计时
目标:能够使用定时器函数重复执行代码
定时器函数可以开启和关闭定时器
5.1.开启定时器
setInterval(函数,时间间隔)
// 开启定时器 setInterval(函数,间隔时间) 单位ms
setInterval(function(){
console.log('一秒钟执行一次')
}, 1000)
setInterval(函数名,时间间隔)
function fn(){
console.log('一秒钟执行一次')
}
setInterval(fn , 1000) //不用写小括号!
//如果要加小括号就要加引号(很少见)
setInterval('fn()',1000)
1.函数名字不需要加括号
2.定时器返回的是一个id数字
5.2.关闭定时器
关闭定时器: let 变量名 = setInterval(函数,间隔时间)
clearInterval(变量名)
function fn(){
console.log('一秒钟执行一次')
}
let a = setInterval(fn, 1000)
clearInterval(a)
1.函数名字不需要加括号
2.定时器返回的是一个id数字
Dom事件基础
1.事件监听(绑定)
什么是事件?
事件是在编程时系统内发生的动作或者发生的事情,比如用户在网页上单击一个按钮
什么是事件监听?
就是让程序检测是否有事件产生,一旦有事件触发,就立即调用一个函数做出响应,也称为 绑定事件或者注册事件,比如鼠标经过显示下拉菜单,比如点击可以播放轮播图等等
元素对象(即 事件源).addEventListener('事件类型',要执行的函数)
事件监听三要素:
- 事件源: 那个dom元素被事件触发了,要获取dom元素
- 事件类型: 用什么方式触发,比如鼠标单击 click、鼠标经过 mouseover 等
- 事件调用的函数: 要做什么事(不是立即执行的,要有触发条件)
案例:随机点名案例(js部分)
// 数据数组
const arr = ['马超', '黄忠', '赵云', '关羽', '张飞']
const qs=document.querySelector('.qs')
//开始按钮模块
let random=0
const start = document.querySelector('.start')
start.addEventListener('click',function (){
// 随机数 写let,const声明就成局部变量,不能在外面关闭了
timerId=setInterval(function(){
const random = parseInt(Math.random()*arr.length)
qs.innerHTML = arr[random]
}),30})
const end=document.querySelector('.end')
end.addEventListener('click',function(){
clearInterval(timerId)
arr.splice(random,1)
if(arr.length===1){
start.disabled=end.disabled=true
}
})
2.事件类型
鼠标事件 | 焦点事件 | 键盘事件 | 文本事件 |
鼠标触发 | 表单获得光标 | 键盘触发 | 表单输入触发 |
click 鼠标点击 mouseenter 鼠标经过 mouseleaver 鼠标离开 |
focus 获得焦点 blur 失去焦点 |
Keydown 键盘按下 keyup 键盘抬起 |
input 用户输入事件 |
const input=document.querySelector('input')
//鼠标
input.addEventListener('click' , function(){ 函数体 })
//焦点事件
input.addEventListener('blur' , function(){ 函数体 })
//键盘
input.addEventListener('keydown' , function(){ 函数体 })
//用户输入文本
input.addEventListener('input' , function(){ console.log(text.value)}) //text.value文本值
3.事件对象
事件对象也是个对象,这个对象里有事件触发时的相关信息 , 例如:鼠标点击事件中,事件对象就存了鼠标点在哪个位置等信息
使用场景
- 可以判断用户按下哪个键,比如按下回车键可以发布新闻
- 可以判断鼠标点击了哪个元素,从而做相应的操作
获取事件对象:
事件绑定的回调函数的第一个参数就是事件对象
一般命名为event、ev、e
元素.addEventListener('click', function(e){ })
//此处的e就是事件对象
部分常用属性
- type 获取当前的事件类型
- clientX/clientY 获取光标相对于浏览器可见窗口左上角的位置
- offsetX/offsetY 获取光标相对于当前DOM元素左上角的位置
- key 用户按下的键盘键的值
4.环境对象
环境对象:指的是函数内部特殊的变量 this ,它代表着当前函数运行时所处的环境
作用:弄清楚this的指向,可以让我们代码更简洁
- 函数的调用方式不同,this 指代的对象也不同
- 【谁调用, this 就是谁】 是判断 this 指向的粗略规则
- 直接调用函数,其实相当于是 window.函数,所以 this 指代 window
//普通函数的this指向window
function fn()
{
console.log(this)
}
const btn = document.querySelector('button')
btn.addEventListener('click',function(){
console.log(this) //btn对象
})
使用,例:点击按钮变红
const btn = document.querySelector('button')
btn.addEventListener('click',function(){
this.style.color = 'red'
})
5.回调函数
如果将函数 A 做为参数传递给函数 B 时,我们称函数 A 为回调函数
简单理解: 当一个函数当做参数来传递给另外一个函数的时候,这个函数就是回调函数
事件流
1.事件流与两个阶段说明
事件流指的是事件完整执行过程中的流动路径
说明:假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段
简单来说:捕获阶段是 从父到子 冒泡阶段是从子到父
实际工作都是使用事件冒泡为主
2.事件捕获
从DOM的根元素开始去执行对应的事件 (从外到里)
DOM.addEventListener(事件类型,事件处理函数,是否使用捕获机制)
捕获从大到小,冒泡从小到大
document.addEventListener('click', function () {
alert('我是爷爷')
})
fa.addEventListener('click', function () {
alert('我是爸爸')
})
son.addEventListener('click', function (e) {
alert('我是儿子')
// 组织流动传播 事件对象.stopPropagation()
e.stopPropagation()
})
3.事件冒泡
当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡
- 简单理解:当一个元素触发事件后,会依次向上调用所有父级元素的 同名事件
- 事件冒泡是默认存在的
- L2事件监听第三个参数是 false,或者默认都是冒泡
4.阻止冒泡
问题:因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素
需求:若想把事件就限制在当前元素内,就需要阻止事件冒泡
前提:阻止事件冒泡需要拿到事件对象
注意:此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效
事件对象.stopPropagation()
我们某些情况下需要阻止默认行为的发生,比如 阻止 链接的跳转,表单域跳转
e.preventDefault()
5.解绑事件
on事件方式(L0),直接使用null覆盖就可以实现事件的解绑
btn.onclick = function(){
alert('点击')
btn.onclick = null
}
addEventListener方式(L2)
removeEventListener(事件类型, 事件处理函数, [获取捕获或者冒泡阶段])
//获取捕获或者冒泡阶段 可省
function fn() {
alert('点击了')
}
btn.addEventListener('click', fn)
// L2 事件移除解绑
btn.removeEventListener('click', fn)
匿名函数无法被解绑
鼠标经过事件的区别
- mouseover 和 mouseout 会有冒泡效果
- mouseenter 和 mouseleave 没有冒泡效果 (推荐)
两种注册事件的区别
传统on注册(L0)
- 同一个对象,后面注册的事件会覆盖前面注册(同一个事件)
- 直接使用null覆盖偶就可以实现事件的解绑
- 都是冒泡阶段执行的
事件监听注册(L2)
- 语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)
- 后面注册的事件不会覆盖前面注册的事件(同一个事件)
- 可以通过第三个参数去确定是在冒泡或者捕获阶段执行
- 必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)
- 匿名函数无法被解绑
事件委托
事件委托是利用事件流的特征解决一些开发需求的知识技巧
优点:减少注册次数,可以提高程序性能
原理:事件委托其实是利用事件冒泡的特点。
给父元素注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件
实现:事件对象.target. tagName 可以获得真正触发事件的元素
ul.addEventListener('click', function(){}) 执行父级点击事件
// 点击每个小li 当前li 文字变为红色
// 按照事件委托的方式 委托给父级,事件写到父级身上
// 1. 获得父元素
const ul = document.querySelector('ul')
ul.addEventListener('click', function (e) {
// this.style.color = 'red' this指向父元素
// console.dir(e.target) // 就是我们点击的那个对象
// e.target.style.color = 'red'
// 我们只要点击li才会有效果
if (e.target.tagName === 'LI') {
e.target.style.color = 'red'
}
})
其他事件
1.页面加载事件
加载外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件
- 有些时候需要等页面资源全部处理完了做一些事情
- 老代码喜欢把 script 写在 head 中,这时候直接找 dom 元素找不到
事件名:load
监听页面所有资源加载完毕: 给 window 添加 load 事件
window>document>html
此处的script是写在head里的
等待页面所有资源加载完毕,就回去执行回调函数
window.addEventListener('load', function () {
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
alert(11)
})
})
img.addEventListener('load', function () {
// 等待图片加载完毕,再去执行里面的代码
})
当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像等完 全加载
事件名:DOMContentLoaded
监听页面DOM加载完毕:给 document 添加 DOMContentLoaded 事件
此处的script是写在head里的
document.addEventListener('DOMContentLoaded', function () {
const btn = document.querySelector('button')
btn.addEventListener('click', function () {
alert(11)
})
})
2.元素滚动事件
滚动条在滚动的时候持续触发的事件
很多网页需要检测用户把页面滚动到某个区域后做一些处理, 比如固定导航栏,比如返回顶部
事件名:scroll
window.addEventListener('scroll',function(){
.....
})
给 window 或 document 添加 scroll 事件
监听某个元素的内部滚动直接给某个元素加即可
获取位置
scrollLeft和scrollTop (属性)
- 获取被卷去的大小
- 获取元素内容往左、往上滚出去看不到的距离
- 这两个值是可读写的
开发中,我们经常检测页面滚动的距离,比如页面滚动100像素,就可以显示一个元素,或者固定一个元素
尽量在scroll事件里面获取被卷去的距离
<style>
body {
padding-top: 100px;
height: 3000px;
}
div {
display: none;
margin: 100px;
overflow: scroll;
width: 200px;
height: 200px;
border: 1px solid #000;
}
</style>
....
<script>
const div = document.querySelector('div')
// 页面滚动事件
window.addEventListener('scroll', function () {
// 必须写在里面
const n = document.documentElement.scrollTop //得到数字型数据 不带单位
if (n >= 100) {
div.style.display = 'block'
} else {
div.style.display = 'none'
}
})
</script>
document.documentElement.scrollTop = 0 //回到页面顶部
3.页面尺寸事件
会在窗口尺寸改变的时候触发事件:resize
window.addEventListener('resize',function(){
let w = documentElement.clientWidth //检测屏幕宽度 clientWidth不包含边框
console.log(w)
}
获取宽高:
- 获取元素的可见部分宽高(不包含边框,margin,滚动条等)
- clientWidth和clientHeight
- 获取出来的是数值,方便计算
- 注意: 获取的是可视宽高, 如果盒子是隐藏的,获取的结果是0
获取位置:
- 获取元素距离自己定位父级元素的左、上距离
- offsetLeft和offsetTop 注意是只读属性
- element.getBoundingClientRect() 方法返回元素的大小及其相对于视口的位置
offsetWidth和offsetHeight是得到元素的内容 + padding + border
以带有定位的父级 ,如果都没有则以 文档左上角为准
只读是不能赋值只可读
Dom节点
1.日期对象
1.1. 实例化
在代码中发现了 new 关键字时,一般将这个操作称为实例化
创建一个数据对象并获取时间
获得当前事件
const date = new Date()
获得指定时间
const date = new Date('2008-8-8 08:30:00')
console.log(date)
1.2.日期对象方法
因为日期对象返回的数据我们不能直接使用,所以需要转换为实际开发中常用的格式
Date 对象方法
方法 | 描述 |
---|---|
getDate() | 从 Date 对象返回一个月中的某一天 (1 ~ 31)。 |
getDay() | 从 Date 对象返回一周中的某一天 (0 ~ 6)。 |
getFullYear() | 从 Date 对象以四位数字返回年份。 |
getHours() | 返回 Date 对象的小时 (0 ~ 23)。 |
getMilliseconds() | 返回 Date 对象的毫秒(0 ~ 999)。 |
getMinutes() | 返回 Date 对象的分钟 (0 ~ 59)。 |
getMonth() | 从 Date 对象返回月份 (0 ~ 11)。 |
getSeconds() | 返回 Date 对象的秒数 (0 ~ 59)。 |
getTime() | 返回 1970 年 1 月 1 日至今的毫秒数。 |
parse() | 返回1970年1月1日午夜到指定日期(字符串)的毫秒数。 |
setDate() | 设置 Date 对象中月的某一天 (1 ~ 31)。 |
setFullYear() | 设置 Date 对象中的年份(四位数字)。 |
setHours() | 设置 Date 对象中的小时 (0 ~ 23)。 |
setMilliseconds() | 设置 Date 对象中的毫秒 (0 ~ 999)。 |
setMinutes() | 设置 Date 对象中的分钟 (0 ~ 59)。 |
setMonth() | 设置 Date 对象中月份 (0 ~ 11)。 |
setSeconds() | 设置 Date 对象中的秒钟 (0 ~ 59)。 |
toDateString() | 把 Date 对象的日期部分转换为字符串。 |
toLocaleDateString() | 根据本地时间格式,把 Date 对象的日期部分转换为字符串。 |
toLocaleTimeString() | 根据本地时间格式,把 Date 对象的时间部分转换为字符串。 |
toLocaleString() | 根据本地时间格式,把 Date 对象转换为字符串。 |
toString() | 把 Date 对象转换为字符串。 |
toTimeString() | 把 Date 对象的时间部分转换为字符串。 |
UTC() | 根据世界时返回 1970 年 1 月 1 日 到指定日期的毫秒数。 |
valueOf() | 返回 Date 对象的原始值。 |
now() | 返回当前时间的毫秒数。 |
// 获得日期对象
const date = new Date()
// 使用里面的方法
console.log(date.getFullYear())
console.log(date.getMonth() + 1) // 月份要 + 1
console.log(date.getDate())
console.log(date.getDay()) // 星期几
//动态时间
const div = document.querySelector('div')
function getMyDate() {
const date = new Date()
let h = date.getHours()
let m = date.getMinutes()
let s = date.getSeconds()
h = h < 10 ? '0' + h : h
m = m < 10 ? '0' + m : m
s = s < 10 ? '0' + s : s
return `今天是: ${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}号 ${h}:${m}:${s}`
}
div.innerHTML = getMyDate()
setInterval(function () {
div.innerHTML = getMyDate()
}, 1000)
1.3.时间戳
是指1970年01月01日00时00分00秒起至现在的毫秒数,它是一种特殊的计量时间的方式
算法:
- 将来的时间戳 - 现在的时间戳 = 剩余时间毫秒数
- 剩余时间毫秒数转换为剩余时间的年月日时分秒就是倒计时时间
- 比如 将来时间戳 2000ms - 现在时间戳 1000ms = 1000ms
- 1000ms 转换为就是 0小时0分1秒
三种方式获取时间戳:
1. 使用 getTime() 方法 必须实例化
const date = new Date()
console.log(date.getTime())
2. 简写 +new Date()
console.log(+new Date())
3. 使用 Date.now() 无需实例化
console.log(Date.now())
但是只能得到当前的时间戳, 而前面两种可以返回指定时间的时间戳
例:
console.log(+new Date(' 2022-4-1 12:00:00 '))