前言
- Web API:是一套操作 网页内容(DOM) 与 浏览器窗口(BOM) 的 对象;
- API:就是一些预定义好的方法,这些方法可以实现特定的功能,开发人员可以直接使用;
- Web API:浏览器提供了一套操作网页和浏览器的方法,通过这些方法可以轻松的操作元素和浏览器;
- 复杂数据类型 用 const 声明:复杂数据类型 放在 堆 里面,栈 里面存放的 地址 指向 堆 里面存放的 数据,变 的只是 堆 里面存放的 数据,存放数据的 地址 不变;
- 变量名 ➡ 栈地址 ➡ 堆数据;
- 解释:
const实际上指的并不是变量的值不得改动,
其实说的是变量指向的那个内存空间中保存的数据不得改变
简单数据类型是将值保存在栈中,所以用const声明简单数据类型时,值不得改变,相当于常量
而复杂数据类型是将引用地址保存在栈中,具体的值保存在堆中
那么用const声明复杂数据类型时,比如对象,数组时,只能保证的是这个地址是固定不变的
堆中保存的数据是不是可变的,就无法保证了
一、❗❗ DOM(文档对象模型)
Document Object Model
— 文档对象模型;- 定义:是HTML文档的编程接口。它提供了对文档的结构化的表述,并定义了一种方式可以在从程序中对该结构进行访问,从而改变文档的结构,样式和内容。DOM 将文档解析为一个由节点和对象(包含属性和方法的对象)组成的结构集合。简言之,它会将 web 页面和程序语言连接起来。进而操作网页的内容,实现各种功能和部分特效。
1.1 DOM树 和 DOM对象
1.1.1 DOM树
- DOM树:一种树状结构,把页面标签按照树状结构排列好,结构更加清晰;
- 每个DOM对象中都有表示层级关系的属性,利用这些属性把所有的DOM对象联系在一起,就形成一个树状结构,称为DOM树;
- 作用:文档树直观的 体现了 标签与标签 之间 的 关系
1.1.2 DOM对象
- DOM对象: 浏览器根据htm标签自动生成的JS对象;
- 浏览器把网页内容翻译成一个个的对象,把 网页内容 的 特征 翻译成 对象 的属性;
- document对象:是DOM里提供的一个对象(DOM里面最大的对象)
- 它提供的属性和方法都是 用来访问 和 操作 网页内容的
1.2 获取DOM对象
- 打印对象:打印元素的时候,以对象的形式展现
console.dir(对象);
1.2.1 ❗❗ 根据CSS选择器来获取DOM元素
- 1️⃣ 选择匹配的 第一个 元素
- 语法:
document.querySelector('css选择器')
- 代码展示:
const li = document.querySelector('ul li')
- 参数:小括号里面 包含 一个 或 多个 有效的css选择器 字符串(引号)
- 返回值:
- css选择器 匹配的 第一个元素,一个 HTMLElement 对象
- ⚠ 如果 没有匹配 到元素就是 null
- ⚠ 注意:必须加引号
- 语法:
- 2️⃣ 选择匹配的 多个 元素
- 语法:
document.querySelectorAll('css选择器')
- 代码展示:
const lis = document.querySelectorAll('li') //NodeList[li, li, li]
- 参数:小括号里面 包含 一个 或 多个 有效的CSS选择器 字符串
- 返回值:
- CSS选择器匹配的 NodeList 对象集合
- 如果页面 不存在 这个 元素,得到的就是一个 空的伪数组
- 把 所有 匹配的 元素 放到 NodeList🟨伪数组🟨 里面 返回
- 有长度,有索引
- 没有push(),pop()等数组方法
- 想要得到里面的每一个对象,则需要遍历获得
- 语法:
1.2.2 获取HTML、body、head、title
document.documentElement --- html
document.body --- body
document.head --- head
document.title --- title
1.2.3 ❌其他获取元素的方式
- 1️⃣根据 id 获取元素
-
document.getElementById('id名称')
- 注意:
- id名称不加 #
-
- 2️⃣根据 标签 获取 一类 元素
-
document.getElementsByTagName('标签名称')
- 注意:
- getElementsByTagName
- 返回值:伪数组
-
- 3️⃣根据 类名 获取元素
-
document.getElementsByClassName('类名')
- 注意:
- 类名不加 .
- getElementsByClassName
- 返回值:伪数组
-
1.3 操作元素内容
1.3.1 对象.innerText 属性
- 将 元素 文本内容 添加/更新 到 任意标签 位置
- 显示 纯文本,不解析标签
- 见代码展示
1.3.2 对象.innerHTML 属性
- 将 元素内容 添加/更新 到任意标签位置
- 包括标签、文本、注释
- 会解析标签,多标签建议使用模板字符串
- ⚠ 既要保留原来内容又要添加新内容,采用 字符串拼接 的方式
-
innerHTML += '要添加的文本'
-
- 见代码展示
代码展示:
<div class="box"> 天地不仁以万物为刍狗!!! </div>
<strong>const box = document.querySelector('.box') // 对象.innerText 属性box.innerText = '扶我起来,我还能干!!!'box.innerText = '<strong>扶我起来,我还能干!!!</strong>'// 对象.innerHTML 属性box.innerHTML = '扶我起来,我还能干!!!'box.innerHTML = '<strong>扶我起来,我还能干!!!</strong>'
</strong>

- ⚠innerText 和 innerHTML 的区别
- innerText
- 只能 获取 元素文本,无法 获取 标签
- ⚠ 在获取文本的时候,包含 子元素 的 文本
- innerHTML
- 可以 获取对象里面的所有 文本+标签
- 代码展示:
<body><div>123<p>456<span>789</span></p></div><script>console.log(document.querySelector('div').innerText);console.log(document.querySelector('div').innerHTML);</script> </body>
- innerText

1.3.3 获取表单元素文本
- 语法: 元素.value
- 获取 输入的字符长度 :元素.value.length
- ⚠ 获取 button 的文本依然使用 innerHTML(button双标签)
- 拓展:
- ⚠ 元素.value.trim() - 去除字符串 左右两侧 的空格
1.4 操作元素属性
1.4.1 操作 元素 属性
- 语法:元素.属性 = ‘值’
- 代码展示:
<img src="./images/1.webp" alt=""> //随机数函数 function getRandom(N, M) {return Math.floor(Math.random() * (M- N + 1)) + N } //获取图片对象 //img在html中是标签,在js中是对象 const pic = document.querySelector('img') // 修改对象的属性值 pic.src = `./images/${getRandom(1, 6) }.webp`
- 代码展示:
1.4.2 控制 样式 属性
- 1️⃣ 通过 style属性 操作css
- 语法:元素.style.样式属性 = ‘值’
- 对于复杂的样式属性建议使用 小驼峰 命名法
- 生成的是 行内样式表,权重很高,只有 !important 能做掉它
- ⚠🔺 只写 属性
- 代码展示:
// 获取对象(元素) const box = document.querySelector('div') // 修改样式属性 对象.style.样式属性 = '值' box.style.width = '400px' box.style.height = '100px' box.style.backgroundColor = 'red' box.style['background-color'] = 'green'
- 语法:元素.style.样式属性 = ‘值’
- 2️⃣ 通过 类名(className) 操作css
- 语法:元素.className = ‘类名’
- ⚠ 注意:会覆盖前面的类名 新值换旧值
- 想要原来的类名 -> 元素.className = ‘旧类名 新类名’
- 代码展示:
<style>div {width: 200px;height: 200px;background-color: pink;}.active {width: 300px;height: 300px;background-color: hotpink;margin-left: 100px;} </style><body><div class="one"></div><script>// 1.获取元素// let box = document.querySelector('css选择器')let box = document.querySelector('div')// 2 给div添加类名 class 是个关键字,选用className替代它// 新值换旧值,会覆盖原来的类名box.className = 'active'// 用旧类名+新类名的形式覆盖原来的类名box.className = 'one active'</script> </body>
- 语法:元素.className = ‘类名’
- ❗3️⃣ 通过 classList 操作类 控制css
- 语法:
- 追加 类:元素.classList.add(‘类名’)
- 删除 类:元素.classList.remove(‘类名’)
- 切换 类:元素.classList.toggle(‘类名’)
- 如果有这个类名就是删掉,没有就追加
- 替换 类:元素.classList.replace(‘旧类名’, ‘新类名’)
- 两个参数,第一个是原来的类名,第二个参数是替换的类名
- 是否包含指定的 类:元素.classList.contains(‘类名’)
- 判断是否包含指定的类名,返回值是布尔值 true / false
- ⚠ 注意:
- 类名 不加 点,并且 是 字符串
- 是 方法,注意加小括号
- 代码展示:
// 获取对象 const box = document.querySelector('.one')// 追加类名 box.classList.add('two') box.classList.add('three')// 删除类名 box.classList.remove('three')// 切换类名 // 如果有这个类名就是删除,没有就是追加 box.classList.toggle('ab') box.classList.toggle('one')// 替换类名 // 将 旧类名box 替换成 新类名box1 box.classList.replace('box', 'box1')// 判断是否包含指定的类名 console.log(box.classList.contains('box')) // true
- 语法:

