前言:
- 求关注😭
- 本篇文章主要记录以下几部分:
- 基础:
- 输入输出语法;
- 数据类型;
- 运算符;
- 流程控制 - 分支语句;
- 流程控制 - 循环语句;
- 数组 - 基础;
- 函数 - 基础;
- 对象 - 基础;
- Web API:
- DOM;
- DOM事件基础(事件监听、常用事件、事件对象);
- DOM事件进阶(事件流、事件委托);
- 日期对象;
- 节点操作;
- M端事件;
- JS插件;
- window对象;
- 本地存储;
- 正则表达式;
- 基础:
- 以下部分请移步JavaScript - 进阶+高级(笔记):
- 进阶:
- 作用域;
- 函数进阶(函数提升、函数参数、箭头函数);
- 解构赋值;
- 对象进阶(构造函数、实例成员、静态成员);
- 内置构造函数(Object、Array、String、Number);
- 编程思想;
- 构造函数;
- 原型
- 高级:
- 深浅拷贝;
- 异常处理;
- this指向;
- 性能优化(防抖、节流);
- 进阶:
壹、❗❗ JavaScript基础
- HTML 表示网页的 结构
- CSS 表示网页的 样式
- JavaScript 表示网页的 行为
- 浏览器内核 = 渲染引擎 + JS引擎
- JavaScript的组成
- ECMAScript — JS的基础语法
- Web APIs
- DOM — 操作文档流的属性和方法(文档对象模型)
- 是HTML文档的编程接口。它提供了对文档的结构化的表述,并定义了一种方式,可以在程序中对该结构进行访问,从而改变文档的结构、样式和内容。DOM将文档解析为一个由节点和对象(包含属性和方法)组成的结构集合。简言之,它会将 web页面和 程序 连接起来。进而操作网页的内容,实现各种功能和部分特效
- BOM — 操作浏览器的属性和方法(浏览器对象模型)
- DOM — 操作文档流的属性和方法(文档对象模型)
- JS的本质: 通过 JS 的语法,让 浏览器 发生变换,让 文档 发生变化。
一、JavaScript书写位置
- 行内 JavaScript
- 内部 JavaScript
- 外部 JavaScript
二、输入输出语法
- 小括号里面只要 不是纯数字 和 变量 必须要用 引号包裹(单、双、反)
alert() --- 警示框 prompt() --- 浏览器弹出输出框,用户可以输入信息 console.log() --- 控制台输出 document.write() --- 在文档页面输出(识别标签)
- 代码展示:
alert('你好, js'); prompt('JavaScript 基础'); console.log('它~会魔法吧~'); document.write('javascript我来');
三、字面量、变量、常量
3.1 字面量
- 用来在计算机中描述 事 / 物
- [ ] : 数组字面量
- { } : 对象字面量
- 数字字面量:数字
- 字符字面量:字符串
- 布尔字面量:布尔值
3.2 变量
- 变量:用来 存储数据 的 容器
- 变量的本质:内存里的一块空间,用来 存储数据
- 变量 必须 先声明 后使用
- 变量的初始化: 声明变量 并 赋值
- 变量命名规则:变量名只能是 数字、大小写字母、美元符($)、下划线组成(⚠ 不能以数字开头)
- ⚠ 不能使用 name 作为变量名 (name会将 数字型 转为 字符型)
- 声明 变量使用 let
- 代码展示:
// 声明变量 // 声明变量不赋值 let a // 变量的初始化 --- 声明变量并赋值 let age = 18 // 声明多个变量(用逗号隔开)--- 不推荐 let a = 18, b = 'pink' // 更新变量 let age = 18 age = 19 console.log(age) // 19 // 用变量赋值 let myFood = '香蕉' let youFood = myFood console.log(youFood) // 香蕉
3.2.1 var 和 let 的区别
- var
- 可以 先使用 再声明
- var声明过的变量可以重复声明
- 存在变量提升、没有块作用域等等。
- let / const
- 必须 先声明 后使用
- 不能 声明 重复变量
- 不存在变量提升
- 使用了 let / const 声明的变量存在块作用域
- const声明变量的时候必须初始化
3.3 常量
- 声明常量使用 const
- ⚠ 常量不允许重新赋值,声明的时候必须赋值 (必须初始化)
- 注意:
const num = 10; num = 10; console.log(num); // 报错 - 常量不允许重新赋值
- 注意:
四、❗❗ 数据类型
- 1️⃣ 值类型 (基本数据类型)(6)
number
— 数字型string
— 字符串型boolean
— 布尔型undefined
— 未定义型null
— 空类型symbol
(了解)bigint
- 2️⃣ 引用数据类型 (复杂数据类型)(3)
object
— 对象function
– 函数array
— 数组
4.1 数字型 - number
- 数字型 = number + NaN
Number
一切十进制表示的数字
Number.MAX_VALUE // 数字中的最大值
Number.MIN_VALUE // 数字中的最小值
Infinity // 无穷大
-Infinity // 无穷小
NaN // 非数值
isNaN(数据) 这个方法用来判定非数字,并且返回一个值,如果是数字返回 false,如果不是数字返回 true
+ `NaN` 代表一个计算错误,它是一个不正确的或者一个未定义的数学操作所得到的结果 + ⚠ **`NaN` 是 粘性 的。任何对 `NaN` 的操作都会返回 `NaN`**
console.log(2 - NaN) // NaN
console.log('粘性' + NaN) // NaN
console.log(NaN === NaN) // NaN
4.2 字符串型 - string
- 用引号(单引号 / 双引号 / 反引号)包裹的都是字符串
4.2.1 字符串的长度
lengtn 属性
let strMsg = (`扶我起来,我还能干!`)
console.log(strMsg.length) // 显示 10
4.2.2 字符串的拼接
- 多个字符串之间可以使用 + 进行拼接,其拼接方式为 字符串 + 任何类型 = 新的字符串
- 拼接前会把与字符串相加的任何类型转成字符串,再拼接成一个新的字符串
- “+” 数字相加,字符相连
4.2.3 ❗❗ 模板字符串
- 使用 反引号(``) 包裹
- 模板字符串可以 嵌套变量
- 代码展示:
let myName = `邵秋华` let age = 22 let str = `扶我起来,我还能干!` document.write(`我叫${myName},今年${age}岁了,我的座右铭是${str}`)
- 代码展示:
4.2.4 ❌ 转义字符
4.3 布尔型 - boolean
- 布尔类型 有 两个值,true 和 false,其中 true 表示 真,而 false 表示 假
- 布尔型 和 数字型 相加 的时候,
true = 1
,false = 0
- ❗ 除了
0
、''
、undefined
、null
、NaN
、false
转换为布尔值后为 假,其余都为真
4.4 未定义类型 - undefined
- 只声明变量,不赋值 的情况下,变量的默认值为
undefined
undefined
这里本该有一个值,但是没有,就是undefined
- ⚠
数字 + undefined = NaN
- ⚠ 注意:
- ⚠ 对 undefined 的任何操作都是NaN,除了拼接字符串
- 出现情况:
- 声明变量未赋值
- 数组 里面 没有 某个 元素
- 对象 里面 没有 某个 属性 或 方法
- 函数
- 形参多于实参(没有赋值的形参默认值是 undefined)
- 函数未指定返回值(没有 return 关键字)
console.log(undefind + 1) // NaN
condsole.log(undefined + 'purple') // undefinedpurple
console.log(typeof(undefined)) //undefined
4.5 空类型 - Null
- Null 这里有一个值,有一个空值
- ✅参与 数学运算 的时候,
null = 0
console.log(null + 1) // 1
console.log(null + 'purple') // nullpurple
console.log(typeof(null)) // object
4.6 null 和 undefined 区别:
- undefined 表示 声明了一个变量,但是没有赋值
-
let num console.log(undefined + 1) // NaN
-
- null 表示 声明了一个变量 并且 赋值了,但是内容为空
-
let num = null console.log(null + 1) // 1
-
- ✅注意:
- ⚠
null
和undefined
值相等,数据类型不相等
console.log(null == undefined) // true
- ⚠
4.7 检测数据类型
- 1️⃣ 作为运算符:
typeof 数据
- 以 字符串 的形式 给你变量的数据类型
- ⚠ 使用 typeof 检测 错误类型 为 undefined 的代码不会报错,检测结果是 undefined
- 2️⃣ 函数形式:
typeof(数据)
(推荐使用)- 以字符串的形式给你变量的数据类型
- 先运算小括号里面结果,然后去检测结果的数据类型
- 代码展示:
let num = 10; console.log(typeof num); // number let str = 'purple'; console.log(typeof str); //string let flag = true; console.log(typeof flag); //boolean let i = undefined; console.log(typeof i); //undefined let timer = null; console.log(typeof timer); //object // 注意 console.log(typeof (a)); // undefined
- 3️⃣
instanceof
- 用于检测构造函数的
prototype
属性是否出现在某个实例对象的原型链上。 - 返回值:布尔值(true / false)
- 用于检测构造函数的
- 4️⃣ 究极方法:
Object.prototype.toString()
- 可以通过
toString()
来获取每个对象的类型。为了每个对象都能通过Object.prototype.toString()
的检测,需要以Function.prototype.call()
或Function.prototype.apply()
的形式来调用,传递要检查的对象作为第一个参数。 - 返回值:[ object type ]
- 代码展示:
// 引用数据类型 const obj = {uname: '迪迦', age: 22}; console.log(Object.prototype.toString.call(obj)); // [object Object] const arr = [1, 2, 3]; console.log(Object.prototype.toString.call(arr)); // [object Array] const fn = function () { console.log(1) }; console.log(Object.prototype.toString.call(fn)); // [object Function] // 基本数据类型 console.log(Object.prototype.toString.call(123)); // [object Number] console.log(Object.prototype.toString.call('123')); // [object String] console.log(Object.prototype.toString.call(false) ); // [object Boolean] console.log(Object.prototype.toString.call(undefined)); // [object Undefined] console.log(Object.prototype.toString.call(null)); // [object Null]
- 可以通过
4.8 数据类型转换
4.8.1 转数字
- 1️⃣
parseInt(string)
可以把 字符串型 转换为 整数型(不识别小数点)- 从 第一个字符开始 取第一个整数,如果取不到就是NaN
- ⚠ 转换的字符开头如果不是数字,就是NaN
- 代码展示:
console.log(parseInt('3.14')) //3(取整数位) console.log(parseInt('3.99')) //3(取整数位) console.log(parseInt('120px')) //120 (自动去除单位,前面必须是数字) console.log((parseInt'rem120px')) //NaN console.log(parseInt(true)) //NaN console.log(parseInt(null)); // NaN
- 2️⃣
parseFloat(string)
可以把 字符串型 转换为 浮点型(识别小数点)- 从第一个字符开始取第一个小数,如果取不到就是NaN
- ⚠ 转换的字符开头如果不是数字,就是NaN
- 代码展示:
console.log(parseFloat('3.14')); //3.14 console.log(parseFloat('3.99')); //3.99 console.log(parseFloat('120px')); //120 (自动去除单位,前面必须是数字) console.log(parseFloat('rem120px')); //NaN console.log(parseFloat(true)) //NaN console.log(parseFloat(null)); // NaN
- ⚠ 注意:
parseInt 和 parseFloat 单词的大小写
- 3️⃣
Number(string)
强制转换- 转换的数据中不能包含非数字,如果有就是NaN
- 识别小数点
- 代码展示:
let num = '123'; console.log(Number(num)); //123 console.log(Number('12')); //12 console.log(NUmber('123.123'); // 123.123 console.log(Number('123abc'); //NaN console.log(Number(null)); // 0 console.log(Number(true)); // 1 console.log(Number(false)); // 0
- 4️⃣ 利用了算术运算 **+(正号) - * / % ** 隐式转换
- 代码展示:
console.log('12' - 0); //12 (先将 '12' 转换成数字型在进行减) console.log('123' - '120'); //3 console.lof('123' * 1); //123 console.log(+'123') //123 + 号作为正号解析可以转换成数字型(单用)
let year = prompt('请输入您的出生年份:'); //let age = 2022 - year; alert('您今年:' + (2022 - year) + '岁了');
- 注意:
- 隐式转换是我们在进行算数运算的时候,JS 自动转换了数据类型
- 代码展示:
let num1 = prompt('请输入第一个数据:'); //得到的num1是字符型的 let num2 = prompt('请输入第二个数据:'); //得到的num2是字符型的 let result = parseInt(num1) + parseInt(num2); alert(parseInt(num1) + '+' + parseInt(num2) + '=' + result); /* let num1 = +prompt('请输入第一个数据:'); let num2 = +prompt('请输入第二个数据:'); let result = num1 + parseInt(num2); */
- 代码展示:
- ✅ 注意:
- 有字符串的加法 ‘’ + 1,结果是 ‘1’
- 减法 只能用于数字,它会使 空字符串 ‘’ 转换为 0
- null 经过 数字转换 之后会变为 0
-
parseInt(null); // NaN parseFloat(null); // NaN Number(null); // 0
-
- undefined 经过 数字转换 之后会变成 NaN
- undefined 做任何操作都是 NaN 除了拼接字符串
null == undefined // true 值都是空 null === undefined // false 数据类型不一样
4.8.2 转字符串
- 1️⃣ String(数据)
- 返回值:转换好的数据
- 代码展示:
console.log(String(123)) // '123'
- 2️⃣ 数据.toString()
- 返回值:转换好的数据
- ⚠ **注意:**需要 配合 变量 使用、
- 代码展示:
let num = 123 console.log(num.toString()) // '123'
- 3️⃣ +(加号)
- 字符串的拼接
- 代码展示:
console.log(123 + '') // '123'
toString() 和 String() 使用方式不一样。
三种转换方式,我们更喜欢用第三种加号拼接字符串转换方式, 这一种方式也称之为隐式转换。
4.8.3 转布尔
- Boolean(数据)
- 返回值:转换好的数据
- ⚠ 注意:代表 空、**否定的值 **会被 转换成 false
- ‘’、 0、 NaN、 null、 undefined、 false
- 代码展示:
console.log(Boolean('')) // false console.log(Boolean(0)) // false console.log(Boolean(NaN)) // false console.log(Boolean(null)) // false console.log(Boolean(undefined)) // false console.log(Boolean(false)) // false 注意: // 两个等号值比较 值 // null 和 undefined 都是空 console.log(null == undefined) // true
五、运算符
5.1 算术运算符
+ ⚠ **注意:** + 取余前面的数比后面的小,余数就是前面的数5.2 递增递减运算符
5.2.1 前置++
- 先自加,后返回值
- 先自加,后运算(++在前 先加)
let num = 10;
alert(++num + 10); // 21
5.2.2 后置++
- 先原值运算,后自加(++在后 后加)
- 先表达式返回原值,后面变量再自加1
let num = 10;
alert(10 + num++); // 20
- **注意:**前置++ 和 后置++ 单独书写没有差别
5.2.3 前置–
先自减,后运算(先已后人)
let num = 10;
alert(--num + 10); // 19
5.2.4 后置–
先原值运算,后自减(先人后己)
let num = 10;
alert(10 + num--); // 20
let a = 10;
++a; // ++a = 11 a = 11
let b = ++a + 2; // a = 12 ++a = 12
console.log(b); // 14
let c = 10;
c++; // c++ = 11 c = 11
let d = c++ + 2; // c++ = 11 c = 12
console.log(d); // 13
let e = 10;
let f = e++ + ++e;
// e++ = 10 e = 11
// e = 12 ++e = 12
console.log(f); // 22
5.3 比较运算符
- 注意:
- 比较运算符有隐式转换
-
console.log(2 == '2') // true
- ⚠ 特殊说明:
- 如果是数字和“其他值”的比较,则其他值会自动转换成数字去比较
- 涉及到“NaN”都是false
- 如果是”字符串“和“字符串”比较,则会比较每一个字符的ASCII码
- 如果是布尔值参与比较,布尔值会自动转换成数字0和1
- 数字只有0是假,其余为真
- 字符串只有’'(空字符串)是假,其余都为真
5.4 ❗ 逻辑运算符
- 1️⃣ && 与 有 一个 是 false ,得到的 结果 就是 false ;只有 两侧 都是 true 的时候,才可以到的 true
- 2️⃣ || 或 有一个 是 true ,得到的结果就是 true ;只有 两侧 都是 false 的时候,才得到 false
- 3️⃣ ! 非 取反(双取反可以转布尔)
- ⚠ 注意: 逻辑运算符 优先级: ! > && > ||
5.4.1 短路运算(逻辑中断)
- 短路运算的原理:当有多个表达式(值)时,左边的表达式值可以确定结果时,就不再继续运算右边的表达式的值;
- 1️⃣ 逻辑与-语法:(一假则假)
-
表达式1 && 表达式2 如果 表达式1的值 为 真,则返回 表达式2 如果 表达式1的值 为 假,则返回 表达式1 (后面的表达式不再执行) 两侧都为真,返回第二个
-
- 2️⃣ 逻辑或-语法: (一真则真)
-
表达式1 || 表达式2 如果 表达式1的值 为 真,则返回 表达式1 (后面的表达式不再执行) 如果 表达式1的值 为 假,则返回 表达式2 两侧都为真返回第一个
-
5.5 赋值运算符
5.6 ❗ 运算符优先级
- 一元运算符 里 逻辑非 优先级最高
- 逻辑与 比 逻辑或 优先级 高
-
console.log( 4 >= 6 || '人' != '阿凡达' && !(12 * 2 == 144) && true) // true let num = 10 console.log( 5 == num / 2 && (2 + 2 * num).toString() === ‘22’) // true let a = 3 > 5 && 2 < 7 && 3 == 4 console.log(a) // false let b = 3 <= 4 || 3 > 1 || 3 != 2 console.log(b) // true let c = 2 === "2" console.log(c) // false let d = !c || b && a console.log(d) // true
六、流程控制 - 分支语句
- 表达式和语句的区别:
- 表达式:表达式可被求值,可以写在赋值语句的右侧(运算符和数据组成的式子)
- 语句:不一定有值(可以被执行的代码)
6.1 if 语句
-
条件为 true 的时候,大括号里面的代码才会执行
-
小括号里的结果若不是布尔类型时,会发生隐式转换转为布尔类型
-
⚠ if分支语句,如果前面的条件被满足,后面的代码就不执行了
-
if (条件) { 满足条件要执行的代码 }
-
if (条件) { 满足条件要执行的代码 } else { 条件不满足时要执行的代码 } eg: // 能被4整除但不能被100整除,或者被400整除的年份是闰年,否则都是平年 let year = +prompt('请输入年份:') if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) { alert(`&{year}是闰年`) } else { alert(`&{year}是平年`) }
-
if (条件表达式1) { 满足条件1要执行的代码 } else if (条件表达式2) { 满足条件2要执行的代码 } else if (条件表达式n) { 满足条件n要执行的代码 } else { 所有条件都不满足时要执行的代码 }
6.2 三元运算符
- 语法结构:
条件表达式 ? 表达式1 : 表达式2
- 执行思路:
- 如果 条件表达式 结果 为真,则 返回 表达式1 的值,如果 条件表达式 结果 为假,则 返回 表达式2 的值
- 代码展示:
let time = prompt('请您输入一个 0 ~ 59 之间的一个数字') // 三元表达式 表达式 ? 表达式1 :表达式2 let result = time < 10 ? '0' + time : time // 把返回值赋值给一个变量 alert(result) /* 如果输入的数字小于10,就在该数字前面加一个0,否则原样输出该数字 */ let num = prompt('请输入数字:') let result = num < 10 ? '0' + num : num alert(result)
6.3 switch 语句
- 表达式有一个返回值,**返回值 **会和 value 做匹配(必须是 全等 的关系),如果匹配上了就执行对应的语句 ,如果都匹配不上就执行default里的语句
- 语法:
switch ( 表达式 ) { case value1: 满足value1执行的代码 break case value2: 满足value2执行的代码 break default: 所有条件都不满足时要执行的代码 }
- ⚠ 注意:
- 每一个 case 和 表达式 必须是 ===(值 和 数据类型 都相等) 才叫满足
- switch 语句,只能判断一些准确的值,不能判断范围
- 实际开发过程中 表达式 我们经常 写成 变量
- 代码展示:
let num1 = +prompt('请输入第一个数字:') let sp = prompt('请输入 + - / * 其中一个:') let num2 = +prompt('请输入第二个数字:') switch (sp) { case '+': let add = num1 + num2 alert(`${num1} + ${num2} = ${add}`) break case '-': let dif = num1 - num2 alert(`${num1} - ${num2} = ${dif}`) break case '*': let bus = num1 * num2 alert(`${num1} x ${num2} = ${bus}`) break case '/': let pro = num1 / num2 alert(`${num1} ÷ ${num2} = ${pro}`) break default: alert('请输入正确的运算符') break }
七、流程控制 - 循环语句
7.1 循环成立的条件
- 初始值 作为循环的开始
- 条件判断 决定要不要继续循环
- 要重复执行的代码
- 改变初始值 为了让循环有结束
7.2 ❗ for 循环
- 语法结构:
for (初始化变量; 条件表达式; 操作表达式) { 循环体 }
- 初始化变量:通常被用于初始化一个计数器,这个变量帮我们来记录次数。
- 条件表达式:用于确定每一次循环是否能被执行。如果结果是 true 就继续循环,否则退出循环。(终止条件)
- 操作表达式:每次循环的最后都要执行的表达式。通常被用于更新或者递增计数器变量。当然,递减变量也是可以的。(递增或者递减)
- 代码展示:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> </body> <script> // 求1-100之间所有数字的和 let sum = 0; let average = 0; for (let i = 1; i<= 100; i++) { sum += i } average = sum / 100; console.log('1-100之间所有数字的和为:' + average); // 求1-100之间所有偶数的和和奇数的和 let even = 0; let odd = 0; for (i = 1; i <= 100; i++) { // 第一种 if (i % 2 == 0) { even = even + i; } else { odd += i; } //第二种 if (i % 2 == 0) { even = even + i; } odd = sum - even; } console.log('1-100之间所有偶数的和为:' + even + '\n' + '1-100之间所有奇数的和为:' + odd); // 求1-100之间所有能被3整除的数字的和 let odd_num = 0; for (let i = 1; i <= 100; i++) { if (i % 3 == 0) { odd_num = odd_num + i; } } console.log('1-100之间所有能被3整除的数字的和为:' + odd_num); //弹出输入框,输入一共有n个学生,然后弹出n个输入框,输入每个学生的成绩,最后输出总成绩和平均成绩 let frequency = prompt('请输入有多少个学生:'); let score = 0; for (let i = 1; i <= frequency; i++) { let a = prompt('请输入第' + i + '个同学的成绩:'); // 从 prompt 获取到的数据是 String 类型的,不能直接和数字进行相加 score = score + parseFloat(a); } alert('该班总成绩是:' + score); alert('该班平均成绩是:' + (score / frequency)); //一行打印五个星星 let str = ' '; for (let i = 1; i <=5; i++) { str = str + '⭐'; } console.log(str); </script> </html>
7.3 ❗ 双重for循环
-
//打印n行m列的星星 let rows = +prompt('请输入需要打印几行的星星:'); let cols = +prompt('请输入需要打印几列的星星:'); let str = ''; for (let i = 1; i <=rows; i++) { for (let j = 1; j <= cols; j++) { str += '⭐'; } str += '\n'; } console.log(str);
- 冒泡排序:
// 外层for循环:控制已经排序好的元素的个数 // 内层for循环:控制每排序好一个元素需要比较几次 const arr = [1, 7, 3, 9, 5, 2, 6, 4, 8]; for (let i = 0; i < arr.length - 1; i++) { for (let j = 0; j < arr.length - i - 1; j++) { if (arr[j] > arr[j + 1]) { let temp = arr[j + 1]; arr[j + 1] = arr[j]; arr[j] = temp; } } } console.log(arr);
7.4 while循环
- 循环三要素:
- 变量初始值
- 终止条件
- 变量变化量
- 语法:
let n = 1 while ( 循环条件 ) { 重复执行的代码 n++ }
7.5 ❗ continue 和 break
- continue
- 退出本次循环,继续下一次循环
- 一般用于排除或者跳过某一个选项的时候
- continue 在循环节构中用来跳过本次循环中剩余的代码,并且在条件求值为真的时开始下一次循环。
- 不能省略 continue 后面的分号(导致胡乱)
- continue 后面 的 代码 不会 再 执行
- break
- 退出整个循环(结束循环)
- 一般用于结果已经得到,后续的循环不需要的时候
- 在一些计算机语言中 break 是保留字,其作用大多数情况下是终止上一层循环。
7.6 JS标记语法
-
JS标记语法 here: for (let i = 1; i <= 5; i++) { for (let j = 1; j <= 3; j++) { if (i === 3 && j === 2) { console.log('看到半条虫子') break here } console.log('吃的第 ' + i + ' 个包子得第 ' + j + ' 口') } console.log('=================') }
八、❗❗ 数组 - 基础
- 一种 有序 的数据集合
8.1 创建数组
8.1.1 字面量创建
let 数组名 = [数据1, 数据2, …, 数据n]
let arr = ['name', 'Jack', 'age', 20, 'gender', '男']
8.1.2 内置构造函数创建
//没有参数表示空数组
let arr = new Array()
//一个参数表示数组的长度
let arr = new Array(100)
//多个参数表示都是数组里面的数据,没有表示长度的参数
let arr = new Array(10, 20, 30)
arr.length = 2 //删除最后一个
arr.length = 5 //后面的两个用空补齐
console.log(arr[2]) //读取里面的额第三个数据
8.2 ❗❗ 遍历数组
能够获取数组里面的每个元素
8.2.1 for
- 格式:
for (let i = 0; i < 数组名.length; i++) { // 执行的操作 // console.log(数组名[i]) }
8.2.2 forEach
- 取代 for 循环
- 语法:
数组.forEach(function (item, index, arr) { } ) -> item :数组里面的每一项 -> index :数组里面每一项的索引 -> arr : 原始数组
- ⚠ 没有 返回值
- forEach中的 return 等同于 for循环的 continue
- 终止forEach循环:try - catch
try { [1, 2, 0, 3].forEach(item => { console.log(item) if (item === 1) { console.log(item) throw Error() } }) } catch (error) { console.log('终止循环') } // 打印结果 // 1 // 1 // z
8.2.3 for of
- 遍历 可迭代 的数据:数组,伪数组,字符串
- 遍历的是数组、伪数组、字符串的值
- 语法:
for (let item of arr) { console.log(item) // item -> 数据里面的每一项 }
8.3 数组的增删查改
8.3.1 增
- 1️⃣✔数组.push() 方法:将 一个或多个元素 追加 **到 数组 的 末尾 ,并 返回 追加元素后数组 的 长度
- 语法:数组.push(数据1, 数据2, … ,数据n)
- 返回值: 追加元素后数组的长度
- ⚠改变原始数组
- 2️⃣ 数组.unshift() 方法:将 一个或多个元素 插入 到 数组 的 开头,并 返回 插入元素后数组 的 长度
- 语法:数组.unshift(数据1, 数据2, …, 数据n)
- 返回值: 插入元素后数组的长度
- ⚠改变原始数组
- 见代码展示
8.3.2 删
- 1️⃣ 数组.pop() 方法:从数组中 删除 最后一个 元素
- 语法:数组.pop()
- 返回值: 被删除的元素
- ⚠改变原始数组
- 2️⃣ 数组.shift() 方法:从数组中 删除 第一个元素
- 语法:数组.shift()
- 返回值: 被删除的元素
- ⚠改变原始数组
- 3️⃣ ✔数组.splice(start, deleteCount) 方法:删除 数组 里 指定的元素
- 语法:数组.splice(开始索引,删除几个)
- ⚠🔺 注意:包含开始索引 对应 的 元素
- deleteCount:表示要移除的数组元素的个数(如果 省略 则 默认 从 指定的起始位置 删除到最后)
- 返回值: 新数组(将删除的元素放到这个新数组里面)
- ⚠改变原始数组
- 语法:数组.splice(开始索引,删除几个)
- 见代码展示
8.3.3 查
- 见代码展示
8.3.4 改
- 见代码展示
代码展示:
<script>
let arr = ['pink', 'red'];
// 增加
// 方案一:数组.push()方法
语法:arr.push(元素1, 元素2, …, 元素n);
追加数组 的同时 有一个 返回值 是 追加以后 的 数组长度
console.log(arr.push('green', 'hotpink', 'deeppink')); // 5
console.log(arr);
// ['pink', 'red', 'green', 'hotpink', 'deeppink']
// 方案二:数组.unshift()方法
语法:arr.unshift(元素1, 元素2, …, 元素n);
console.log(arr.push('green', 'hotpink', 'deeppink')); // 5
console.log(arr);
// ['green', 'hotpink', 'deeppink', 'pink', 'red']
// 删除
// 方案一:数组.pop()
console.log(arr.pop()); // green
// 方案二:数组.shift()
console.log(arr.shift()); // red
// 方案三:数组.splice(起始位置的索引, 删除几个元素)
// 从索引号0的位置开始,删除两个元素
console.log(arr.splice(0, 2)); // ['hotpink', 'deeppink']
// 查询 --- 利用数组的索引进行查找
console.log(arr[0]); // pink
// 修改 --- 利用 对数组进行遍历,然后和字符串拼接,重新赋值给数组里的元素
// 方案一:
arr[0] = 'blue';
arr[1] = 'purple';
//方案二:
for (let i = 0; i < arr.length; i++) {
arr[i] += '老师';
}
console.log(arr)
</script>
九、❗❗ 函数
- 函数:执行特定任务的代码块
9.1 函数的声明和使用
9.1.1 函数的声明
- 函数命名规范:
- 和变量命名基本一致
- 小驼峰式命名法
- ⚠ 前缀应该为动词
-
can --- 判断是否可执行某个动作 has --- 判断是否含义有某个值 is --- 判断是否为某个值 get --- 获取某个值 set --- 设置某个值 load --- 加载某些数据
-
- 1️⃣ 具名函数
- 语法:
function 函数名() { 函数体 }
- ⚠ 注意:可以 后声明先调用 ,也可以 先声明后调用
- 语法:
- 2️⃣ 匿名函数
- 使用方式:
- ① 函数表达式:
- 语法:
let 函数名 = function () { // 函数体 }
- 语法:
- ② 立即执行函数:
- 语法:
!(function (实参) { 函数体 })(实参); !(function (实参) { 函数体 } (实参));
- ⚠ 注意: 立即执行函数 末尾 必须加 分号
- 语法:
- ① 函数表达式:
- ⚠ 注意:
- 函数名() — 表示调用这个函数
- 函数名 — 表示一个变量,代表这个函数
- 使用方式:
- 代码展示:
function getSum() { let sum = 0 for (let i = 1; i <= 100; i++) { sum += sum } document.write(sum) }
9.1.2 函数的使用
- 函数的调用:
-
函数名()
-
- ⚠ 注意: 声明的函数 必须 调用 才会真正执行,使用 () 调用函数
- 函数 一次声明 可以 多次调用 ,每一次函数调用函数体里面的代码会重新执行一次
- 代码展示:
function getSum() { let sum = 0 for (let i = 1; i <= 100; i++) { sum += sum } document.write(sum) } getSum() // sum = 5050
- 代码展示:
9.1.3 函数传参
- 声明语法:
-
function 函数名(参数列表) { 函数体 }
- 传入数据列表
- 声明这个函数需要传入几个数据
- 多个参数之间用 逗号 隔开
- 代码展示:
function getSum(start, end) { // 形参 - 形式上的参数 let sum = 0 for (let i = start; i <= end; i++) { sum += sum } document.write(sum) }
-
- 调用语法:
-
函数名(传递的参数列表)
- 代码展示:
getSum(10, 20) // sum = 33 // 实参 - 实际的参数
- 形参:
- 形式上的参数 — 写在 函数声明阶段 的 小括号 里
- 本质:就是在函数内部声明的变量(局部变量)
- 作用:接受实参传递的参数
- 如果既没有实参传递,也没有设置默认值,就是 undefined
- 实参:
- 实际上的参数 — 写在 **函数调用阶段 **的 **小括号 **里
- 实参 可以是 变量
- 作用:给形参赋值
-
- 参数默认值:
- 在调用的过程中,如果有参数传递过来,就用传递过来的;如果没有参数传递过来就用默认的
- ⚠ **注意:**若没设置默认值,默认就是 undefined
- 代码展示:
function getSum(x = 0, y = 0) { // 小括号里写了就不用写 // 推荐用逻辑中断 // x = x || 0 // y = y || 0 document.write(x + y) } getSum() // 0 getSum(1, 2) // 3
9.1.4 函数的返回值:
- 语法:返回给调用者(当调用某个函数,这个函数会返回一个结果出来)
return 要返回的数据
- ⚠ 注意:
- 在 函数体 中使用 return 关键字能 将内部的执行结果 交给 函数外部 使用
- 所以 return 后面的数据不要换行写
- ⚠ return 和 要返回的数据不能换行写(模板字符串、函数除外)
- 一个函数只能有一个return(因为第一个return后面的代码就不执行了)
- 如果 函数 没有指定 return,这种情况函数 默认返回值 为 undefined
- 函数如果有 多个返回值 ,用 中括号包裹,中间 用 逗号隔开 (返回的是一个新数组)
- 代码展示:
-
function fn() { return 20 // 把 结果(20) 返回给 fn } fn() // 相当于执行了 fn() = 20 (fn()是函数的调用者) console.log(fn()) // 20 // 求两个数的最大值 function getMax(x = 0, y = 0) { return x > y ? x : y } getMax(65, 99) // 99 let max = getMax(65, 99) console.log(max)
- 求数组里面的最大值和最小值
- 代码展示:
// 声明一个 getArrValue函数 function getArrValue(arr = []) { let max = arr[0] let min = arr[0] //遍历数组 for (let i = 0; i < arr.length; i++) { // 最大值 if (max < arr[i]) { max = arr[i] } // 最小值 if (min > arr[i]) { min = arr[i] } } // return 返回多个值写法 return [max, min] } let newArrr = getArrValue([23, 56, 32, 198, 0, 5, -98]) console.log(`数组中的最大值是:&{newArr[0]}`) // 198 console.log(`数组中的最小值是:&{newArr[1]}`) // -98
- 时间转换 + 补0
- 代码展示:
function getTime(t = 0) { let second = parseInt(t % 60) let minute = parseInt(t / 60 % 60) let hour = parseInt(t / 60 / 60 % 24) // if条件分支语句 /* if (second < 10) { second = '0' + second } if (minute < 10) { minute = '0' + minute } if (hour < 10) { hour = '0' + hour } */ // 三元表达式 second < 10 ? second = '0' + second : second minute < 10 ? minute = '0' + minute : minute hour < 10 ? hour = '0' + hour : hour return `${t}秒 = ${hour}小时${minute}分钟${second}秒` } let str = getTime(+prompt('请输入秒数:')) // 1000 document.write(str) // 1000秒 = 00小时16分钟40秒
-
- ⚠ 函数注意点:
- 两个函数名相同的函数,后面的会覆盖前面的
- 两个函数体相同的匿名函数不是同一个函数
- 传递参数
- 形参过多会自动填上 undefined
- 实参多于形参,剩余的实参不参与运算
- 实参少于形参,多于的形参使用默认值 undefined
- 函数一旦碰到 return 就不会往下执行,函数的结束用 return
- break 和 return 的区别:
- break:结束 的是 循环 和 switch
- return:结束 的是 函数 和 for循环
9.2 作用域基础
作用域: 变量的 可用性范围 就是 作用域 (提高程序逻辑的局部性)
9.2.1 全局作用域
- 全局有效
- 作用与所有代码执行的环境(整个script标签内部)或者一个独立的js文件
9.2.2 局部作用域
- 局部有效
- 作用与函数内的 代码环境,就是局部作用域。因为跟函数有关,所以也称为函数作用域
9.2.3 特殊
- 如果函数内部,变量没有声明,直接赋值。也当全局变量看。不推荐
- 代码展示:
function fn() { num = 10 // 全局变量来看 } fn() console.log(num) // 10
- 代码展示:
- 函数内部的 形参 可以看作是 局部变量
- 代码展示:
function fun(x, y) { // 形参可以看做是函数的局部变量 console.log(x) // 1 } fun(1, 2) console.log(x) // x is not defined (报错)
- 代码展示:
9.3 变量的访问原则
- 只要是代码,就至少有一个作用域
- 写在函数内部的局部作用域
- 如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
- ⚠ 访问原则(就近原则):在能够访问到到的情况下 先局部,局部没有 再找全局 (由内向外)
-
代码展示:
functin f1 () { let num = 123 function f2 () { console.log(num) // 123 } f2() } let num = 456 f1()
function f1() { let num = 123 function f2() { let num = 0 console.log(num) // 0 } f2() } let num = 456 f1()
let a = 1 function fn1() { let a = 2 let b = '22' function fn2() { let a = 3 fn3() function f3() { let a = 4 console.log(a) // a的值 = 4 console.log(b) // b的值 = '22' } } } fn1()
-
9.4 匿名函数
- 没有名字的函数,无法直接使用
- 使用方式: 函数表达式 / 立即执行函数
- 1️⃣ 函数表达式:
- 函数表达式:将匿名函数赋值给一个变量,并且通过变量名称进行调用
- 语法:
let 变量名 = function () { // 函数体 }
- ⚠ 调用:变量名()
- 代码展示:
let fn = function () { console.log('我是函数表达式') } fn() // 我是函数表达式
- ⚠🔺 注意: 函数表达式 必须 先 声明 、后 调用
- 因为变量必须先声明后使用
- 2️⃣ 🔻**立即执行函数:**避免全局变量的污染(防止变量污染(冲突))
- 函数声明后立即执行,并且执行完一次后被释放
- 语法:
// 写法一: (函数)(); // 写法二: (函数 ());
- ⚠ 立即执行函数 必须 加 分号
- 立即执行函数可以传递参数
- 先写两个小括号,前面的小括号写函数
- 可以给匿名函数添加函数名
- ⚠ 最后面的小括号相当于调用函数
- 代码展示:
(function () { console.log('立即执行函数') })(); // 保存之后会在控制台立即打印 '立即执行函数'
// 第一种写法: (function () { console.log('立即执行函数第一种写法'); })(); (function (x, y) { // (x, y) ---> 形参 console.log(x, y); // x = 5; y = 9 })(5, 9); // (5, 9) ---> 实参 // 第二种写法: (function () { console.log('立即执行函数第二种写法'); } ()); (function (x, y) { // (x, y) ---> 形参 console.log(x, y); // x = 5, y = 9 } (5, 9)); // (5, 9) ---> 实参
- 代码展示:
- 1️⃣ 函数表达式:
9.5 具名函数 和 匿名函数 的区别
- 具名函数:
- 可以 先调用 后声明,也可以 先声明 后调用
- 具名函数 的 调用 可以 写在 任意位置
- 匿名函数
- 函数表达式必须 先声明函数表达式 后调用
- 匿名函数 的 调用 必须写在 声明函数 的 后面
9.6 函数中的逻辑中断
- 函数的默认值
- 方案一:(赋值)
function fn(x = 0, y = 0) { x = x y = y console.log(x, y) } fn(1, 2) // x = 1, y = 2 fn() // x = 0, y = 0
- 方案二:(逻辑中断)(推荐)
function fn(x, y) { x = x || 0 y = y || 0 console.log(x, y) } fn(1, 2) // x = 1, y = 2 fn() // x = 0, y = 0
- 方案一:(赋值)
十、❗❗ 对象 - 基础
- 对象 = 属性 + 方法
10.1 对象 - object
- 对象是一种复杂数据类型
- 一种 无序 的数据集合(不可迭代)
10.2 使用对象
10.2.1 声明对象
- 语法:
- let 对象名 = {}
- let 对象名 = new Object()
let obj = {} // 声明了一个obj的对象 let obj = new Object();
- 组成:属性 + 方法
let 对象名 = { 属性名: 属性值, 方法名: 函数 } eg: let obj = { uname: 'pink老师', age : 18, gender: '男' } console.log(obj) // {uname: 'pink老师', age: 18, gender: '男'}
- 属性都是 成对 出现的,包括 属性名 和 属性值 ,它们之间使用英文 : 分隔
- 对象属性没有顺序
- 多个属性之间使用英文 , 分隔
- 属性就是依附在对象上的变量(外面 是 变量,对象内 是 属性)
- ⚠ 属性名可以使用 " " 或 ’ ’ ,一般情况下省略,除非名称遇到特殊符号如 空格 、 中划线 等
- ⚠ 属性名 不能 使用 反引号
10.2.2 增删查改
- 1️⃣查
- 语法:
- 对象名.属性名
- 对象名[‘属性名’] (🔺 属性名 必须 加 引号)
- ⚠ 如果属性名存放在一个 变量 里面,只能使用 数组关联语法
- 代码展示:
console.log(obj.uname) // pink老师 console.log(obj['uname']) // pink老师
- 语法:
- 2️⃣改
- 语法:对象名.属性名 = 新值
- 代码展示:
obj.gender = '女' console.log(obj.gender) // 女
- 3️⃣增
- 语法:对象名.新属性名 = 新值
obj.address = '武汉黑马' console.log(obj) // {uname: 'pink老师', age: 18, gender: '女', address: '武汉黑马'} console.log(obj.address) // 武汉黑马
- 语法:对象名.新属性名 = 新值
- 4️⃣删(了解)
- 语法:delete 对象名.属性名
delete obj.address console.log(obj) // {uname: 'pink老师', age: 18, gender: '女'}
- 语法:delete 对象名.属性名
10.2.3 对象的方法
- 方法 = 方法名 + 函数,它们之间用 : 分隔
- 多个属性 之间 使用英文 , 分隔
- 方法是依附在对象中的函数(外面 是 函数,对象内 是 方法)
- 方法名可以使用 “” 或 ‘’ ,一般情况下省略,除非遇到特殊符号如 空格、中划线 等
- 声明对象,并添加了若干方法后,可以使用,调用 对象 中的 (方法)函数,我们称之为方法调用
- 语法:对象名.方法名()
- 千万别忘了给方法名后面加 小括号
- 代码展示:
let obj = { uname: '刘德华', // 方法 song: function () { console.log('冰雨') }, getSum: function (x, y ) { x = x || 0 y = y || 0 console.log(x + y) } } // 方法调用 对象名.方法名() obj.song() // 冰雨 obj.getSum(2, 5) // 7
10.3 ❗ 遍历对象
- 1️⃣ for in 语法:
- 遍历的是 对象的属性 / 数组的索引 / 伪数组的索引 / 字符串的索引
- 得到的索引都是字符串型
for (let k in obj) { console.log(obj[k]) }
- 结论: k 是获得 对象 的属性名 , 对象名[k] 是获得 对象 的 属性值
- 遍历的是 对象的属性 / 数组的索引 / 伪数组的索引 / 字符串的索引
- 2️⃣ 判断一个成员在不在对象里面
- 语法:
属性/方法 in 对象名
- 返回值: 布尔值 (true / false)
- 代码展示:
let obj = { uname: 'jack', age: 18, gender: '男', score: 100 }; console.log('name' in obj); // true
- 语法:
- 代码展示:
//声明一个 obj 对象 let obj = { uname: 'pink老师', age: 18, gender: '男' } // 用 fou in 循环遍历对象 for (let k in obj) { // k 可以是任何变量,选择 k/key 是因为许多程序员都在使用 // console.log(k) // k 是字符串型的,带有引号 // k => 'uname' 'age' 'gender' // obj 对象里面没有 'uname'、 'age'、 'gender' 的属性名 ------ 有引号 // obj 对象里面的属性名是 uname、 age、 gender ------ 没有引号 // 查询对象里面的属性有两种方法:1)对象名.属性名 // 2)对象名['属性名'] // 原因:obj[k] === obj['uname'] === obj.uname // obj[k] === obj['age'] === obj.age // obj[k] === obj['gender'] === obj.gender // 所以,利用for in循环遍历对象采用第二种查询方法 // 结论:k 是获得对象的属性名 , 对象名[k] 是获得 属性值 console.log(obj[k]) // 'pink老师'(string) 18(number) '男'(string) }
- 渲染学生信息
- 代码展示:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <style> table { width: 600px; text-align: center; } table, th, td { border: 1px solid #ccc; border-collapse: collapse; } caption { font-size: 18px; margin-bottom: 10px; font-weight: 700; } tr { height: 40px; cursor: pointer; } table tr:nth-child(1) { background-color: #ddd; } table tr:not(:first-child):hover { background-color: #eee; } </style> </head> <body> <h2>学生信息</h2> <p>将数据渲染到页面中...</p> <table> <caption>学生列表</caption> <tr> <th>序号</th> <th>姓名</th> <th>年龄</th> <th>性别</th> <th>家乡</th> </tr> <script> // 1.数据准备 let students = [ { name: '小明', age: 18, gender: '男', hometown: '河北省' }, { name: '小红', age: 19, gender: '女', hometown: '河南省' }, { name: '小刚', age: 17, gender: '男', hometown: '山西省' }, { name: '小丽', age: 18, gender: '女', hometown: '山东省' } ] // 2.开始渲染页面 for (let i = 0; i < students.length; i++) { document.write(` <tr> <td>${i + 1}</td> <td>${students[i].name}</td> <td>${students[i].age}</td> <td>${students[i].gender}</td> <td>${students[i].hometown}</td> </tr> `) } </script> </table> </body> </html>
- 代码展示:
10.4 内置对象
10.4.1 Math对象包含的方法
- 1️⃣Math.random():生成0-1之间的随机小数(🔺包含0不包括1)
- 如何生成0-10的随机小数?
-
let num = Math.floor(Math.random() * (10 + 1))
-
- 如何生成5-10的随机数?
-
let num = Math.floor((Math.random() * (5 + 1)) + 5
-
- 如何生成N~M的随机数?
-
let num = Math.floor(Math.random() * (M - N + 1)) + N function getRandom (N, M) { // return Math.floor(Math.random() * (M - N + 1)) + N; return parseInt(Math.random() * (M - N + 1)) + N; }
-
- 数组随机数:
Math.floor(Math.random() * arr.length)
- 如何生成0-10的随机小数?
- 2️⃣Math.ceil(数据):向上取整
- 代码展示:
console.log(Math.ceil(1.1)) // 2 console.log(Math.ceil(1.01)) // 2
- 代码展示:
- 3️⃣Math.floor(数据):向下取整
- 代码展示:
console.log(Math.floor(1.9)) // 1 console.log(Math.floor(1.99)) // 1
- 代码展示:
- 4️⃣Math.max(数据):找最大数
- 代码展示:
console.log(Math.max(0.1, 1, 65, 23, 88)) // 88
- 代码展示:
- 5️⃣Math.min(数据):找最小数
- 代码展示:
console.log(Math.min(99, 65, 3, 0.1, -99)) // -99
- 代码展示:
- 6️⃣pow:幂运算(了解)
- 7️⃣Math.abs(数据):绝对值
- 代码展示:
console.log(Math.abs(-100)) // 100
- 代码展示:
- 8️⃣Math.round(数据):四舍五入(取整数)
- 代码展示:
console.log(Math.round(2.45)) // 2 console.log(Math.round(2.5)) // 3 console.log(Math.round(-20.5)) // -20 console.log(Math.round(-20.51)) // -21
- 代码展示:
贰、❗❗ Web APIS
- 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) // 1 function 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> // 事件侦听-页面滚动事件-window window.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.clientWidth console.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) // 补0 hours = hours < 10 ? '0' + hours : hours let minutes = parseInt(count / 60 % 60) // 补0 minutes = minutes < 10 ? '0' + minutes : minutes let seconds = parseInt(count % 60) // 补0 seconds = seconds < 10 ? '0' + seconds : seconds return `${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 // 下一个兄弟节点 // 语法:兄弟元素.nextElementSibling console.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或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'; } })();
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('哈')); // true console.log(/哈/.test('哈哈')); // true console.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('二哈')); // true console.log(/^哈/.test('哈')); // true console.log(/^哈/.test('哈哈')); // true console.log(/^哈/.test('二哈')); // flase console.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 - 事件