- classList 和 className区别
- classList 是 追加类名,将新类名追加到旧类名后面,不影响以前的类名
- className 是 覆盖类名,新类名 覆盖 旧类名,影响以前的类名
1.4.3❗❗ 操作 表单元素 属性
- 语法:
- 获取:元素.属性名
- 设置:元素.属性名 = 新值
- 代码展示:获取表单里面的值
const input = document.querySelector('.computer') console.log(input.value) input.value = '扶我起来,我还能干!!!'
- ⚠ 注意:对象.innerHTML 只能获取 普通元素 的 文本,获取 表单元素 的 文本 需要使用 对象.value
- ⚠ 注意: 对象.value 获得的文本是 包含 首尾 的 空白符
- 想要获得 有效的文本 需要使用 trim() 方法[对象.value.trim()],删除首尾的空白符
- ⚠注意:获取 button 的文本还是用 innerHTML
- ⚠ 注意: 对象.value 获得的文本是 包含 首尾 的 空白符
- 表单属性中添加就有效果,移除就没有效果,一律使用 布尔值 表示,如果为 true 代表 添加 了该 属性 ,如果是 false 代表 移除 该 属性
- disabled(禁用)、checked(选中)
- 除了 ’ ', false, 0, null, undefined,NaN 其余的都是true,但是不建议写,建议写布尔值
- 代码展示:
<input type="checkbox" value="" name=""> const input = document.querySelector('.computer') input.checked = true // 让复选框选中 input.checked = false // 让复选框不选中 input.disable = true // 禁用吗? - 禁用(true) input.disable = false // 禁用吗? - 不禁用(false默认值)
1.4.4 ❗❗ 自定义属性
- 属性
- 标准属性:标签天生自带的属性,可以直接使用 点语法 操作
- 自定义属性:
- 在h5中推出了专门的 data- 自定义属性
- 在标签上一律以 data- 开头(必须以 data- 开头)
- 在DOM对象上一律以 dataset 对象方式获取
- dataset 是一个 自定义属性集合,包含 该标签内 的 所有 自定义属性
- 代码展示:
<div data-id="1" data-spm="不知道">1</div> <div data-id="2">2</div> <div data-id="3">3</div> <div data-id="4">4</div> <div data-id="5">5</div> <script>// 获取元素const one = document.querySelector('div')// one是一个HtmlElement对象console.log(one)// dataset是一个对象集合console.log(one.dataset)// 获取dataset对象里面的id属性console.log(one.dataset.id)// 获取dataset对象里面的spm属性console.log(one.dataset.spm) </script>
- ❌⛔ 拓展:
- 获取自己 瞎定义的属性
- 浏览器不会识别 瞎定义属性,并且它不会体现在DOM对象上
- 语法:
DOM对象.getAttribute(瞎定义属性名)
1.5 ❗❗ 定时器 - 间歇函数
- 定时器 = setInterval() + setTimeout()
- setInterval(函数, 间隔时间)(间歇函数):按照指定的周期(以毫秒计)来调用函数或计算表达式。方法会不停地调用函数,直到clearInterval()被调用或窗口关闭
- 间歇函数可以根据时间自动重复执行某些代码
- setTimeout(函数, 延迟时间)(延时函数):在指定的毫秒数后调用函数或计算表达式 (Timeout - 超出)
- 定时器 里面的 第一个参数 是 回调函数
- 将一个函数作为参数传递给另外一个函数,这个函数就是回调函数
1.5.1 开启定时器
- 语法:
匿名函数:setInterval(函数, 间隔时间) // Interval - 间隔,间隙 具名函数:setInterval(函数名, 间隔时间)
- 作用:每隔一段时间 回头调用函数(先隔段时间 ,再调用函数)
- 间隔时间 单位 是 毫秒(ms),不用写
- 不是立即调用函数,时间间隔 过后再去 调用函数
- 间歇函数返回值
- 表示 当前定时器 是 页面中 的 第几个 定时器,是一个 id数字(数字型)(唯一的)
- 一个 间歇函数 只有 一个 返回值,并且是唯一
- 代码展示:
// 匿名函数 setInterval(() => {console.log('扶我起来,我还能干!!!') }, 1000)// 具名函数 function fn() {console.log('扶我起来,我还能干!!!') } setInterval(fn, 1000) // fn() --- 执行这个函数,只执行一次 (fn()调用这个函数) // fn --- 每隔一段时间,回头去找fn这个函数(自动调用),再执行(fn是一个变量,代表这个函数)// 定时器的返回值,是数字型,是id // 声明一个变量,接收定时器的返回值 let n = setInterval(() => {console.log(1) }, 1000) console.log(n) // 1function fn() {console.log('扶我起来,我还能干!!!') } let m = setInterval(fn, 1000) console.log(m) // 2
1.5.2 关闭定时器
- 语法:
// 匿名函数 let 变量名 = setInterval(函数, 时间间隔) // 具名函数 let 变量名 = setInterval(函数名, 时间间隔) clearInterval(变量)
- 代码展示:
// 匿名函数 let n = setInterval(() => {console.log(11) }, 1000)// 具名函数 function fn() {console.log('扶我起来,我还能干!!!') } let m = setInterval(fn, 1000)// 关闭定时器 clearInterval(n) clearInterval(m)
二、❗❗ DOM事件基础
2.1 事件监听(绑定)
- 让程序检测是否有事件产生,一旦事件触发,就立即调用一个函数做出响应,也称为 绑定事件 / 注册事件
- 事件监听三要素:
- 事件源
- 事件类型
- 事件调用的函数(事件处理函数)
- 语法:
元素对象.addEventListener('事件类型', 要执行的函数)
- ⚠ 注意:事件类型 要加 引号
- L2 注册的 事件 不会 出现 覆盖
- L0 注册的事件会发生覆盖(后面的覆盖前面的)(同对象、同事件类型)
- 代码展示:
// 获取元素 const btn = document.querySelector('button') // 事件监听-点击事件 btn.addEventListener('click', () => {alert('扶我起来,我还能干!!!') })
2.2 事件类型
2.2.1 鼠标事件
- 1️⃣ click - 鼠标点击
- 2️⃣ mouseenter - 鼠标经过
- 3️⃣ mouseleave - 鼠标离开
- 4️⃣ mousemove - 鼠标移动
- 5️⃣ dblclick - 鼠标左键双击
2.2.2 焦点事件
- 1️⃣ focus - 获得焦点
- 2️⃣ blur - 失去焦点
2.2.3 键盘事件
- 1️⃣ keydown - 键盘按下触发
- 2️⃣ keyup - 键盘抬起触发
- 注意: 用鼠标粘贴内容不会触发
2.2.4 文本事件
- 1️⃣ input - 用户输入事件
- 获取用户 输入文本 :对象.value
- 获取用户 输入文本 的 长度 :对象.value.length
- ⚠ 注意:
- 得到的 是 数字型
- ⚠ 包括 左右两侧 的 空格
- ⚠ 注意:
- 想要获得 有效 的 文本和长度 (不包括首尾的空白符)
- 有效文本: 对象.value.trim()
- 有效长度: 对象.value.trim().length
- trim() 方法:删除 字符串 首尾 空白符(空格、制表符、换行符等其他空白符)
- 不会 改变 原始字符串
- 不适用于null,undefined,Number类型
2.2.5 表单事件
- 1️⃣ submit - 提交事件
- 2️⃣ change - 失去焦点并且表单内容发生改变触发事件
- 3️⃣ 重置表单: form.reset()
2.2.6 光标的坐标
- 1️⃣ clientX 和 clientY
- 光标距相对于 浏览器 可视化窗口 左上角 的位置
- 鼠标在窗口中的位置
- 2️⃣ pageX 和 pageY
- 光标距离 文档流 左上角 的位置
- 鼠标在页面中的坐标
- 计算鼠标在某个盒子中的坐标:
- 鼠标到盒子顶部的距离 = 鼠标到页面顶部的距离 - 盒子顶部到页面顶部的距离
- 鼠标到盒子左侧的距离 = 鼠标到页面左侧的距离 - 盒子左侧到页面左侧的距离
-
- 3️⃣ offsetX 和 offsetY
- 光标距离 DOM元素 左上角 的位置
- 鼠标在标签中的位置
- ⚠🔺 拓展:利用 js 调用 事件
- 语法:元素.事件类型()
2.3 事件对象 - event
- 也是个对象,里面有事件触发时相关的信息
2.3.1 获取事件对象
- ⚠ 使用场景:
- 可以判断用户按下哪个键
- 可以判断鼠标点击了哪个元素,从而做相应的操作
- 语法:
- 在 事件绑定 的 回调函数 的 第一个参数 就是 事件对象
- 一般命名为 event、ev、e
-
element.addEventListener('事件类型', function (e) {})
- 在触发 DOM 上的某个事件时,会产生一个事件对象
- event对象 包含与创建它的特定相关的和方法
2.3.2 事件对象常用属性
- type:获取 当前的 事件类型
- clientX / clientY:获取 光标 相对于 浏览器可视化窗口 左上角 的位置
- offsetX / offsetY:获取 光标 相对于 当前DOM元素 左上角 的位置
- pageX / pageY : 光标距离 文档流 左上角 的位置
- key:
- 用户 按下 键盘键的值
- 现在不提倡使用keyCode
- 代码展示:
元素.addEventListener('click', function (e) {console.log(e.key)console.log(e.type) })
2.4 环境对象
- 函数内部特殊的变量 this,代表当前函数运行时所处的环境
- 每个函0数 里面 都有 this
- 非严格模式
- this指向函数的调用者(普通函数里面 this -> window)
- 事件侦听里面,指向事件源
- 箭头函数没有this(箭头函数不会自己创建this,它只会沿用自己所在这条作用域链的上层作用域的this)
- 定时器里面this指向window
2.5 回调函数
- 官方:如果将函数A作为参数传递给函数B时,我们称函数A为 回调函数
- 把一个 函数 当作 参数 来 传递给 另外一个函数 的时候,这个函数就是 回调函数
三、❗❗ DOM事件进阶
3.1 事件流
3.1.1 事件流与两个阶段说明
- 事件流 = 事件捕获 + 事件目标 + 事件冒泡
- 事件流指的是:事件 完整执行过程 中的 流动路径
- 过程:事件捕获 => 事件目标阶段 => 事件冒泡 整个完整的过程就是事件流
- 两个阶段:
- 捕获阶段:从父到子(从大到小)
- 冒泡阶段:从子到父(从小到大)
- 实际工作中都是使用 事件冒泡 为主
-
3.1.2 ❌ 事件捕获(了解)
- 概念:从DOM的 根元素 开始去执行对应的事件(从外到里)
- 事件捕获需要写对应代码才能看见效果
- 代码:
DOM.addEventListener(事件类型, 事件处理函数[, 是否使用捕获机制 = false])
- addEventListener第三个参数传入 true 代表是捕获阶段触发(很少使用)
- 若传入false代表冒泡阶段触发,默认就是false
- 若是用 L0(on事件类型) 事件监听,则只有冒泡阶段,没有捕获
- 代码:
3.1.3 事件冒泡
- 从子到父(从小到大)
- 概念:当一个元素触发事件后,会 依次 向上 调用 所有父级元素 的 同名事件(同种事件类型)
- 事件冒泡是默认存在的
- 事件冒泡的必要性
- 如果没有冒泡,给大盒子注册点击事件,点击的是里面的小盒子,会导致大盒子的点击无法执行
- 事件委托(委托给祖先元素)
- L2事件监听第三个参数是false,或者默认都是冒泡
- 代码展示:
const fa = document.querySelector('.father') const son = document.querySelector('.son') document.addEventListener('click', function () {alert('我是爷爷') }) fa.addEventListener('click', function () {alert('我是爸爸') }) son.addEventListener('click', function () {alert('我是儿子') }) // 事件冒泡:从子到父 // 弹出框的顺序依次是:儿子 -> 我是爸爸 -> 我是爷爷
3.1.4 阻止冒泡
- 语法:
事件对象.stopPropagation() // 方法 停止传播 sp(快捷) e.stopPropagation() // 事件对象 - 回调函数的第一个参数
- 此方法可以阻断事件流动传播 ,不光在冒泡阶段有效,捕获阶段也有效
- 代码展示:
const fa = document.querySelector('.father') const son = document.querySelector('.son') document.addEventListener('click', function () {alert('我是爷爷') }) fa.addEventListener('click', function () {alert('我是爸爸') }) son.addEventListener('click', function (e) {alert('我是儿子')// 阻止流动传播(阻止事件冒泡)// 在这一块卡住,不允许向上传播e.stopPropagation() }) // 弹出框:我是儿子
- 阻止元素默认行为:
- 语法:
e.preventDefault() // 方法 阻止默认 pd(快捷) // 阻止 链接跳转 // 阻止 表单提交(缺点:提交数据页面h)
- 代码展示:
<body><form action="http://www.itcast.cn"><input type="submit" value="免费注册"></form><a href="http://www.baidu.com">百度一下</a><script>// 获取元素const a = document.querySelector('a')const form = document.querySelector('form')// 事件侦听-a-点击事件a.addEventListener('click', function (e) {// 阻止a的默认行为-页面跳转e.preventDefault()})// 事件侦听-form-提交事件form.addEventListener('submit', function (e) {// 阻止form的默认行为-表单的提交e.preventDefault()})</script> </body>
- 语法:
3.1.5 两种鼠标事件的区别
- ⚠ 注意:
- mouseover 和 mouseout 会 有 冒泡效果
- mouseenter 和 mouseleave 没有 冒泡效果(推荐)
3.1.6 解绑事件
- 1️⃣ L0事件解绑(on方式)
- 直接使用 null 覆盖就可以实现事件的解绑
- 代码展示:
btn.onclick = function () {alert('点击了') } // L0事件移除解绑 btn.onclick = null
- 2️⃣ L2 事件解绑
-
语法:
removeEventListener(事件类型, 函数名[, 获取捕获或冒泡阶段])
- ⚠ 中括号包裹的参数可以省略
- ⚠🔺 匿名函数 无法进行 事件解绑 操作
- ⚠ 解绑的时候必须是 同一个函数(两个函数体一样 的 匿名函数 不是 同一个函数)
-
代码展示:
<script>// 获取元素const btn = document.querySelector('button')// 声明函数let fn = function (e) {alert(`事件解绑语法:元素.removeEventListener(事件类型, 函数名)匿名函数 无法进行 事件解绑 操作${e.target.tagName}`)}// 事件侦听-点击事件btn.addEventListener('click', fn)// 事件解绑btn.removeEventListener('click', fn) </script>
-
3.1.7 两种注册事件的区别
- 1️⃣ 传统on 注册(L0)
- 同一个对象,后面注册的事件会 覆盖 前面注册的事件(同一个事件)
- 直接使用 null 覆盖就可以实现事件的解绑
- 都是 冒泡阶段 执行的(只有冒泡没有捕获)
- 2️⃣ 事件监听 注册(L2)
- 语法:addEventListener(事件类型, 事件处理函数[ , 是否使用捕获 = false])
- 既能捕获也能冒泡
- 捕获 将第三个参数 改成 true
- 冒泡不用写,默认就是冒泡(false)
- 后面注册的事件 不会覆盖 前面注册的事件(同一个事件)
- 可以通过 第三个参数 去确定是在 冒泡阶段 或者 捕获阶段 执行的
- 必须使用 removeEventListener(事件类型, 事件处理函数[ , 获取捕获或者冒泡阶段])
- ⚠🔺 匿名函数无法解绑
- 语法:addEventListener(事件类型, 事件处理函数[ , 是否使用捕获 = false])
3.2 事件委托
- 优点:
- 减少注册次数
- 提高程序性能
- ⚠ 为未来元素预备事件(添加节点)
- 原理: 事件委托其实是利用 事件冒泡 的特点
- 给 父元素 注册事件,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件
- ⚠ 注意: 事件触发元素嵌套关系很复杂就不可以使用事件委托
- 实现:
- 获得 真正 触发事件 的 元素(对象):事件对象.target
- 获得 真正 触发事件元素 的 标签名称:事件对象.target.tagName
- 代码展示:
<body><ul><li>第1个孩子</li><li>第2个孩子</li><li>第3个孩子</li><li>第4个孩子</li><li>第5个孩子</li><p>我不需要变色</p></ul><script>// 1.点击每个小li,当前li文字变色// 按照事件委托的方式,委托给父级,事件写到父级身上// 获取元素const ul = document.querySelector('ul')// 事件绑定ul.addEventListener('click', function (e) {// e:事件对象// 得到一个事件对象// console.log(e)// 得到被点击的元素// console.log(e.target)// 得到被点击元素的标签名称// 得到的标签名称是 大写的 字符串// console.log(e.target.tagName) // 'LI'// 判断点击的是不是li,如果是li文字就变颜色if (e.target.tagName === 'LI') {e.target.style.color = 'red'}})</script> </body>
3.3 其他事件
3.3.1 页面加载事件
- ⚠ JavaScript 写在body上面,必须 使用 load事件 或者 DOMContentLoaded事件
- 1️⃣ load 事件
- 等待页面 所有外部资源(外联CSS、外联JavaScript,图片……) 加载完毕 时,就回去执行回调函数
- 事件名: load(等待)
- 监听 整个页面 所有外部资源 加载完毕
- ⚠ 给 window 添加 load 事件
- 语法:
window.addEventListener('load', function () {// 执行操作 })
- 代码展示:script标签在body标签上方
<script>// 给 window 添加 页面加载事件(load)window.addEventListener('load', function () {// 获取元素const btn = document.querySelector('button')// 事件侦听-点击事件btn.addEventListener('click', function () {alert(`页面加载事件给 window 添加 load 事件window.addEventListener('load', function() {})`)}) }) </script> <body><button>点击</button> </body>
- ⚠ 注意:不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定 load事件
- 2️⃣ DOMContentLoaded 事件
- 当 初始的HTML文档(DOM节点) 被完全加载和解析完成之后,DOMContentLoaded事件被触发,而无需等待外部资源加载
- 事件名: DOMContentLoaded
- 监听页面 DOM节点 加载完毕
- ⚠ 给 document 添加 DOMContentLoaded 事件
- 语法:
document.addEventListener('DOMContentLoaded', function () {// 执行的操作 })
- 代码展示:
document.addEventListener('DOMContentLoaded', function () {// 获取元素const btn = document.querySelector('button')// 事件侦听-点击事件btn.addEventListener('click', function () {alert(`页面加载事件给 document 添加 DOMContentLoaded 事件document.addEventListener('DOMContentLoaded', function() {})`)}) })
3.3.2 元素滚动事件 - scroll
- 滚动条 滚动的时候 触发(⚠ 页面 必须有 滚动条 才能 触发)
- 让 页面 具有 滑动效果 (不是瞬间到指定位置,而是缓慢滑动到指定位置)
-
/* 页面滑动 */ html {/* 让滚动条丝滑的滑动 *//* 滚动-行为:平滑 */scroll-behavior: smooth; }
-
- 让 页面 具有 滑动效果 (不是瞬间到指定位置,而是缓慢滑动到指定位置)
- 让 html标签(页面最大的标签) 滚动
-
document.documentElement.scrollTop // html被卷去的高度 window.pageYoffset // 页面滚出去的高度(页面在竖轴的偏移量) document.documentElement.scrollTop === window.pageYoffset
-
- 应用场景:固定导航栏、返回顶部
- 事件名: scroll
- 监听 整个页面 滚动
- 语法:
window.addEventListener('scroll', function () {// 执行的操作 })
- ⚠ 注意:
- 给 window(常用) 或 document 添加 scroll 事件
- 监听 某个元素的内部 滚动 直接给 某个元素 加即可
- 代码展示:
- 语法:
- 获取位置
- scrollTop(被卷去的头部) 和 scrollLeft(被卷去的左侧) (属性)
- ⚠🔺 读写属性(可以 读取 亦可以 赋值)
- ⚠ 获取到的是 数字,赋值的时候 不带单位
- 检测页面滚动的头部距离
-
document.documentElement.scrollTop = 数字
-
window.pageYoffset - 只读属性
-
- 获取 被卷去 的大小
- 获取 元素内容 往左、往上滚出去 看不到 的 距离
- ⚠ 得到的是:数字型 不带单位
- 尽量写在 scroll事件 里面
- 代码展示:
<style>* {margin: 0;padding: 0;box-sizing: border-box;}body {height: 3000px;}div {display: none;margin: 100px auto;width: 470px;height: 150px;border: 1px solid #000;text-align: center;font-size: 20px;font-family: '隶书';}.active {display: block;position: fixed;top: 0;left: 50%;transform: translateX(-50%);margin-top: 0;} </style><body><div>世间的悲喜皆不如我所愿,但苦于乐皆是恩赐!!!世间的悲喜皆不如我所愿,但苦于乐皆是恩赐!!!世间的悲喜皆不如我所愿,但苦于乐皆是恩赐!!!世间的悲喜皆不如我所愿,但苦于乐皆是恩赐!!!世间的悲喜皆不如我所愿,但苦于乐皆是恩赐!!!世间的悲喜皆不如我所愿,但苦于乐皆是恩赐!!!</div><script>// 事件侦听-页面滚动事件-windowwindow.addEventListener('scroll', function () {// 获取html元素const html = document.documentElement// 获取div元素const div = document.querySelector('div')// console.log(html.scrollTop); // 得到的是数字型// scrollTop >= 300显示div并固定在可视区域顶部,小于300隐藏if (html.scrollTop >= 300) {div.classList.add('active')} else {div.classList.remove('active')}})</script> </body>
- 让页面 滚动到指定位置
- 1️⃣ 属性赋值
document.documentElement.scrollTop = 指定位置距离顶部的距离
- 2️⃣ 方法
window.scrollTo(x, y)
- 1️⃣ 属性赋值
- scrollTop(被卷去的头部) 和 scrollLeft(被卷去的左侧) (属性)
3.3.3 ❌ 页面尺寸事件 - resize(了解)
- 窗口尺寸改变 的时候 触发事件
- 事件类型: resize
- 语法:
window.addEventListener('resize', function () {// 执行的代码 })
- 检测屏幕宽度
- 语法:
window.addEventListener('resize', function () {let w = document.documentElement.clientWidthconsole.log(w) })
- 语法:
- 获取元素宽高 (属性)
- 获取元素的 可见部分 宽高(不包含border、margin、滚动条)
- clientWidth 和 clientHeight
- 得到的是 数字型
-
3.4 ❗❗ 元素的尺寸和位置
- 1️⃣ 获取 宽高:(属性)
- 获取元素 自身的宽高
- 内容 + padding + border
- offsetWidth 和 offsetHeight
- 获取到的是 数值
- ⚠ 获取的是 可视宽高,如果盒子是隐藏的,获取的结果是0
- 获取元素 自身的宽高
- 2️⃣ 🔻 获取 位置:
- ① 获取元素 距离自己 最近一级 定位 祖先元素 的 左、上 距离
- 带有 定位属性 的 祖先元素
- 如果都没有,就以文档左上角为准
- offsetLeft 和 offsetTop
- 相对于 页面 来说
- ⚠ 只读属性
- 获取的是可视宽高,若盒子隐藏,则获得的是0
- ② ❌ 元素.getBoundingClientRect() (了解)
- 返回元素的 大小 及其 相对于视口的位置
- 相对于 视口
- 代码展示:
const div = document.querySelector('div') div.getBoundingClientRect()
- ① 获取元素 距离自己 最近一级 定位 祖先元素 的 左、上 距离
✅ 总结
属性 | 作用 | 说明 |
---|---|---|
scrollLeft 和 scrollTop | 被 卷去 的 头部 和 左侧 | 配合 页面滚动(scroll 事件) 来用,可读写 |
clientWidth 和 clientHeight | 获得 元素 的 宽度 和 高度 | 不包含border、margin、滚动条,用于js 获取元素大小 只读属性 |
offsetWidth 和 offsetHeight | 获得 元素 的 宽度 和 高度 | 包含border、padding、滚动条等,只读属性 |
offsetLeft 和 offsetTop | 获取元素距离 自己定位父元素 的左、上距离 | 获取 元素位置 的时候使用,只读属性 |
- scroll系列 、 client系列 、 offset系列
- scroll系列: 标签内部 内容 的大小,位置
- scrollTop / scrollLeft:表示标签真实内容相对于标签的位置
- ❌scrollWidth / scrollHeight:表示标签真实内容的大小(和标签本身的大小无关,是内容的宽高)
- client系列: 标签 容纳范围(border里面) 的大小、位置
- clientWidth / clientHeight:表示标签内容区域的大小
- ❌clientTop / clientLeft:表示标签内容区域的位置(几乎不用,仅仅表示上边框的高度,左边框的宽度)
- ✔🔻 offset系列: 标签本身 的大小、位置
- offsetWidth / offsetHeight:包含content + padding + border
- offsetTop / offsetLeft:最近一级 的 带有定位属性 的 祖先元素 的 相对位置(如果没有定位,那么就是相对于body)
- scroll系列: 标签内部 内容 的大小,位置
- 拓展:
- 添加css让页面滚动平滑
-
html {scroll-behavior: smooth; }
-
- if单分支语句拓展:
-
if (old) old.classList.remove('active')
-
old && old.classList.remove('active') - 逻辑与
-
old?.classList.remove('active') - 可选链
-
- 属性选择器
-
document.querySelector('[data-name=new]') 自定义属性 的 属性值 可以 省略 双引号(不推荐)
-
- 添加css让页面滚动平滑
四、❗❗ 日期对象
- 用来 表示时间 的 对象
- 作用: 得到 当前系统 的 时间
- 日期对象:Date构造函数的实例,记录了一个时间信息
4.1 实例化
- 实例化:有 new 关键字的都是实例化(见 JS 高级)
4.1.1 得到当前时间
- 创建一个 时间对象 并 获取时间
- 语法:
const 常量名 = new Date()
- 代码展示:
const date = new Date() console.log(date) // Wed Aug 03 2022 20:37:52 GMT+0800 (中国标准时间) // 周三 八月 3号 年份 时间
- 语法:
4.1.2 指定时间
- 倒计时 的时候 使用
- 语法:
const 常量名 = new Date('指定的时间')
- ⚠ 参数:
- 数字 : 月份是从 0 开始的
- 字符串 : 月份是正常的
- 代码展示:
const date = new Date('2022-8-3 20:50:00') let h = date.getHours() console.log(h) // number console.log(date) console.log(typeof date) // object // Wed Aug 03 2022 20:50:00 GMT+0800 (中国标准时间)
4.2 日期对象方法
-
语法 作用 说明 对象.getFullYear() 获得 年份 获取 四位 年份 对象.getMonth() 获得 月份 取值为 0 ~ 11 对象.getDate() 获取 月份中的 某一天 不同月份取值不同 对象.getDay() 获取 星期 取值为 0 ~ 6 对象.getHours() 获取 小时 取值为 0 ~ 23 对象.getMinutes() 获取 分钟 取值为 0 ~ 56 对象.getSeconds() 获取 秒 取值为 0 ~ 59 对象.getMilliseconds() 获取 毫秒 取值为 0~1000 - ⚠ 注意:
- 周日是一周的开始 -> 周日 = 0
- 当前月份 = 获取的月份 + 1
- ⚠ 得到的都是 数字型
4.3 时间的另一种写法
- 时间对象.toLocaleString() :年月日时分秒
- 时间对象.toLocaleDateString() :年月日
- 时间对象.toLocaleTimeString() :时分秒
- 代码展示:
// 获得当前系统时间 const systemTime = new Date() // 从 获取的当前系统时间(systemTime)里找 // 获取年份 const year = systemTime.getFullYear() // 获得月份 const month = systemTime.getMonth() + 1 // 获取月份中的某一天 const date = systemTime.getDate() // 获取星期 const day = systemTime.getDay() // 获取小时 const hours = systemTime.getHours() // 获取分钟 const minutes = systemTime.getMinutes() // 获取秒 const seconds = systemTime.getSeconds() console.log(systemTime); console.log(`${year} 年 ${month} 月 ${date} 日 周${day} ${hours} 时 ${minutes} 分 ${seconds} 秒`) // Wed Aug 03 2022 21:14:56 GMT+0800 (中国标准时间) // 2022 年 8 月 3 日 周3 21 时 14 分 56 秒
- 格式化时间:
<!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>* {margin: 0;padding: 0;box-sizing: border-box;}div {margin: 100px auto;width: 600px;height: 100px;border: 5px solid #969696;background-color: #d7d7d7;color: hsl(338, 100%, 50%);font-size: 44px;font-weight: 700;font-family: '华文隶书';text-align: center;line-height: 100px;} </style> </head><body><div></div><script>// 写法一: const div = document.querySelector('div')function getTime() {const date = new Date()let hours = date.getHours() < 10 ? '0' + date.getHours() : date.getHours()let minutes = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes()let seconds = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds()return `当前时间:${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${hours}:${minutes}:${seconds}`}div.innerHTML = getTime()setInterval(function () {div.innerHTML = getTime()}, 1000)// 当前时间:2022-8-3 22:45:50// 写法二:自动补0/*// 定时器外面再写一次的原因:定时器隔1s再去执行,用外面的填补页面空白div.innerHTML = date.toLocaleString()// div.innerHTML = date.toLocaleDateString()// div.innerHTML = date.toLocaleTimeString()setInterval(function () {const date = new Date()div.innerHTML = date.toLocaleString() // 2022/8/3 23:00:00// div.innerHTML = date.toLocaleDateString() // 2022/8/3// div.innerHTML = date.toLocaleTimeString() // 22:48:20}, 1000)*/</script> </body> </html>
4.3 时间戳
- 使用场景:倒计时
- 时间戳: 是指1970年01月01日00时00分00秒起 至现在 的 毫秒数
- 注意:
- 时间戳 是 唯一的
- ⚠ 中国 在 东8区
const date = +new Date('1970-01-01 00:00:00') / 1000 / 60 / 60 console.log(date) // -8
- ⚠🔻 算法:
- 将来的时间戳 - 当前的时间戳 = 剩余时间毫秒数
- 剩余时间毫秒数 转换为 剩余时间的 日时分秒 就是 倒计时
- 展示:
将来时间戳 2000ms - 现在时间戳 1000ms = 1000ms 1000ms 转换时分秒就是 0小时0分1秒
- 三种获取时间戳的方法:
- 1️⃣ 使用 getTime() 方法
- 语法:
const date = new Date() console.log(date.getTime()) // 1659539589145 毫秒数 // new Date().get.Time() 拓展: 时间对象.getTime() 和 时间对象.valueOf() 的到的结果一样 console.log(date.getTime() === date.valueOf()) // true
- ⚠ 注意:
- 必须 先 实例化
- ⚠ 可以返回 指定时间的时间戳
- 语法:
- 2️⃣ ✔简写 +new Date()
- 语法:
console.log(+new Date()) // 1659539732765 毫秒数
- ⚠ 注意:
- + -> 正号 (隐式转换)
- ⚠ 可以返回 指定时间的时间戳
- 代码展示:
conlose.log(+new Date('2022-10-1 07:30:00')) // 1664580600000 -- 指定时间的时间戳
- 代码展示:
- 语法:
- 3️⃣ 使用 Date.now()
- 语法:
console.log(Date.now()) // 1659540040487 毫秒数
- ⚠ 注意:
- 没有 实例化
- ⚠ 只能得到 当前的 时间戳
- 语法:
- 1️⃣ 使用 getTime() 方法
- 时间转换公式
- 通过 时间戳 得到是 毫秒,需要转换为秒在计算
- 转换公式:
- days: d = parseInt(总秒数 / 60 / 60 / 24)
- hours: h = parseInt(总秒数 / 60 / 60 % 24)
- minutes: m = parseInt(总秒数 / 60 % 60)
- seconds: s = parseInt(总秒数 % 60)
- ✔ 倒计时函数:
const element = document.qurySelector('css选择器') function getCountTime(time) {// 得到当前的时间戳(毫秒)const now = +new Date()// 得到未来时间时间戳(毫秒)const last = +new Date(time)// 得到剩余的时间戳(毫秒) 并转换为 秒const count = (last - now) / 1000// 将 剩余秒数 -> 天、时、分、秒let day = parseInt(count / 60 / 60 / 24)let hours = parseInt(count / 60 /60 % 24)// 补0hours = hours < 10 ? '0' + hours : hourslet minutes = parseInt(count / 60 % 60)// 补0minutes = minutes < 10 ? '0' + minutes : minuteslet seconds = parseInt(count % 60)// 补0seconds = seconds < 10 ? '0' + seconds : secondsreturn `${day} 天 - ${hours}:${minutes}:${seconds}` }function getRemainingTime(element, time) {// 先执行一次:定时器过1s再去执行,会有1s的显示空白时间,让它限制性,填补空白,再让后面的覆盖element.innerHTML = getCountTime(time)setInterval(function () {element.innerHTML = getCountTime(time)}, 1000) }getRemainingTime(div, '2022-10-1 00:00:00')
五、❗❗ 节点操作
- 浏览器 使用 对象 来 记录 网页内容
- 浏览器把网页内容翻译成一个个的对象,把 网页内容 的 特征 翻译成 对象 的 属性
- 这个对象就称为 DOM对象
- 网页内容的特征:
- 标签属性
- 标签内容
- 标签上下级
- …
- 网页内容 、对象
- 网页内容: 标签、文本、注释
- DOM对象:标签(元素)节点、文本节点、注释节点
- 网页内容 和 DOM对象 一 一 对应
- DOM树
- 每个DOM对象中都有表示层级关系的属性,这些属性把所有DOM对象联系在一起,形成一个树状结构,称为DOM树
- DOM树 === 网页
5.1 DOM节点
- DOM节点: DOM树 里面 每一个内容 都称之为 节点
- 节点类型:
- 1️⃣ ❗✔元素节点:
- 所有的 标签
- html 是 根节点
- 2️⃣ 属性节点:
- 所有的属性
- 3️⃣ 文本节点:
- 所有的文本
- 1️⃣ ❗✔元素节点:
-
5.2 查找节点 - 属性
- 有效返回值 是一个 对象
- 无效返回值 - null
5.2.1 父节点
- 1️⃣ parentNode属性
- 语法:
子元素.parentNode
- 返回值:
- ⚠ DOM对象
- 最近一级 的父节点(亲爸爸),找不到 返回 null
- 代码展示:
<div class="yeye"><div class="dad"><div class="baby">x</div></div> </div><script>const baby = document.querySelector('.baby')console.dir(bady) // div.bady -> DOM对象console.dir(baby.parentNode) // div.dad -> DOM对象console.dir(baby.parentNode.parentNode) // div.yeye -> DOM对象 </script>
- 关闭广告:
<!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 {position: relative;width: 1000px;height: 200px;background-color: pink;margin: 100px auto;text-align: center;font-size: 50px;line-height: 200px;font-weight: 700;}.box1 {position: absolute;right: 20px;top: 10px;width: 20px;height: 20px;background-color: skyblue;text-align: center;line-height: 20px;font-size: 16px;cursor: pointer;}</style> </head><body><div class="box">我是广告<div class="box1">X</div></div><div class="box">我是广告<div class="box1">X</div></div><div class="box">我是广告<div class="box1">X</div></div><script>// const smallBox = document.querySelectorAll('.box1')/* for (let i = 0; i < smallBox.length; i++) {smallBox[i].addEventListener('click', function () {this.parentNode.style.display = 'none'})} */// 事件委托document.body.addEventListener('click', function (e) {if (e.target.className === 'box1') {e.target.parentNode.style.display = 'none'}})</script> </body></html>
- 语法:
5.2.2 子节点
- 1️⃣ ❌childNodes 属性
- 语法:
父元素.childNodes
- 返回值: 获得 所有子节点、包括文本节点(空格、行换)、注释节点等
- 语法:
- 2️⃣ ⚠🔻 children 属性
- 语法:
父元素.children
- ⚠ 返回值: 伪数组 (要想得到里面的每一个节点还是要遍历
- ⚠ 注意:
- 仅获得 所有 元素节点(标签)
- 只选亲儿子,只不过是把亲儿子里面包含的所有节点(元素、文本、注释…)拿过来
- 代码展示:
<ul><li>1<p>6</p><span>7</span></li><li>2</li><li>3</li><li>4</li><li>5</li> </ul><script>// 获取子节点// 语法:父元素.children// 返回值:伪数组// 仅 获得 所有 元素节点(标签)console.log(document.querySelector('ul').children);// 得到的是一个伪数组 </script>
- 语法:

5.2.3 兄弟节点
- 1️⃣ 下一个 兄弟节点
- nextElementSibling 属性
- 语法:
兄弟元素.nextElementSibling
- ⚠ 返回值:
- 有下一个兄弟节点,得到的就是下一个元素
- 没有 就是 null
- 2️⃣ 上一个 兄弟节点
- previousElementSibling 属性
- 语法:
兄弟元素.previousElementSibling
- ⚠ 返回值:
- 有上一个兄弟节点,得到就是上一个元素
- 没有 就是 null
- 代码展示:
<ul><li>1<p>6</p><span>7</span></li><li>2</li><li>3</li><li>4</li><li>5</li> </ul><script>// 上一个兄弟节点// 语法:兄弟元素.previousElementSibling// 下一个兄弟节点// 语法:兄弟元素.nextElementSiblingconsole.log(document.querySelector('ul li:nth-child(2)').nextElementSibling) </script>
5.3 增加节点
- 先 创建 再 追加
5.3.1 创建节点
- 语法:
document.createElement('标签名') // 文档.创建元素
- 创建的节点是一个正常的DOM对象
- ⚠ 注意:
document.createElement() -- 创建的是空标签
5.3.2 追加节点
- 1️⃣ 插入到 父元素 的 最后一个 子元素
- 语法:
父元素.appendChild(要插入的元素) // 追加孩子
- ⚠ 注意: 做为 父元素 里面 的 最后一个 子元素
- 代码展示:⬇
- 语法:
- 2️⃣ 插入到 父元素 中 某个子元素 的 前面
- 语法:
父元素.insertBefore(要插入的元素, 在哪个元素前面) // 插入在…之前
- ⚠ 注意: 插入到 父元素 里面 的 指定位置
- 代码展示:⬇
- 语法:
- ⚠ 注意: appendChild 和 insertBefore 如果插入的是页面上 已经存在 的标签,会有 剪切 效果(将已经存在的元素 从 原位置 剪切到 新位置)
- 代码展示:
<style>div,li {font-size: 20px;text-align: center;line-height: 50px;}li:first-child {width: 150px;height: 50px;background-color: #b8f0e8;}li:last-child {width: 150px;height: 50px;background-color: #a6a6db;}div {width: 150px;height: 50px;background-color: #c0bfbf;} </style><body><ul><li>我是老大</li></ul><script>// 增加节点(节点 -> 元素节点)// 1.创建节点// 语法:document.createElement('标签名称')// 2.插入节点// (1).作为父元素里面的最后一个子元素// 语法:父元素.appendChild(要插入的元素)// (2).插入到父元素里面指定位置(插入到父元素里面某个子元素的前面)// 语法:父元素.insertBefore(要插入的元素, 在那个子元素前面)// 1.创建节点const li = document.createElement('li')li.innerHTML = '我是老二'// 2.插入节点// 2.1获取父元素const ul = document.querySelector('ul')// 2.2作为父元素里面的最后一个子元素ul.appendChild(li)// 1.创建节点const div = document.createElement('div')div.innerHTML = '我是老大的大哥'// 2.插入节点// 2.1获取父元素const body = document.body// 2.2插入到父元素指定位置(插入到父元素里面指定元素前面)body.insertBefore(div, ul)// 将创建的节点始终插入到父元素的最前面/*body.children // 包含body下所有的(亲)子节点伪数组body.insertBefore(div, body.children[0])*/</script> </body>
5.3.3 克隆节点
- 特殊情况 的 新增节点
- 应用场景:轮播图
- 步骤:
- 1️⃣ 复制 一个 原有 的 节点
- 2️⃣ 把 复制的节点 放入到 指定 的 元素内部
- 语法:
元素.cloneNode(布尔值)
- clondNode会克隆出一个跟原标签一样的标签(原标签上面有什么,克隆后的标签上面就有什么)(是两个元素,克隆成功后前后两个元素没有任何关系)
- 参数:
- true - 深克隆: 克隆时 包含 后代节点 一起克隆 (节点 -> 文本节点、注释节点等所有的节点)
- false - 浅克隆: 克隆时 不包含 后代节点、
- 默认值 为 false
- 代码展示:
<ul><li>1</li><li>2</li><li>3</li> </ul><script>const ul = document.querySelector('ul')// const newLi = ul.children[0].cloneNode(true)// newLi.innerHTML = 'newLi'// ul.appendChild(newLi)ul.insertBefore(ul.children[0].cloneNode(true), ul.children[11]) </script>
5.4 删除节点
- 在 JavaScript 原生DOM操作中,要 删除元素 ⚠ 必须 通过 父元素 删除
- 语法:
父元素.removeChild(要删除的元素)
- ⚠ 注意:
- 如果 不存在 父子关系 则 删除 不成功
- 删除节点 和 隐藏节点(display: none;)有区别
- 删除节点:从DOM中删除节点
- 隐藏节点:只是在页面上不显示,DOM节点依然存在
- ✔ 补充:
- 语法: 自杀式z
删除的元素.remove()
- 语法: 自杀式z
- 拓展:
- 替换节点:
- 语法:
parentNode.replaceChild(newChild, oldChild);
- 方法用指定的节点替换当前节点的一个子节点,并返回被替换掉的节点
- 重绘 和 回流
- 重绘: 由于节点(元素)样式的改变并不会影响它在文档流中的位置和文档布局时,则是重绘
- 回流: 元素的尺寸、结构、布局等发生变化时,浏览器就会重新渲染部分或全部文档的过程称为回流
- 重绘不一定一起回流,但回流一定会引起重绘
- 简单理解,影响到了布局,就会有回流
六、❌ M端事件
- 常见的触屏事件(touch)
-
触屏 touch 事件 说明 touchstart 手指 触摸 到一个DOM元素时触发 touchmove 手指在一个DOM元素上 滑动 时触发 touchend 手指从一个DOM元素上 移开 时触发
七、❗ JS插件
- 熟悉官网,了解这个插件可以完成什么需求 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.cn/api/index.html
- 注意: 多个swiper同时使用的时候, 类名需要注意区分
- 使用步骤:
- 引入资源
- css文件
- js文件
- 导入代码
- 复制html结构
- 复制css样式
- 复制js行为
- 引入资源
八、❗❗ window对象
window(JS中的顶级对象) > BOM(浏览器对象模型) > DOM(页面文档对象模型)
8.1 BOM(浏览器对象模型)
- BOM(Browser Object Model)- 浏览器对象模型
- 由一些列相关的对象构成,并且每个对象都提供了很多方法和属性
- 浏览器的内容被翻译成了对象
-
- window 对象是一个 全局对象,也可以说是 JavaScript 中的 顶级对象
- 像document、alert()、console.log()这些都是window的属性,基本BOM的属性和方法都是window的
- ⚠ 所有通过 var 定义在 全局作用域 中的 变量、函数 都会变成 window对象 的 属性 和 方法
- ⚠ window对象下的属性和方法调用的时候可以 省略window
- DOM 和 BOM 区别:
-
8.2 定时器 - 延时函数
- 定时器 = 间歇函数(setInterval()) + 延时函数(setTimeout())
- 让代码 延迟 执行的函数 - setTimeout()
- 语法:
setTimeout(回调函数, 等待的毫秒数)
- 清除延时函数:
let timerId = serTimeout(回调函数, 等待的毫秒数) clearTimeout(timerId)
- ⚠ 注意:
- setTimeout 仅仅 只执行 一次 ,平时省略 window
- 延时函数需要等待,所以后面的代码先执行
- 每一次调用延时器都会 产生一个 新的 延时器
- 间歇函数 和 延时函数 的区别:
- 间歇函数:每隔一段时间就执行一次,除非手动清除
- 延时函数:执行一次
8.3 JS执行机制
- JS代码 从上到下 执行
- 单线程: 同一时间只能做一件事
- 所有的任务需要排队,前一个任务结束,才会执行后一个任务
8.3.1 同步 与 异步
- 1️⃣ 同步
- 前一个任务结束后再执行后一个任务,程序的执行顺序 和 任务的排列顺序 是 一致的
- 同步任务: 都在 主线程 执行栈 上执行
- 2️⃣ 异步
- 如果执行一个任务需要花费一定的时间,在执行这个任务的同时可以去执行别的任务
- 耗时 的都是 异步
- JS的 异步 是通过 回调函数 实现的
- 异步任务的三种类型
- 普通事件(click、mouseenter、mouseleave、……)
- 资源加载
- 定时器 (间歇函数(setInterval) + 延时函数(setTimeout))
- 异步任务 添加到 任务队列 (消息队列)中
- 同步 和 异步 的 本质 区别:
- 一条流水线上的 各个流程 执行顺序不同
- 3️⃣ 执行机制:
- ① 先执行 任务栈 中的 同步任务
- ② 异步任务 添加到 任务队列 里面
- 对 异步任务 进行 排序(根据每个异步任务所消耗的时间 )
- ③ 一旦 执行栈 里面的 所有 同步任务 执行完毕,系统 就会 按次序 读取 任务队列 中的 异步任务,于是 被读取 的 异步任务 结束 等待状态,进入 执行栈,开始执行。
-
8.3.2 事件循环 - event loop
-
- 由于 主线程 不断的 重复 获得任务、执行任务、再获取任务、再执行,所以这种机制被称为 事件循环(enevt loop)
- 事件循环: js执行代码时,会把同步任务添加到主线程执行栈中执行,把满足条件的异步任务推到任务队列排队等候执行,事件循环是一种轮询机制,当执行栈中的所有同步任务执行完毕之后,系统就会依次读取任务队列中的异步任务,异步任务结束等待状态,按照次序添加到执行栈中执行。
8.4 location 对象
- location 的 数据类型 是 对象,它 拆分 并 保存 了 URL地址 的 各个 组成 部分
- 常用 属性 和 方法:
- 1️⃣ ✔🔺 href: 属性 获取 完整的URL地址,对其 赋值时 用于 地址的跳转
-
const btn = document.querySelector('button'); btn.addEventListener('click', function () {location.href = 'https://www.baidu.com'; });
- 可以后退
-
- 2️⃣ search: 属性 获取地址中 携带的参数,符号 ? 后面部分
-
console.log(location.search); // 字符串
-
- 3️⃣ hash: 属性 获取地址中的 哈希值,符号 # 后面部分
-
console.log(location.hash); // 字符串
-
- 4️⃣ reload(是否使用缓存): 方法 用来 刷新 当前 新页面,传入参数 true 时表示 强制刷新,默认false使用缓存刷新
-
const btn = document.querySelector('.reload'); btn.addEventListener('click', function () {// F5 -> 刷新//location.reload();// F5+Ctrl -> 强制刷新 location.reload(true); });
-
- 5️⃣ replace(): 方法 页面跳转(覆盖旧地址)
-
const btn = document.querySelector('button'); btn.addEventListener('click', function () {location.replace('https://www.baidu.com'); });
-
- ⚠ href属性 和 replace()方法 实现页面跳转的区别:
- href属性: 保留 原地址,具有回退功能
- replace()方法: 覆盖 原地址,没有回退功能
- 1️⃣ ✔🔺 href: 属性 获取 完整的URL地址,对其 赋值时 用于 地址的跳转
8.5 navigator 对象
- navigator 的 数据类型 是 对象,该对象记录了 浏览器 自身的 相关信息
- 常用 属性 和 方法:
- 浏览器相关信息:navigator.userAgent
- 通过 userAgent 检测 浏览器 的 版本 及 平台
// 检测userAgent(浏览器信息) !(function () {const userAgent = navigator.userAgent;// 验证是否为Android或iPhoneconst 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';} })();
8.6 history 对象
- histroy 的 数据类型 是 对象,主要管理历史记录,该对象与浏览器地址栏的操作相对应。
- 常用属性和方法:
- back(): 可以 后退 功能
- forward(): 前进 功能
- go(参数): 前进 后退 功能
- 参数:
- 1: 前进 一个页面 (back())
- 0: 刷新页面(使用缓存刷新) (reload())
- -1: 后退 一个页面 (forward())
- 参数:
- 代码展示:
<button>后退</button> <button>前进</button> <script>// 获取元素const back = document.querySelector('button');const forward = back.nextElementSibling;// 事件侦听-点击事件back.addEventListener('click', function () {// 前进一步history.go(1);});forward.addEventListener('click', function () {// 后退一步history.go(-1);}); </script>
九、❗❗ 本地存储
- 数据存储 在 用户 浏览器 中(硬盘)
- 设置、读取方便、甚至 页面刷新 不会丢失 数据
- 容量较大、sessionStprange 和 localStorage 约5M
- 为什么要将数据存储到本地存储里?
- 数据持久化(数据不丢失)
9.1 本地存储分类
9.1.1 localStorage
- 作用: 可以 将 数据 永久存储 在 本地(用户电脑),除非手动清除,否则关闭页面 也会 存在
- 生命周期: 什么时候失效,什么时候死掉
- 特性:
- 可以多窗口(页面)共享(同一浏览器可以共享)(不能跨域(同一个域名)使用)
- 以 键值对 的形式 存储使用
- ⚠🔻 语法:
- 1️⃣ 存储/修改 数据:
localStorage.setItem(key, value) // 设置每个小项,key和value要加引号,key和value可以是变量
- 有 这个键就是 修改,没有 就是 添加
- 代码展示:
// 本地存储 - localStorage // 存储数据 - localStorage.setItem(key, value); // 要加引号 localStorage.setItem('uname', '邵秋华');
- 2️⃣ 获取 数据:
localStorage.getItem(key) // 获取里面的项,key必须加引号
- 返回值:
- 有这个key,就返回对应的值
- 没有这个key,返回 null
- 代码展示:
// 获取数据 - localStorage.getItem(key) // 要加引号 console.log(localStorage.getItem('uname')); // 邵秋华
- 返回值:
- 3️⃣ 删除 某项数据:
localStorage.removeItem(key) // 移除里面的项, key必须加引号
- 代码展示:
// 删除本地存储 localStorage.removeItem('uname');
- 代码展示:
- 4️⃣ 清空 数据:
localStorage.clear()
- 慎用
- 清空全部数据
- 1️⃣ 存储/修改 数据:
- ⚠⚠ 注意:
- ⚠ 所有的 键 必须加 引号
- 有这个键就是修改,没有就是添加
- ⚠🔺 本地存储 只能存储 字符串 类型的数据
- 得到的值也是字符串型
- 如果value写的是数值型,存的时候会转换成字符型
- 代码展示:
const arr = [1, 2, 3, 4]; arr.forEach((item, index) => {localStorage.setItem(`id:${index}`, item);console.log(localStorage.getItem(`id:${index}`)); // 1 2 3 4(字符串)console.log(typeof (localStorage.getItem(`id:${index}`)));// string });
9.1.2 sessionStprange
- 特性:
- 生命周期 为 关闭浏览器窗口
- 在同一个窗口(页面)下 数据可以共享
- 以 键值对 的形式 使用
- 用法跟localStorage基本相同
- ⚠ localStorage 与 sessionStorage 的区别
- 他们的 作用 相同,但是 存储方式 不同,因此应用场景也不同
- localStorage 的数据可以 长期存储,关闭浏览器 数据 也 不会消失,除非 手动清除
- sessionStorage 的数据时是 临时存储,页面被关闭,存储在sessionStorage里的 数据 会被 清除
9.2 存储复杂数据类型
- 本地存储 只能存储 字符串,不能存储 复杂数据类型
- JSON:
- 属性和值都有引号,而且是双引号
- 一种字符串格式的名称
- 把 数组/对象 转换为结构为 ”数组/对象“ 的字符串
- 只能存:数字、字符串、对项
- 不能存null、undefined、函数
- ⚠ JSON.parse(转换数据):要转换的数据不是JSON格式的字符串
- 存储复杂数据类型步骤:
- 1️⃣ 将 复杂数据类型 ➡ JSON字符串
- 语法:
JSON.stringify(复杂数据类型)
- 代码展示:
// 本地存储只能存储字符串,不能存储复杂数据类型 // 如果要存储复杂数据类型,需要将复杂数据类型转换成 JSON 字符串 // 语法:JSON.stringify(复杂数据类型); let obj = {uname: '邵秋华',age: 22,gender: '男', };localStorage.setItem('obj', JSON.stringify(obj));
- 语法:
- 2️⃣ 将 JSON字符串 ➡ 复杂数据类型
- 语法:
JSON.parse(JSON字符串)
- 代码展示:
console.log(JSON.parse(localStorage.getItem('obj')));
- 语法:
- 1️⃣ 将 复杂数据类型 ➡ JSON字符串
- 存:
- 复杂数据类型 -> JSON.stringify() -> JSON字符串 -> 存到本地存储中
- 取:
- 从本地存储中取 -> JSON字符串 -> JSON.parse() -> 复杂数据类型
十、❗ 正则表达式
- 正则表达式(Reular Expression):用于 匹配 🔺字符串🔺 中 字符组合 的 模式。
- 在 JavaScript 中,正则表达式 也是 对象(object )
- ⚠🔺 注意: 只能对 字符串 进行匹配
- 正则表达式的作用:
- 1️⃣ 表单验证(匹配)
- 2️⃣ 过滤敏感词汇(替换)
- 3️⃣ 字符串中提取我们想要的部分(提取 ->-爬虫)
10.1 正则表达式的使用
- 1️⃣ 定义规则:
- 语法:
const regObj = /表达式/ // 正则表达式不改变使用
const regObj = new RegExp('表达式') // 正则表达式变化使用
- ⚠ 注意:
- // 正则表达式 的 字面量
- ⚠ // 中间写什么,就查找什么
- ⚠🔺 // 中间 不要乱加 空格
- 只要涉及到正则表达式不要乱加空格,最好用vs格式化
- 语法:
- 2️⃣ ✔检测查找是否匹配:
- 语法:
- test() :用来 查看 正则表达式 与 指定的字符串 是否匹配
-
regObj.test(被检测的字符串)
- ⚠ 返回值:
- 匹配 ➡ true
- 不匹配 ➡ false
- 规则在前面,被检测的就是用户输入的内容
- 注意:
- 语法:
- 代码展示:
// 正则表达式 // 正则表达式使用 // 1)定义规则 // 语法:const 变量名(regObj) = /表达式/; // 注意:正则表达式的字面量中间 不要乱加空格 // 字面量中间些什么就匹配什么(不要乱加空格) // 2)是否匹配 // 语法:regObj(字面量).test(被检测的字符串);const reg = /前端/;const str = '我在学习前端,希望学完高薪就业!!!';console.log(reg.test(str)); // true// 注意const reg = / 前端 /;const str = '我在学习前端,希望学完高薪就业!!!';console.log(reg.test(str)); // false// 注意console.log(/哈/.test('哈')); // trueconsole.log(/哈/.test('哈哈')); // trueconsole.log(/哈/.test('二哈')); // true
- 3️⃣ ⭕检索符合规则的字符串:
- 语法:
- exec():在一个 指定字符串 中执行一个 搜索匹配
-
regObj.exec(被检测字符串)
- 返回值:
- 匹配 ➡ 数组
- 不匹配 ➡ null
- 语法:
- 代码展示:
const reg = /前端/; const str = '我在学习前端,希望学完高薪就业!!!'; console.log(reg.exec(str));
- 正则表达式检测查找 test()方法 和 exec()方法 有什么区别?
- text(): 用于判断是否有符合规则的字符串,返回的是一个布尔值,找到 返回 true ,没找到 返回 false;
- exec(): 用于检索(查找)符合规格的字符串,找到 返回 数组,没找到 为 null
10.2 ❗ 元字符
- 字符
- 普通字符
- 大多数的字符仅能够描述它们本身(字母数字)
- 普通字符 只能够 匹配 字符串中 与 它们 相同的字符
- 元字符
- 是一些 具有 特殊含义 的 字符
- 优点: 极大提高了 灵活性 和 强大的匹配功能
- 普通字符
- ⚠🔻 边界符、量词、字符类 组合使用
- 1️⃣ 边界符
- 表示位置,开头和结尾,必须用什么开头,用什么结尾
- 用来提示字符所处的位置
-
边界符 说明 ^ 表示匹配 行首 的文本(以谁开始) $ 表示匹配 行尾 的文本(以谁结束) - 注意:
- ⚠ ^ $ 在一起,表示 必须是 精确匹配
- 代码展示:
console.log(/哈/.test('哈')); // true // 字符串的内容 和 规则相等 console.log(/哈/.test('哈哈')); // true console.log(/哈/.test('二哈')); // trueconsole.log(/^哈/.test('哈')); // true console.log(/^哈/.test('哈哈')); // true console.log(/^哈/.test('二哈')); // flaseconsole.log(/^哈$/.test('哈')); // true console.log(/^哈$/.test('哈哈')); // false -- 精确匹配(字符串的内容、长度必须和规则相等) console.log(/^哈$/.test('二哈')); // flase
-
- 2️⃣ 量词
- 表示重复次数
- 设定 某个模式 的 出现 次数
-
量词 说明 ***** 重复 零次 或 多次 >= 0 + 重复 一次 或 多次 >=1 ? 重复 零次 或 一次 {n} 重复 n次 {n,} 重复 n次 或 更多次 >= n {n,m} 重复 n 到 m 次 n <= 次数 <= m - ⚠🔺注意:
- 🔺n,m🔻 之间 🔺没有空格🔻
- 离量词 最近的(量词的左边) 重复,别的不重复
- ⚠🔺注意:
- 3️⃣ 字符类
- ① [] - 匹配字符集合
- 被检测的数据 只要能和 规则 匹配到 任意🔺一个🔻字符 就是 true,否则就是false
- 代码展示:
后面的字符串只要包含abc中任意一个字符,都返回true console.log(/[abc]/.test('andy')); // true console.log(/[abc]/.test('baby')); // true console.log(/[abc]/.test('cry')); // true console.log(/[abc]/.test('die')); // false
- 精确匹配: 被检测的数据 🔺只能🔻 和 规则 匹配 🔺一个🔻 字符(n选1),要想多个匹配须和量词配合使用
console.log(/^[abc]$/.test('a')); // true console.log(/^[abc]$/.test('b')); // true console.log(/^[abc]$/.test('c')); // true console.log(/^[abc]$/.test('ab')); // false console.log(/^[abc]{1,}$/.test('abc')); // true
- ② - 连字符
- 使用连字符 - 表示一个 范围
- [a-z] : 表示 a~z 26个英文字母都可以
- [a-zA-Z] : 表示大小写都可以
- [0-9] : 表示 0~9 都可以
-
[\u4e00-\u9fa5] 匹配汉字
- 代码展示:
/^[1-9][0-9]{4,}$/ // 第一个数字:1~9;从第二位开始:0~9;可以重复>=4
- ③ ^ - 取反符号
- [] 里加上 ^ 取反符号
- [^a-z] :匹配除了小写字母以外的字符
- ⚠ 注意: 写到 [] 里面
- ④ . 匹配 除了换行符之外的任何单位
- ⑤ 预定义类:某些常见模式的简写方法
-
预定义 说明 \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}$
-
-
- ⑥ () 使用小括号将字符包成一个整体
- ⑦ | 或
- ① [] - 匹配字符集合
- 特殊符号写到 [] 里的最后面
10.3 修饰符
- 修饰符 约束 正则表达式的某细节 行为 ,如是否区分大小写、是否支持多行匹配
- 语法:
/表达式/修饰符
- i: (ignore)正则匹配时 不区分 大小写
- g: (global)匹配 所有 满足正则表达式 的 结果 (全局匹配)
- m: 执行多行匹配
- 代码展示:⬇
- replace - 替换
- 语法:
字符串.replace(/正则表达式/, '替换文本')
- 返回值: 替换好的字符串
- 代码展示:⬇
- 替换多个:
字符串.replace(/正则表达式/g, '替换文本')
- 代码展示:⬇
- ✔ 替换多个 并且 不区分大小写:
字符串.replace(/正则表达式/ig, '替换文本') 字符串.replace(/正则表达式/gi, '替换文本')
- ⚠🔺 g i 不区分 前后
- 代码展示:⬇
- 替换多个 并且 不区分大小写:(用正则表达式里面的 或 )
字符串.replace(/正则表达式|正则表达式/, '替换文本')
- ⚠🔺 或(|) 两侧 不要加 空格
- 如果没有小括号,就是将正则表达式分成多份
- 代码展示:⬇
- 代码展示:
// 修饰符:约束正则表达式的细节行为 // 语法: /正则表达式/修饰符 // i(不区分大小写) + g(匹配所有满足正则表达式的结果) // i console.log(/^java$/.test('JAVA')); // false console.log(/^java$/i.test('JAVA')); // true console.log(/^java$/i.test('JavA')); // true// 替换 - replace // 语法:字符串.replace(/正则表达式/, '替换文本') const str = 'java是一门编程语言,学完JAVA工资很高';// 替换一个 const re1 = str.replace(/java/, '前端'); console.log(re1); // '前端是一门编程语言,学完JAVA工资很高'// 替换多个 - g const re2 = str.replace(/java/ig, '前端'); console.log(re2); // '前端是一门编程语言,学完前端工资很高'// 替换多个 - 或(|) (正则表达式里的或)(|两侧不要加空格) const re3 = str.replace(/java|JAVA/g, '前端'); console.log(re3); // '前端是一门编程语言,学完前端工资很高'
- 语法:
- 拓展:
- change - 事件
- 当鼠标离开表单,并且表单值发生变化触发
- change - 事件