JavaScript知识回顾
本文最后更新于 2024-12-08,文章内容可能已经过时。
JavaScript
js
是运行在客户端(浏览器)
的编程语言
,可以在浏览器
中实现交互特效
,网页特效
,表单验证
等!
JavaScript特性
解释性语言
解释性语言
就是读一行解释一行
, 一般在开发JavaScript
代码时候,计算机是无法直接认识的
,需要经过编译或者解释
后才能让计算机正确执行代码
! 计算机只认识0101011001
这样的二进制数字
!
解释性语言:
会将编写好的代码一行一行解释翻译成浏览器所认识的代码
!编译性语言:
会将编写好的代码完整过一遍后生成翻译文件
,进行整体翻译
!
单线程
单线程
就是不能在同一个时间段内干多件事儿
,只能同一时间内做一件事儿
!
多线程
就是可以在同一事件内处理很多事务
!
JS三大部分
ECMAScript
和DOM
以及BOM
ECMAScript:
是一种规范
,用于制定js语法
和规则
,JavaScript
是在ECMA规范
上的一种实现
!
DOM:
是文档对象模型
,是w3c
的标准
,提供了关于操作html元素dom的API方法
!
BOM:
是浏览器对象模型
,是每个浏览器内部
的一个实现
,提供对浏览器操作的API方法
!
ECMAScript
ECMAScript
是JavaScript
一套标准规范,
ECMA
会制定出方案
结果,JS
对方案进行的一个功能实现!
例如:
循环 判断 变量声明 函数声明
等基本语法!
字面量
字面量
表示声明值的一部分
,根据值的类型
,有不同类型的字面量
!
var age = 15;
则15
为数字字面量
!
var name = "张三";
则张三
为字符串字面量
!
变量
变量(容器):
它可以将不同数据类型的值
,存放
到一个变量(容器)
当中!
变量就是在程序中用来表示存储数据的!
声明变量
声明变量的方式有三种,分别为
var
let
和const
!
var name = "张三"
let name = "张三"
const name = "张三"
这里以
var
关键字举例说明:
var
是一种声明变量的关键字
,定义某个变量时
,必须加入关键字进行声明
,此处用来表示定义存储变量
!
var 变量声明关键字!
name 变量名!
= "张三"; 对变量名进行赋值操作,并存储数据!
三者声明变量区别
var
是属于早期的一种声明变量
的一种方式,其中存在众多问题,例如变量提升
,变量污染
,重复定义
等 !
变量提升
:js引擎
会在解析js代码
时,将变量声明提取到作用域的最顶端
,且初始值为undefind
!
变量污染
:var
没有块级作用域
,在块级作用域中定义的变量
,依旧可以在上级或全局作用域中访问
!
重复声明
:var
在同一个作用域
内可以重复声明一个变量
,会导致后面的变量值覆盖已有的变量值
!
变量提升
变量提升
,就是js引擎
在解析代码
时,会将声明的所有变量提升在作用域顶部
,这就意味着在变量赋值之前
依旧可以访问变量,只不过该变量的值为undefind
!console.log(name); // undefind var name = "张三"; console.log(name); // 张三
变量污染
变量污染
, 就是var
是属于函数作用域
,不属于块级作用域
,块级作用域
包括(if for switch
),这意味着,在块级内部定义的变量
,在块级之外
依旧可以访问
!function demo(){ if(true){ // 块级内部 var name = "张三"; } // 块级外部 console.log(name); // 张三 }
重复定义声明
重复定义声明
, 在代码中可以在同一个作用域下重复定义一个已存在声明变量的值
!var age = 19; var age = 22; console.log(age); // 22
let
和 const
这两种
声明方式
,是es6提出的标准
,它们的出现就是为了解决var
声明变量时所遗漏的一些问题 !
let
:- 不能在
同一个作用域内重复定义变量
! - 无法
在变量赋值前进行变量访问
,不存在变量提升
! - 属于
块级作用域
,在块级之外无法访问,且不会对环境变量造成污染!
- 不能在
const
:与
let
关键字基本一致,则区别是,const
是用来定义常量
!- 常量在
声明变量时
,必须有初始值
! - 且
一旦被赋值,则无法在改变值
!
- 常量在
变量的本质
定义变量的本质,就是
在内存中开辟一个空间
,将变量值存储内存空间中
!其
变量名
则为内存空间
的一个命名方式
,通过命名
可以获取对应空间中的存放数据
!
命名规范
不能以关键字进行命名
,例如let const if for
等语法关键字!只能用下划线 字母 数字 $部分组成
,不能以数字作为开头
!首字母不能大写,驼峰命名,例如
noProblem
!
数据类型
在
js
中数据类型
为弱类型
,弱类型
就是根据赋值后
来判定该值类型
为某一个具体类型
!
基本数据类型
基本类型
: (字符串
,布尔值
,null
,undefind
,symbol
,bigInt
)
引用数据类型
引用数据类型
: 也是复杂类型
(Array_数组 Object_对象 Function_函数
)
引用数据类型
:在内存中会开辟一个新的空间
用来存放这个地址
,并将这个地址指向声明变量的变量名!
区别
基本数据类型
:- 存放在
栈内存中
,在内存中存放的是值
,可以直接对值进行操作,和对比
!
- 存放在
引用数据类型
:- 存放在
堆内存中
,在内存中会存放引用类型的地址
,并将地址指向声明变量的变量名
!
- 存放在
运算符
运算符
包含(算数运算符
和比较运算符
以及赋值运算符
和逻辑运算符
)
算数运算符:+ - * / %(取模),算数优先级,先乘除后加减,若有括号先算括号里的表达式! %(取模) 就是两者相除剩余的余数!
比较运算符: 分为弱类型判断(== !=) 和 强类型判断(=== !==) 以及大小数值比较 (<= >=)!
赋值运算符:
分为正常赋值( a = b ) 和 逻辑赋值( ??=, ||=, &&=, )
弱类型和强类型比较
弱类型:(== !=) 使用 弱类型判断 时,会做类型隐士转换,比如(“1” == 1)返回为 true,原因是,会将字符串1转换为 number类型后在进行判断!
强类型:(=== !==) 使用 强类型判断 时,类型不会做转换,("1"=== 1) 返回为 false,两者类型不一致,则结果不同!
逻辑赋值
逻辑与赋值
(Logical AND Assignment)- &&=
语法
:x &&= y
解释
:如果x
为真
(truthy),则将y
赋值给x
。
逻辑或赋值
(Logical OR Assignment)- ||=
语法
:x ||= y
解释
:如果x
为假
(falsy),则将y
赋值给x
。
逻辑空赋值
(Logical Nullish Assignment)- ??=
语法
:x ??= y
解释
:如果x
为null
或undefined
,则将y
赋值给x
。
类型转换
类型转换,通常就是将一种基本类型,转换成另一个基本类型,常用在
字符串转数字
的场景!
"123" + 123 // 123123 不会进行算数运算,直接当作两个字符串进行拼接
转换 字符串 为数字
+"123" + 1 // 124 在字符串前方加入 + 号会做隐式转换为数字
作为
"-"
号运算符时,例如:
"12" - 1 // 11 会默认将 字符串12 转换为数字12 在做运算
条件判断
条件判断
:就是根据表达式
返回(true/false
)分别进入不同分支代码
块进行运行结果!
if语句
语法
if( true ){
// 条件为 true 时
} else {
// 条件为 false 时
}
if(languageScore > mathScore){
// 语文 大于 数学时 进入该分支逻辑
console.log("语文成绩大于数学成绩!")
} else if(languageScore < mathScore){
// 语文 小于 数学时 进入该分支
console.log("语文成绩小于数学成绩!")
}
switch语句
var languageScore = 50;
switch (languageScore) {
case 10:
console.log("匹配到 10!");
break;
case 20:
console.log("匹配到 20!");
break;
case 50:
console.log("匹配到 50!");
break;
default:
console.log("默认")
}
循环语句
let __arr_ = [1, 43, 5, 65, 6, 3]
for (let i = 0; i < __arr_.length; i++) {
console.log("__arr_[i]" + __arr_[i]);
}
冒泡排序
let arr = [1, 43, 5, 65, 6, 3]
// arr.length 长度为6 arr.length - 1 长度为5
// for循环 index 下标从0开始算. [1 的 index为0 到最后3 的index为5]
for (let i = 0; i < arr.length - 1; i++) {
// arr.length - i - 1
// [6-0-1 = 5]
// [6-1-1 = 4]
// [6-2-1 = 3]
for (let j = 0; j < arr.length - i - 1; j++) {
// arr[j] = 1;
// arr[j + 1] = 43;
// 判断前一个是否大于下一个数值,满足条件进行数据交换!
if (arr[j] > arr[j + 1]) {
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
console.log("arr", arr)
函数
函数的作用
就是将一些共用的业务逻辑进行抽离
, 在不同的业务场景下进行函数调用处理
!
封装冒泡函数案例
let __arr_ = [1, 43, 5, 65, 6, 3]
function sortArr(arr) {
let sortArr = [...arr];
for (let i = 0; i < sortArr.length - 1; i++) {
for (let j = 0; j < sortArr.length - i - 1; j++) {
if (sortArr[j] > sortArr[j + 1]) {
let temp = sortArr[j];
sortArr[j] = sortArr[j + 1];
sortArr[j + 1] = temp;
}
}
}
return sortArr;
}
let __result = sortArr(__arr_);
console.log("sort before", __arr_, "sort after", __result)
// 排序前[1,43,5,65,6,3] 排序后 [1,3,5,6,43,65]
在
函数体内
中包含,参数对象(arguments)
,动态绑定对象(this),
返回值(return)
,作用域
,以及原型链
等相关知识!
函数声明方式
命名函数
function funName(){
}
let funName = function () {
}
匿名函数
匿名函数
,就是没有命名的函数
,常见的应用场景有,立即执行函数
,回调函数
,闭包
,事件绑定函数
!
立即执行函数:
(function(){ console.log("函数体~") })()
回调函数:
[1,4,undefind,5,6,null].filter(function(item){ return !!item; })
闭包:
function closure(){ let name = "张三"; return function(){ console.log("name", name) return name; } }
事件绑定:
el.onclick = function(){ console.log("我被点击了~") }
立即执行函数
立即执行函数
,就是创建一个独立作用域
的可立即执行
的函数体
!
(function(a,b){
console.log(a + b)
})(1,2);
注意: 立即执行函数,代码结束位置需要加入";"号
,确保上一个代码段执行完毕
!
参数传递
参数
分为:形参
和实惨
!
形参: 是在声明函数的时候,定义的参数叫形参!
实参: 在函数调用时,传递的参数可称为实参数,实际传递的参数!
let funName = function (num1, num2) {
console.log("funName", num1, num2)
}
funName(12, 15);
在函数内部
,有一个arguments
参数对象,改对象包含一组参数列表!
arguments
有自己的length
,但是不是真正的数组,无法使用数组的方法
来处理参数列表对象
!
let funName = function (num1, num2) {
console.log("funName arguments", arguments, arguments.length)
// log funName arguments {"0":12,"1":15} 2
}
funName(12, 15);
返回值
返回值,
就是函数在执行完毕时,向外部返回的一个结果!
使用
return
关键字可以返回一个结果!
let funName = function (num1, num2) {
console.log("funName arguments", arguments, arguments.length)
return num1 + num2;
}
let sumResult = funName(12, 15);
console.log("sumResult", sumResult)
作用域
作用域
就是一个变量可访问范围,
通常在创建一个函数时
,内部会有自己的作用域
,除了自身作用域以外
,还会包含父级作用域
!
局部作用域
:- 在
函数内部
,或者块级儿内部
定义的变量
,且该变量仅能在局部作用域内生效!
- 在
全局作用域
:- 在
全局代码
中定义的变量,可供全局任何地方访问
的变量就是属于全局作用域
!
- 在
let anya = "全局作用域定义的变量";
function funName(){
// 局部作用域 父级
let name = "张三";
let age = 18;
console.log(`funName ${name} ${age}`)
return function (){
// 局部作用域 子级 解析函数时会创建父级作用域 并指向父级作用域
// 如果内部作用域没有定义该变量,且被引用时,则会查找父级作用域
console.log(`return fun ${name} ${age}`)
}
}
作用域链
js引擎
在解析函数
时,会创建内部作用域
,也会有一个父级作用域parentScope!
当在内部作用域内访问一个变量
时,且该变量未在当前作用域中定义
,则会向父级作用域中查找
,直到全局作用域获取到为止
若都没有
,则报错is not defind!
this动态绑定
this
是函数体内
特有的动态绑定对象
,根据函数调用情况
,返回不同this对象
!
this
有四种绑定
方式分别为:默认绑定
隐式绑定
显示绑定
new绑定
!
默认绑定
:- 在
全局中定义函数
时,并在全局中调用该函数
时,this
会指向 window 全局
对象!
- 在
隐式绑定
:- 当
函数作为某个对象中的方法
时,通过对象的方式来调用该函数
,这时,this
会指向该对象
!
- 当
显示绑定
:- 在
js
中有三种方法可以修改函数的this指向
,分别是apply call bind
,该方法是函数中特有的,用来修改函数
中的this
指向!
- 在
new绑定
:new
是创建构造函数
的关键字,创建构造函数时会创建一个空对象
,并将函数的this指向这个空对象
!
字符串
字符串就是被双引号引住的值,例如
"张三", "描述"
!
let str = "Hello, World!";
常用的方法
字符串的长度
:length
: "hello".length
查找字符串
:charAt(index)
: 指定索引位置,返回索引位置所对应的字符!indexOf(substr, start)
: 检索字符串,根据指定字符
,返回该字符的索引位置
!console.log(str.indexOf("World")); // 输出: 7
lastIndexOf(substr, start)
: 返回子字符串最后一次出现的位置
。console.log(str.lastIndexOf("o")); // 输出: 8
includes(searchString, position)
:判断
字符串是否包含指定的子字符串
,返回布尔值
。console.log(str.includes("World")); // 输出: true
startsWith(searchString, position)
:判断
字符串是否以指定的子字符串开始
。console.log(str.startsWith("Hello")); // 输出: true
endsWith(searchString, length)
:判断
字符串是否以指定的子字符串结束
。console.log(str.endsWith("!")); // 输出: true
字符串提取
:slice(start, end)
: 根据开始索引和结束索引,对字符串进行截取,不包含最后索引值!。"hello,world".slice(2,4); // ll
substring(start, end)
: 与slice
相似,不支持负数!substr(start, length)
: 从索引开始位置截取,到指定长度!// 从开始索引第7个位置,截取5个字符串长度! let str = "Hello, World!"; console.log(str.substr(7, 5)); // 输出: "World"
字符串替换
:replace(searchValue, replaceValue)
: 替换字符串中匹配的值
(只替换第一个匹配项
)。let str = "Hello, World!"; console.log(str.replace("World", "JavaScript")); // 输出: "Hello, JavaScript!"
replaceAll(searchValue, replaceValue)
: 替换字符串中所有匹配的值
。let str = "Hello, World! World!"; console.log(str.replaceAll("World", "JavaScript")); // 输出: "Hello, JavaScript! JavaScript!"
大小写转换
:toUpperCase()
: 将字符串转换为大写
。toLowerCase()
: 将字符串转换为小写
。
去空白字符
:trim()
: 去除字符串两端
的空白字符。trimStart()
: 去除字符串开头
的空白字符。trimEnd()
: 去除字符串结尾
的空白字符。
分割字符串
split
: 根据指定字符,将字符串进行分割,返回一个分割后的数组列表!let str = "Hello, World!"; console.log(str.split(",")); // 输出: ["Hello", " World!"]
连接字符串
:concat(str1, str2, ...)
: 连接两个或多个字符串
。
重复字符串
repeat(count)
: 重复字符串指定的次数。
数组
数组是js中数据结构存储之一
,它可以存储任意类型的值
,包括基本类型
,引用类型
,Set,
Map
,等数据结构
!
let testSet = new Set([1, '2', '5', 7])
let testFun = function () { }
let testArr = [testSet, testFun, 12, "张三", true, { name: "张三"} ]
console.log("testArr", testArr)
操作数组方法
unshift
: 在数组的第一项添加
元素!shift
:删除
数组的第一项,并返回删除的元素
!push
: 在数组的最后一项添加
元素!pop
:删除
数组最后一项,并返回删除的元素
!indexOf
:检索
数组中的某一项,若找到则返回该元素在数组中的索引位置
,否则返回-1
!slice
:截取
根据起始索引
和结束索引
位置,对数组进行截取,不包含结束索引位置
!splice
:添加
、删除
或替换
数组中的元素,会改变原始数组
!语法
:array.splice(start, deleteCount, item1, item2, ..., itemX)
start
: 必需。指定开始修改数组的索引位置。如果为负数
,则从数组末尾开始计算
。deleteCount
: 可选。指定要删除的元素数量。如果省略或大于等于
从start
到数组末尾的元素数量
,则删除从 start 到数组末尾的所有元素
。item1, item2, ..., itemX:
可选。要添加到数组中的新元素
,从start
位置开始插入。
includes
: 判断数组中是否包含该元素
,返回Boolean
!concat
: 将两个数组合并
,返回一个新的数组长度!join
: 将数组通过某个字符分割成字符串
!
数组遍历方法
let arr = [1,43,45,56,66,3]
let obj = { name: "张三", age: 18 }
for循环
:for(let i = 0; i < arr.length; i++){ console.log("遍历的每一项 item", arr[i]) }
forEach
:forEach
无法通过break
或return
提前终止循环
。arr.forEach(function(item){ console.log("遍历的每一项 item", item) })
for...in
:主要用于
迭代对象的可枚举属性
!for(let key in obj) { console.log("遍历的每一项 对象的属性", key) }
for...of
:循环用于遍历
可迭代对象
(如数组、字符串、Map、Set
等)的元素。for(let item of arr){ console.log("遍历的每一项 item", item) }
ES6新增的迭代方法
map
:对数组元素进行
逻辑处理
后,返回一个新的数组
,不会影响原来的数组结构!let newArr = arr.map(function(item, index, sourceArr){ console.log(item, index, sourceArr) return item * 2; }) console.log("newArr", newArr)
filter
:对数组元素进行过滤, 返回一个
过滤后新的数组结构
! 会过滤掉undefind
和null
的数据!// 返回一个 boolean,过滤数组中大于 10 的元素! let filterResult = arr.filter((item, index, sourceArr) => { return item > 10; }) console.log("filterResult", filterResult)
some
:判断该数组中,是否有
某些元素
满足条件,满足则返回true
, 否则返回false
;let result = arr.some((item, index, sourceArr) => { return item === 3; }) console.log("result", result) // true
every
:判断数组中
所有元素
满足该条件时,返回true
, 否则返回false
;let arr = [2,2,5,2,3,5]; let arr2 = [3,3,3,3,3]; arr.every(item => item === 2) // false arr2.every(item => item === 3); // true
find
:返回满足条件的那一条数据!
let result = arr.find((item, index, sourceArr) => { return item === 3; }) console.log("result", result) // 3
findIndex
:返回满足条件的那一条数据的
index
索引值!let result = arr.find((item, index, sourceArr) => { return item === 3; }) console.log("result", result) // index 5
对象
在
js
中对象
也是一种数据存储结构
,它是以键值对
形式存储数据
,并通过键来获取值
!
let obj = { name: "张三", age: 19 }
对象的操作
根据
key
来获取value
:console.log("name", obj["name"], obj.name);
设置对象属性值:
obj["name"] = "李四"; obj.name = "李四"
遍历对象属性
for(let key in obj){
console.log("obj[key]", obj[key])
}
内置对象
内置对象
就是js中预制的对象
,像Date日期
Math数学
RegExp正则
对象等 !
全局对象
Object
: 是所有对象的基类
,包含对象的一些操作方法
,例如对象属性是否可枚举,可写
,以及一些属性判断
等方法!Function
: 是所有函数的基类
,提供了一些方法,如call(), apply(), bind()
等。Array
: 用于创建数组对象
,包含一些数组的方法push(), pop(), slice(), splice()
!String
: 用于创建字符串对象
,包含一些字符串相关的方法,如slice(), replace(), toUpperCase()
等!Number
: 用于创建数字对象
,包含一些数字处理相关的方法,如toFixed() toPrecision()
!Boolean
: 用于创建布尔对象
,提供布尔值相关的方法。Date
: 用于处理日期和时间
,提供日期和时间相关的方法,如getFullYear(), getMonth(), getDate()
等!RegExp
: 用于创建正则表达式对象
,提供正则表达式相关的方法,如test(), exec()
等!
错误对象
Error
: 所有错误对象的基类
。try { throw new Error("An error occurred"); } catch (e) { console.log(e.message); // 输出: "An error occurred" }
TypeError
: 表示类型错误
。try { null.f(); } catch (e) { console.log(e instanceof TypeError); // 输出: true }
ReferenceError
: 表示引用错误
。try { console.log(x); } catch (e) { console.log(e instanceof ReferenceError); // 输出: true }
数学对象
Math
: 提供数学常数和函数,如Math.PI
,Math.sin()
,Math.cos()
,Math.random()
等。
console.log(Math.PI); // 输出: 3.141592653589793
console.log(Math.random()); // 输出: 0 到 1 之间的随机数
Math.abs()
: 绝对值,所有负数
转换为正数
!Math.max() Math.min()
: 返回参数中最大值(max)
或最小值(min)
!Math.max(1,5,2) // 5 Math.min(1,5,2) // 1 // 如果没有参数 则是 +infinity 和 -infinity
Math.floor() Math.ceil()
: 向下取整(floor
) 和 向上取整(ceil
)!// 向下取整 Math.floor(3.2) // 3 Math.floor(-3.2) // -4 // 向上取整 Math.ceil(3.2) // 4 Math.ceil(-3.2) // -3
Math.random()
: 随机数 默认 >= 0 小于1// 10 ~ 20 随机数 function random( min, max ){ /* 先算乘除 在算加减 Math.random() 默认为 0 ~ 1 max - min = 10 Math.random() * 10 + min 10 */ return Math.floor(Math.random() * ( max - min ) + min); }
JSON对象
JSON
: 提供将对象转换为 JSON 字符串
和将 JSON 字符串解析为对象
的方法,如JSON.stringify()
,JSON.parse()!
let obj = { name: "Alice", age: 30 };
let json = JSON.stringify(obj);
console.log(json); // 输出: '{"name":"Alice","age":30}'
let parsed = JSON.parse(json);
console.log(parsed); // 输出: { name: "Alice", age: 30 }
DOM
DOM
(文档对象模型)document
主要用来操作 html dom元素
!
例如:
对 元素的创建
删除
以及 元素属性的添加删除
等一系列有关操作!
DOM 不属于javascript中的范畴,是独立的,是w3c标准提供的对html元素一系列操作的接口(api)!
文档中对应的属性
document
: 获取整个html
文档!- 获取文档中的父节点或子节点
parentNode
: 获取元素的父节点
!childNodes
: 获取子节点
!sibling
: 获取同级
节点!
获取元素节点属性
通过
nodeType
属性可以获取元素节点所对应的属性常量值
:
属性 |
数值 |
描述 |
---|---|---|
文档节点(document ) |
9 |
Node.DOCUMENT_NODE |
元素节点(element ) |
1 |
Node.ELEMENT_NODE |
属性节点(attribute ) |
2 |
Node.ATTRIBUTE_NODE |
文本节点(text ) |
3 |
Node.TEXT_NODE |
文档片段(DocumentFragment ) |
11 |
Node.DOCUMENT_FRAGMENT_NODE |
document.nodeType === Node.DOCUMENT_NODE // 9 true
获取元素的方法
<div class = "box"></div>
<div class = "container"></div>
<div id = "app"></div>
根据元素标签
获取
指定
元素标签
返回一组符合元素标签的元素节点集合[HTMLCollection[div]]
!可以
实时获取元素的变化
,若没有符合
的元素,则返回一个空的集合
!
document.getElementsByTagName("div") // HTMLCollection[div]
获取元素集合中某个元素
document.getElementsByTagName("div")[0] // 获取集合中第一个元素节点
根据className
获取
document.getElementsByClassName("div.box") // HTMLCollection[div.box]
根据 id
获取
document.getElementById("#app") // 返回一个唯一符合的元素节点
使用query
选择器
接受一个
css选择器
,返回第一个符合的元素节点
!
document.querySelector(".box") // className
document.querySelector("#box") // id
document.querySelector("div") // tagName
使用queryAll
选择器
与
querySelector
类似,返回的是一个NodeList元素节点集合
!
document.querySelectorAll(".box")
创建元素与属性
创建和删除元素
使用
createElement
方法来创建
一个元素标签
!
let divEl = document.createElement('div');
document.removeChild(divEl); // 删除指定元素
创建元素属性
使用
createdAttribute
方法来创建
一个元素属性
,并返回一个元素节点
!使用
setAttributeNode
给元素设置属性
!
let idAttr = document.createAttribute('id');
divEl.setAttributeNode(idAttr);
使用
setAttribute
和getAttribute
来设置元素的属性
和获取元素的属性
!
el.setAttribute('style', 'display: flex; flex-wrap: wrap; gap: 10px;');
console.log(el.getAttribute('style')); // display: flex; flex-wrap: wrap; gap: 10px;
使用
removeAttribute
可以删除指定的属性!
el.removeAttribute('style');
追加子元素
使用
appendChild
在父元素
节点内部元素之后添加子元素
节点!
let pEl = document.createElement("p");
pEl.innerText = "hello world~";
divEl.appendChild(pEl);
使用
insertBefore
在父元素
节点内部元素之前添加子元素
节点!
Element元素属性
元素属性
就是,可通过Element对象
直接获取或设置对应的属性内容
!
属性 | 描述 | 获取方式 |
---|---|---|
id |
获取元素属性id |
el.id |
className |
获取元素属性的class |
el.className |
classList |
获取元素属性的class 集合 |
el.classList |
innerText |
设置 或获取 元素节点的内容content |
el.innerText 获取 / el.innerText = '新的内容' |
innerHtml |
设置 或获取 元素节点中的html元素内容 |
el.innerHtml 获取 / el.innerHtml = '<p>新的内容</p>' |
console.log("获取id", div.id); // div-id
console.log("获取class", div.className); // div-class
console.log("获取classList", div.classList); // DOMTokenList(1) ["div-class", value: "div-class"]
添加或删除class
属性
add
属性可以添加
一个class
属性,remove
可以删除
一个class
属性!
el.classList.add("new_class_name")
el.classList.remove("new_class_name")
contains
属性可以判断该元素中的class属性列表中是否包含,指定class选择器
!
el.classList.contains('new_class_name');
toggle
如果class列表中不存在,则添加,否则删除
!
el.classList.toggle('new_class_name');
innerHTML属性
innerHTML
可以在页面中添加元素标签以及内容!
el.innerHTML = `<div style="width: 50px; height: 50px; background-color: red;"></div>`
当
循环追加多个标签
时,存在性能问题
,导致页面渲染缓慢
!拼接字符串会引起渲染性能问题!
let startTime = Date.now();
let div = document.querySelector('.test');
for(let i = 0; i < 1000; i++){
div.innerHTML += `<div style="width: 50px; height: 50px; background-color: red; margin-bottom: 10px;"></div>`
}
let endTime = Date.now();
console.log(`生成1000个div耗时${endTime - startTime}ms`); // 生成1000个div耗时372ms左右
处理
性能问题
,可以将添加的元素放在数组当
中,通过join
的方法,将数组转换为字符串
,最后赋值与innerHTML元素
!
let startTime = Date.now();
let arr = [];
let div = document.querySelector('.test');
for(let i = 0; i < 1000; i++){
arr.push(`<div style="width: 50px; height: 50px; background-color: red; margin-bottom: 10px;"></div>`);
}
div.innerHTML = arr.join('');
let endTime = Date.now();
console.log(`生成1000个div耗时${endTime - startTime}ms`); // 生成1000个div耗时1ms
相对于
createElement
方法创建元素
,innerHTML
采用数组方式
来生成元素
时,其效率更高
!
获取元素的位置
包含
元素的宽高
,网页可见宽高
以及body的宽高
,用来配合元素在页面中的位置计算
!
元素偏移量(offset)
offset
系列
offset(偏移量):
获取
元素的自身的宽高
还有获取当前
元素的距离定位的父元素距离位置!
常用的属性
属性 | 描述 |
---|---|
el.offsetParent |
获取带有定位(绝对定位 和 相对定位)的父元素 ,如果没有定位 ,则获取body ! |
el.offsetLeft |
获取元素距离带有定位的父元素的左侧距离 |
el.offsetTop |
获取元素距离带有定位的父元素的顶部距离 |
el.offsetHeight |
获取元素自身的高度 ,内边距 边框 以及内容区的高度 ! |
el.offsetWidth |
获取元素自身的宽度 ,内边距 边框 以及内容区的宽度 ! |
offsetParent: 带有定位的父元素,没有则获取 body!
parentNode: 不管父元素有没有定位,都是最近一层上级元素!
offset
和 style
的区别
offset:
是只读
属性,无法进行赋值,只能用来获取元素的相关属性!
style:
是行内样式
,只能获取和修改元素的行内样式
!
获取在盒子内部的x
和y
坐标
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>获取鼠标在盒子内部的坐标</title>
<style>
.box{
width: 200px;
height: 200px;
background-color: #ccc;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
const box = document.querySelector('.box');
box.addEventListener('click', function(event){
/* const x = event.clientX - box.offsetLeft;
const y = event.clientY - box.offsetTop; */
const x = event.pageX - box.offsetLeft;
const y = event.pageY - box.offsetTop;
console.log(`鼠标在盒子内部的坐标为:(${x}, ${y})`);
})
</script>
</body>
</html>
拖动元素案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>拖拽盒子</title>
<style>
:root, body {
width: 100%;
height: 100%;
}
:root {
margin: 0;
padding: 0;
}
.box {
width: 500px;
height: 300px;
background-color: #fcf;
border-radius: 15px;
box-shadow: 0 0 10px #999;
position: fixed;
top: 50%;
left: 50%;
/* margin: auto; */
transform: translate(-50%, -50%);
cursor: move;
}
</style>
</head>
<body>
<div class="box"></div>
<script>
const box = document.querySelector('.box');
// 当鼠标按下时
box.addEventListener('mousedown', function(event) {
const innerX = event.pageX - box.offsetLeft;
const innerY = event.pageY - box.offsetTop;
// 当鼠标在 文档页面 上移动时
document.addEventListener('mousemove', move);
// 当鼠标在 文档页面 上抬起时
document.addEventListener('mouseup', up);
function move(event) {
console.log(box.offsetLeft)
box.style.left = event.pageX - innerX + 'px';
box.style.top = event.pageY - innerY + 'px';
}
function up() {
document.removeEventListener('mousemove', move);
document.removeEventListener('mouseup', up);
}
});
</script>
</body>
</html>
innerX
和innerY
是鼠标
在box元素内部
触发事件时到左侧的一个距离
!
move
触发移动事件
时,需要重新计算
出,box
元素的距离body top
和left
值!
计算规则
:(pageX, pageY or clientX ,clientY )
获取鼠标
距离左侧
和顶部
的坐标值(x,y)
减去
,鼠标
距离box元素
的内部坐标值(x,y)!
内部鼠标距离元素 left 和 top 的距离,通过 pageX - offsetLeft 和 pageY - offsetTop!
client系列
获取元素的
边框大小
,以及元素的大小
!
属性 | 描述 |
---|---|
el.clientLeft |
返回左边框 的大小! |
el.clientTop |
返回上边框 的大小! |
el.clientWidth |
返回元素的宽度,包含 padding 和 内容的宽度 ,不包含border ! |
el.clientHeight |
返回元素的高度,包含 padding 和 内容的高度 ,不包含border ! |
scroll系列
可以获取元素滚动时的距离!
属性 | 描述 |
---|---|
e.scrollTop |
返回上层被滚动隐藏元素的距离 ! |
e.scrollLeft |
返回左侧被滚动隐藏元素的距离 ! |
e.scrollWidth |
返回自身实际的宽度,不包含border ! |
e.scrollHeight |
返回自身实际的高度,不包含border ! |
在窗口下滚动被卷去的元素距离
window.pageXOffset:
获取在窗口内滚动
被卷去的左侧距离!
window.pageYOffset:
获取在窗口内滚动
被卷去的顶部距离
!
兼容问题 : >= ie9
否则使用document.body.scrollTop
如果是在
元素内部
滚动,请使用el.scrollTop
和el.scrollLeft!
滚动案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>滚动案例</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
div:not(.box, .back-top, .top) {
width: 85%;
border-radius: 10px;
margin: 15px auto;
text-align: center;
font-weight: bold;
font-size: 24px;
color: white;
}
div.box {
position: relative;
}
div.header {
height: 100px;
line-height: 100px;
background-color: cadetblue;
}
div.banner {
height: 400px;
line-height: 300px;
background-color: darkcyan;
}
div.main {
height: 1200px;
line-height: 800px;
background-color: darkslategray;
}
.back-top {
width: 50px;
height: 100px;
border: 1px solid red;
position: absolute;
top: 300px;
right: 4.6%;
background-color: tomato;
border-radius: 5px;
font-size: 14px;
font-weight: bold;
color: #fff;
line-height: 100px;
text-align: center;
}
.back-top .top {
cursor: pointer;
opacity: 0;
transition: all 0.3s ease;
}
</style>
</head>
<body>
<div class="box">
<div class="header">header</div>
<div class="banner">banner</div>
<div class="main">main</div>
<div class="back-top">
<div class="top">Top</div>
</div>
</div>
<script>
/*
1. 判断 header 是否在可视范围内
2. 设置 backTop 为 fixed 重新计算 top 值
*/
const header = document.querySelector(".header");
const banner = document.querySelector(".banner");
const main = document.querySelector(".main");
const backTop = document.querySelector(".back-top");
const headerTop = header.offsetTop;
const bannerTop = banner.offsetTop;
const mainTop = main.offsetTop;
const backTopOffset = backTop.offsetTop - bannerTop;
document.addEventListener("scroll", function (e) {
if (window.pageYOffset >= bannerTop) {
console.log("header 隐藏");
backTop.style.position = "fixed";
backTop.style.top = backTopOffset + "px";
} else {
console.log("header 显示");
backTop.style.position = "absolute";
backTop.style.top = 300 + "px";
}
// 滚动到主内容区域
if (window.pageYOffset >= mainTop) {
console.log("main 显示");
backTop.children[0].style.opacity = 1;
} else {
console.log("main 隐藏");
backTop.children[0].style.opacity = 0;
}
});
backTop.addEventListener("click", function (e) {
window.scrollTo({
top: 0,
left: 0,
behavior: "smooth",
});
})
</script>
</body>
</html>
总结
offset
和client
以及scroll
都可以获取元素本身的宽高
,除了offset
获取宽高包含boder
以外,其它两个都不包含border
!
设置css
样式
使用
setAttribute
来设置样式:el.setAttribute("style", "width: 120px; height:120px; background: red;")
使用
style
属性来设置:el.style.width = "120px"; el.style.color = "red";
使用
cssText
属性来设置:el.style.cssText = "width: 120px;", + "height: 120px;", + "background: red;",
事件操作
事件
就是一种在网页元素中的操作行为
,例如鼠标事件
,滚动事件, 键盘事件
等!
鼠标事件:
常见的有点击事件
,滚轮事件
,以及鼠标移入移出的事件
!
键盘事件:
就是键盘的按键触发事件
的一种行为操作
!
let btn = document.querySelector(".btn");
btn.onclick = function( e ){
console.log("触发点击事件!", e)
}
以上就是一个
按钮点击事件
的案例!
注册事件
注册事件
,就是给元素添加事件
的方式,一种是传统方式注册
,一种是对事件的一个监听
!
传统方式注册
在元素上
通过事件绑定属性
来注册一个事件:<button onclick = "alert("hello")"></button>
在j
s代码中通过元素对象
,获取事件属性
并赋值与函数来注册事件:el.onclick = function(){ }
传统事件注册
,只能给元素绑定一个事件
,若出现多次绑定
的情况,则后面的绑定事件会覆盖之前的
绑定!
btn.onclick = function( e ){
console.log("触发点击事件!", e)
}
btn.onclick = function( e ){
console.log("我会覆盖之前的事件绑定", e)
}
事件监听器
事件监听器
就是通过addEventListener
该方法来注册一个事件
!
addEventListener:
该方法存在兼容性问题,IE9之前
不支持,添加事件需使用attachEvent(非标准,生产环境不推荐使用);
参数:
事件监听的类型(
type
):click mouseover mouseout
等...,这里不需要写on
!事件监听的回调函数(
callback
): 当触发事件
时,该回调函数被执行
!
el.addEventListener('click', function(e){
console.log(e.target);
})
el.addEventListener('click', function(e){
console.log(e.target);
})
给某个元素
添加事件监听
!这里同一个元素
可以添加多个事件监听,且依次执行
!
attachEvent事件注册
事件类型
,需要补充on语法!
el.attachEvent('onclick', function(e){
console.log(e.target);
})
删除事件
删除事件 使用该方法 removeEventListener,可以对事件进行解绑和删除!
传统
事件绑定
的删除方式
:el.onclick = null;
使用
监听器
绑定事件后进行解绑
:// 绑定监听的函数 let bindFunction = functon(){ } el.addEventListener("click", bindFunction); // 解除绑定函数 el.removeEventListener("click", bindFunction);
使用
attchEvent
方式绑定的事件进行解绑:let bindFunction = functon(){ } el.attachEvent("onclick", bindFunction); // 解除绑定函数 el.detach("onclick", bindFunction);
DOM事件流
事件流
就是一个传播过程
,当给元素绑定事件时
会有以下流程:
捕获阶段
: 由根元素
到子元素
的一个过程,直到发现被绑定事件的子元素
!
冒泡阶段
: 由子元素
到根元素
的一个过程,直到根元素
为止!
事件捕获
addEventListerer
第三个参数为Boolean
,为true
时,则为捕获阶段
,为false
时或默认
时,则为冒泡阶段
!
捕获阶段:
事件的执行顺序
,会从根元素
开始, 从外
而内
,进行事件触发执行
!
<div class="parent">
<div class="sub">sub</div>
</div>
let parent = document.querySelector('.parent');
let sub = document.querySelector('.sub');
parent.addEventListener('click', function(e){
console.log("parent click");
}, true);
sub.addEventListener('click', function(e){
console.log("sub click");
},true);
// log parent click
// log sub click
冒泡阶段
冒泡阶段
: 事件的执行顺序
,会从子元素
开始,从内
而外
,进行事件触发执行
!
设置冒泡
行为,第三个参数为false
时,或者默认不传
时,则为冒泡
!
let parent = document.querySelector('.parent');
let sub = document.querySelector('.sub');
parent.addEventListener('click', function(e){
console.log("parent click");
}, false);
sub.addEventListener('click', function(e){
console.log("sub click");
},false);
// log sub click
// log parent click
事件对象
事件对象(event)
,就是每一个事件类型
所触发后
产生的一个对象
!
鼠标事件
触发后,事件对象会包含该鼠标对象
的信息!
键盘事件
触发后,事件对象会包含改键盘对象
的信息!
el.onclick = function ( event ) {
console.log("包含了click点击事件的信息", event);
}
event
是一个对象
,包含了,当前触发事件
的一系列信息!
注意: event
在 ie678
里存在兼容问题!
可以通过window.event
来获取!
el.onclick = function ( event ) {
console.log("包含了click点击事件的信息", event || window.event);
}
this
绑定对象 与 el.target
对象的区别
this
是绑定对象
,是当前绑定事件
的元素对象
!
el.target
是获取返回触发事件的元素对象
信息!
<ul>
<li>sub</li>
<li>sub</li>
<li>sub</li>
<li>sub</li>
<li>sub</li>
</ul>
<script>
let ul = document.querySelector('ul');
// this = ul 当前绑定元素
// el.target = 触发事件的元素 比如我点击了 li 则 li为触发元素的对象!
ul.addEventListener('click', function(e){
console.log("ul click", e.target, this);
})
</script>
el.target
是有兼容问题,在ie678
下可以通过el.srcElement
来获取
阻止默认事件
阻止默认事件(preventDefault方法)
,默认事件就是某个元素在触发事件时,一些默认行为操作
!
表单提交 button type="submit",提交后,会默认提交以及刷新浏览器的行为!
<form action="http://www.baidu.com">
<button type="submit">提交</button>
</form>
<script>
let btn = document.querySelector('button');
btn.addEventListener('click', function(e){
e.preventDefault(); // 阻止 默认提交行为操作 以及浏览器刷新操作!
console.log('提交');
})
</script>
preventDefault 兼容问题:
ie678
请使用:e.returnValue!
这是一个属性不是一个方法!
btn.onclick = function(e){
// e.preventDefault(); // 兼容问题
// e.returnValue;
return false;
}
// 三种方式都可以阻止默认事件
阻止冒泡事件
冒泡事件
,从子元素开始,向上层逐个事件触发
,为了保证事件触发的单一行
,需要进行冒泡行为阻止
!
stopPropagation
该方法,可以帮我们阻止事件传播过程
!
el.onclick = function(e){
e.stopPropagation(); // 兼容问题
e.cancelBubble = true; // 兼容 ie678
}
事件委托
事件委托
,就是不在子元素上进行一个一个事件绑定
,通过事件传播
的行为,将事件绑定到父元素上
,来操作子元素
!
<ul>
<li>1. 事件委托</li>
<li>2. 事件委托</li>
<li>3. 事件委托</li>
<li>4. 事件委托</li>
<li>5. 事件委托</li>
</ul>
<script>
let ul = document.querySelector('ul');
ul.addEventListener('click', function(e){
console.log(e.target.innerText);
})
</script>
事件对象中常见的属性和方法
鼠标事件
事件类型
有很多种,鼠标移入元素 移出元素 点击元素 以及 触发焦点 和 失去焦点事件等类型
!
事件类型 | 事件描述 |
---|---|
onclick |
鼠标点击左键 事件 |
onmouseenter |
鼠标移入自身元素时 触发事件,如果自身元素下有子元素 ,则移入子元素时,不触发 ,因为没有冒泡 ! |
onmouseleave |
鼠标移出 事件,与enter相似 |
onmouseover |
鼠标经过该元素时频繁 触发事件,如果自身元素下有子元素 ,则鼠标移入时依旧触发事件 ! |
onmouseout |
鼠标移出 事件,与over相似 |
onfocus |
鼠标触发焦点 事件 |
onblur |
鼠标失去焦点 事件 |
onmousemove |
鼠标移动 事件 |
onmouseup |
鼠标按键抬起 触发事件 |
onmousedown |
鼠标按键按下 触发事件 |
contentmenu |
鼠标右键按下 触发事件 |
selectstart |
鼠标开始选择 时触发事件 |
ondbclick |
鼠标双击 时触发事件操作 |
禁止鼠标右键菜单
document.addEventListener("contextmenu", function(e){
e.preventDefault();
console.log("contextmenu", e.target);
})
禁止文字选择
document.addEventListener("selectstart", function(e){
e.preventDefault();
console.log("contextmenu", e.target);
})
鼠标事件对象
鼠标事件对象
event
, 当鼠标事件触发
时,产生的MouseEvent事件对象
,包含关于鼠标相关属性信息!
比如,鼠标的
坐标位置
等相关信息!
属性 | 描述 |
---|---|
e.clientX |
当前鼠标指针 相对于 body 可视窗口 x 坐标 ! |
e.clientY |
当前鼠标指针 相对于 body 可视窗口 y 坐标 ! |
e.pageX |
当前鼠标指针 相对于 document(文档 包含滚动区域 的 x 坐标! |
e.pageY |
当前鼠标指针 相对于 document(文档) 包含滚动区域 的 y 坐标! |
e.screenX |
当前元素 相对于 电脑屏幕 x 坐标 ! |
e.screenY |
当前元素 相对于 电脑屏幕 y 坐标 ! |
e.offsetX |
当前鼠标指针 相对于当前元素 的left 距离! |
e.offsetY |
当前鼠标指针 相对于当前元素 的top 距离 ! |
鼠标事件跟随案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>鼠标跟随案例</title>
<style>
body{
height: 3000px;
}
.cursor-box{
width: 30px;
height: 30px;
background-color: #f00;
position: relative;
cursor: pointer;
box-shadow: 0 0 10px #000;
border-radius: 20px;
}
</style>
</head>
<body>
<div class="cursor-box"></div>
<script>
const cursorBox = document.querySelector('.cursor-box');
document.addEventListener('mousemove', (event) => {
cursorBox.style.left = event.pageX + 'px';
cursorBox.style.top = event.pageY + 'px';
});
</script>
</body>
</html>
案例
<!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>
*{
margin: 0;
padding: 0;
/* 设置边框盒 外边距, 内容等的宽高自动计算,加上内边距和边框*/
box-sizing: border-box;
}
.container{
width: 100%;
height: 100vh;
border: 1px solid #000;
display: flex;
flex-wrap: wrap;
gap: 10px;
justify-content: center;
align-items: center;
}
.container div{
width: 100px;
height: 100px;
background-color: sandybrown;
text-align: center;
line-height: 100px;
font-size: 14px;
font-weight: bold;
border-radius: 25px;
transition: all .5s linear;
}
.container div:hover{
transform: scale(1.1);
cursor: pointer;
}
.container div:nth-child(2n){
background-color: skyblue;
}
</style>
</head>
<body>
<div class="container">
<div>click</div>
<div>mouseover</div>
<div>mouseout</div>
<div>mousedown</div>
<div>mouseup</div>
<div>mousemove</div>
</div>
<div class="test"></div>
<script>
let events = ['click', 'mouseover', 'mouseout', 'mousedown', 'mouseup', 'mousemove'];
let container = document.querySelector('.container');
let els = document.querySelectorAll('.container div');
for(let i = 0; i < events.length; i++){
let event = `on${events[i]}`;
els[i][event] = function(){
console.log(event);
}
}
</script>
</body>
</html>
键盘事件
事件 | 描述 |
---|---|
onkeyup |
按键抬起 时触发事件 |
onkeydown |
按键按下 时触发事件 不区分大小写! |
onkeypress |
按键按下 时触发事件 不识别 ctrl shift 箭头 ,区分大小写! |
键盘事件对象
event键盘事件对象
,就是当触发键盘行为操作
时,会生成一个键盘对象(keybordEvent
)
根据keyCode
来判断对应的按键值
keyCode
返回一个ASCLL码值
,每一个按键,且对应一个相应的值!
表单事件
事件 | 描述 |
---|---|
submit |
当表单元素提交 时,触发该事件! |
reset |
当表单元素被重置 时,触发该事件! |
change |
当表单元素发生改变 的时候触发该事件! |
input |
当表单元素输入内容 时触发该事件! |
窗口事件
事件 | 描述 |
---|---|
load |
当页面以及所有资源加载完毕 时触发该事件! |
unload |
当用户离开页面 时触发该事件! |
scroll |
当页面滚动时 触发该事件! |
resize |
当窗口或者元素大小发生改变 时触发该事件! |
触摸事件
事件 | 描述 |
---|---|
touchstart |
当用户开始触摸屏幕 时触发。 |
touchmove |
当用户触摸屏幕并移动 时触发。 |
touchend |
当用户停止触摸屏幕 时触发 |
touchcancel |
当触摸被中断 时触发 |
拖拽事件
拖拽元素时,需要
给拖拽元素增加拖拽属性
,才可以对元素进行拖拽
行为!
draggable="true"
<div id="draggableid" class="drag-box" draggable="true">拖拽元素</div>
<!-- 放置目标区域 -->
<div class="placement-box">请将红色的框拖拽到此处</p>
事件 | 描述 |
---|---|
dragstart |
当用户开始拖动元素 时触发。此事件通常用于初始化拖拽数据,如设置 dataTransfer 对象的数据类型和数据值 |
drag |
当元素被拖动时持续触发 。此事件可以用于实时更新拖拽 过程中的状态,例如显示拖拽进度 。 |
dragenter |
当被拖动的元素进入目标元素时触发 。此事件用于确定目标元素是否可以接受拖拽 的数据 |
dragover |
当被拖动的元素在目标元素上移动时持续触发 。此事件用于更新拖拽过程中的视觉效果 ,例如改变目标元素 的样式 |
dragleave |
当被拖动的元素离开目标元素 时触发。此事件用于重置目标元素的样式 |
drop |
当用户释放鼠标按钮并完成拖放操作时触发 。此事件用于处理拖放的数据 ,例如将拖动的元素插入到目标元素 中 |
dragend |
当拖拽操作结束 时触发,无论是否成功放下 。此事件用于清理拖拽过程中的状态 ,例如重置拖拽元素 的样式 |
拖拽案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>拖拽事件</title>
<style>
div{
padding: 15px;
}
.drag-box {
width: 100px;
height: 100px;
background-color: #f00;
color: #fff;
cursor: move;
border-radius: 10px;
}
.placement-box {
margin-top: 15px;
height: 300px;
border-radius: 10px;
background-color: #4fc3f7;
}
</style>
</head>
<body>
<div id="draggableid" class="drag-box" draggable="true">拖拽元素</div>
<!-- 放置目标区域 -->
<div class="placement-box">请将红色的框拖拽到此处</p>
<script>
const draggable = document.querySelector(".drag-box");
const placement = document.querySelector(".placement-box");
// 开始拖拽
draggable.addEventListener("dragstart", function (event) {
console.log("开始拖拽",event);
event.dataTransfer.setData("text/plain", event.target.id);
event.dataTransfer.effectAllowed = "move";
});
// 拖拽过程中持续触发
draggable.addEventListener("drag", function (event) {
// console.log("正在拖动...");
});
// 进入目标元素时触发
placement.addEventListener("dragenter", function (event) {
event.preventDefault();
console.log("进入目标元素",event);
event.target.classList.add("dragover");
});
// 在目标元素上移动时持续触发
placement.addEventListener("dragover", function (event) {
event.preventDefault();
// console.log("在目标元素上移动",event);
});
// 离开目标元素时触发
draggable.addEventListener("dragleave", function (event) {
event.target.classList.remove("dragover");
// console.log("离开目标元素");
});
// 放下元素时触发
placement.addEventListener("drop", function (event) {
event.preventDefault();
console.log("放下元素",this, event.target);
const id = event.dataTransfer.getData("text/plain");
const draggableElement = document.getElementById(id);
event.target.appendChild(draggableElement);
event.target.classList.remove("dragover");
});
// 拖拽结束时触发
draggable.addEventListener("dragend", function (event) {
console.log("拖拽结束");
});
</script>
</body>
</html>
事件顺序
: 拖拽事件有特定的触发顺序,通常是dragstart -> drag -> dragenter -> dragover -> drop -> dragend
。阻止默认行为
: 在dragover
和drop
事件中,需要调用event.preventDefault()
来阻止默认的拖拽
行为(如打开文件
)。数据传递
: 在dragstart
事件中,可以使用dataTransfer.setData
方法传递数据
,在drop
事件中可以使用dataTransfer.getData
方法获取数据
浏览器兼容性
: 虽然大多数现代浏览器
BOM
BOM(浏览器对象模型) 主要是浏览器内一些相关的api操作!
例如: 对 浏览器窗口对一些操作,如果页面跳转历史记录(history), location, navgitor,本地存储(sessionStorage localStorage cookie)等!
BOM缺乏标准规范
,JS的标准
规范是由ECMA
实现的,DOM 的标准
规范是由W3C
实现的,而BOM
最早是由Netscape浏览器标准
的一部分!
BOM
是浏览器厂商自己
定义的标准,兼容性比较差
!
window全局对象
在
浏览器
中,window
是一个顶层对象
也是全局对象
,可以通过该对象来访问一些基本的webapi
!所有定义在
全局的变量和函数
,都会被绑定在顶层对象window
中!
window.alert(123) // alert(123) window可以省略!
注意:
window
对象中有一个特殊的属性
,window.name
,该name
属性,是在一开始初始化
时,就已经存在的属性
,且默认值为" "
!
window常见的事件
页面加载事件
onload
: 当页面所有资源以及dom元素加载完毕
时调用该回调函数!window.onload = function(){ console.log("页面所有资源以及dom元素以加载完毕!") } window.addListenerEvent("load", function(){ console.log("页面所有资源以及dom元素以加载完毕!") })
DOMContentLoaded
: 当所有dom元素加载完毕
时调用该回调函数,不包含图片 flash 以及css样式表资源
!ie9+ 支持!
- 好处是,
不用等待其他外部引用资源加载完毕
时,处理逻辑操作,当外部资源体积大且加载缓慢
,则只需要处理dom时
可以使用该事件!
document.addListenerEvent("DOMContentLoaded", function(){ console.log("所有的 dom 元素加载完毕时 调用回调函数, 不包含外部资源!") })
pageshow
: 在页面显示时加载事件,无论页面是否缓存!a 连接跳转
返回当前页时触发事件刷新页面
或者强制刷新
触发事件pageshow
会在load 事件后触发
e.persisted
可以判断该是否在缓存中的页面中触发!
火狐浏览器
,在跳转页面并通过浏览记录返回时
,会对原有的页面进行缓存
,缓存后的页面不会触发onload事件
!
window.addListenerEvent("pageshow", function(e){ console.log("在 onload 事件后触发,无论页面是否有缓存!") })
窗口以及元素大小监听事件
resize事件,
当窗口
或者元素大小
发生变化时,触发该事件!
window.onresize = function(e){
console.log("窗口大小发生改变!")
}
定时器
定时器
就是根据指定时间
去执行一段逻辑
!
setTimeOut
根据
指定时间
,在时间结束后
,执行回调
函数!单位为
毫秒
!第一个参数,
回调函数
,第二个参数,指定的时间单位(毫秒)
!
setTimeout(function(){
console.log("3秒钟会执行此回调函数!")
},3000)
setInterval
根据
指定时间
,循环间隔执行回调
函数!单位为
毫秒
!第一个参数,
回调函数
,第二个参数,指定的时间单位(毫秒)
!
setInterval(function(){
console.log("每此间隔3秒钟执行回调函数!")
},3000)
清除定时器
清除定时器
,在一定时机内
,将定时器进行清除
,结束回调函数的执行行为!
clearInterval
是清除定时器(Interval)
的一个函数,且参数为定时器所返回的id值
!
clearTimeout
是清除定时器(Timeout)
的一个函数,且参数为定时器所返回的id值
!
let startTime = Date.now();
setTimeout(()=>{
let endTime = Date.now();
console.log(`执行时间:${endTime - startTime}ms`);
// 5s中清除定时器setInterval
clearInterval(intervalId);
},5000)
let intervalId = setInterval(() => {
console.log("每次间隔2s");
}, 2000);
倒计时案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>定时器</title>
<style>
.timer-box {
height: 90vh;
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
}
.timer-box div {
width: 50px;
height: 50px;
background-color: rgba(0, 0, 0, 0.6);
text-align: center;
line-height: 50px;
font-weight: bold;
color: #fff;
}
</style>
</head>
<body>
<center><h2>倒计时</h2></center>
<div class="timer-box">
<div class="hour">1</div>
<div class="minute">2</div>
<div class="second">3</div>
</div>
<script>
const hour = document.querySelector(".hour");
const minute = document.querySelector(".minute");
const second = document.querySelector(".second");
const futureTime = new Date("2024-11-27 15:00:00").getTime();
// 防止一开始页面加载时,时间显示为空白
countdown();
let timerId = setInterval(countdown, 1000); // 每秒执行一次倒计时
// 时间单位换算
// 1秒 = 1000毫秒
// 1分钟 = 60秒 * 1000毫秒
// 1小时 = 60分钟 * 60秒 * 1000毫秒
// 1天 = 24小时 * 60分钟 * 60秒 * 1000毫秒
// 1周 = 7天 * 24小时 * 60分钟 * 60秒 * 1000毫秒
// 1月 = 30天 * 7天 * 24小时 * 60分钟 * 60秒 * 1000毫秒
// 1年 = 365天 * 30天 * 7天 * 24小时 * 60分钟 * 60秒 * 1000毫秒
function countdown() {
const nowTime = new Date().getTime();
const gapTime = (futureTime - nowTime) / 1000; // 未来时间 - 现在时间 = 剩余时间
// 3600 = 1000 * 60秒 * 60分 1小时
const hourGap = Math.floor(gapTime / 3600); // 剩余小时
const minuteGap = Math.floor((gapTime - hourGap * 3600) / 60); // 剩余分钟
const secondGap = Math.floor(gapTime - hourGap * 3600 - minuteGap * 60); // 剩余秒
hour.textContent = hourGap < 10 ? "0" + hourGap : hourGap;
minute.textContent = minuteGap < 10 ? "0" + minuteGap : minuteGap;
second.textContent = secondGap < 10 ? "0" + secondGap : secondGap;
if (gapTime <= 0) {
clearInterval(timerId);
hour.textContent = "00";
minute.textContent = "00";
second.textContent = "00";
}
}
</script>
</body>
</html>
时间单位换算
1秒 = 1000毫秒
1分钟 = 60秒 * 1000毫秒
1小时 = 60分钟 * 60秒 * 1000毫秒
1天 = 24小时 * 60分钟 * 60秒 * 1000毫秒
1周 = 7天 * 24小时 * 60分钟 * 60秒 * 1000毫秒
1月 = 30天 * 7天 * 24小时 * 60分钟 * 60秒 * 1000毫秒
1年 = 365天 * 30天 * 7天 * 24小时 * 60分钟 * 60秒 * 1000毫秒
function countdown() {
const nowTime = new Date().getTime();
const gapTime = futureTime - nowTime; // 未来时间 - 现在时间 = 剩余时间
// 3600 = 1000 * 60秒 * 60分 1小时
const yearGap = Math.floor(gapTime / (1000 * 60 * 60 * 24 * 365));
const monthGap = Math.floor(gapTime / (1000 * 60 * 60 * 24 * 30));
const weekGap = Math.floor(gapTime / (1000 * 60 * 60 * 24 * 7));
const dayGap = Math.floor(gapTime / (1000 * 60 * 60 * 24));
const hourGap = Math.floor((gapTime / (1000 * 60 * 60)) % 24); // 剩余小时
const minuteGap = Math.floor((gapTime / 1000 / 60) % 60); // 剩余分钟
const secondGap = Math.floor((gapTime / 1000) % 60); // 剩余秒
day.textContent = dayGap;
hour.textContent = hourGap < 10 ? "0" + hourGap : hourGap;
minute.textContent = minuteGap < 10 ? "0" + minuteGap : minuteGap;
second.textContent = secondGap < 10 ? "0" + secondGap : secondGap;
if (gapTime <= 0) {
clearInterval(timerId);
day.textContent = "00";
hour.textContent = "00";
minute.textContent = "00";
second.textContent = "00";
}
}
小时: parseInt(时间戳 / 60 / 60 % 24)
分钟: parseInt(时间戳 / 60 % 60)
秒: parseInt(时间戳 % 60)
js执行机制
js 语言
是单线程
语言,在代码执行
时只能同时处理一件事情
!在
js 代码
中,如果遇到代码运行比较耗时
的逻辑,按照单线程
的管理,只有等待耗时任务完成
时,才能执行下一段
逻辑!因此,为此解决问题,则执
行代码逻辑
分为,同步代码
执行,和异步代码
执行!
同步任务(代码)
同步任务:
代码依次执行
,只有等待上一步代码执行完毕后才会执行下一步代码
,且不能同时
执行其他任务代码
!
异步任务(代码)
异步任务:
代码依次执行
,但是执行代码时,遇到异步任务
代码,会将异步任务回调函数
存放到任务队列
当中,不影响其它代码执行
,当需要异步任务代码执行
时,会从队列中按次序获取相应的回调函数并执行
!
定时器(setTimeout setInterval)
,事件(click mousemove...) 以及网络请求(ajax)
等都属于异步任务
!
执行栈:
此处,会执行同步任务代码
!
console.log(1);
setTimeout(fun, 100);
console.log(2);
任务队列: 此处,会存放异步任务回调函数!
fun
回调函数 |console.log(3)
执行顺序,
首先
执行执行栈
中的同步任务
,当遇到异步任务
时,会将回调函数放入任务队列
当中,然后继续执行同步任务
,至到同步任务执行完毕
后,才会从任务队列中执行异步任务回调函数
!
事件循环
在
执行代码
时,遇到异步代码
,会将异步回调函数存放到任务队列
当中,但是在存放过程
中,有一个异步任务队列处理程序
,主要来判断
,该异步任务回调函数是否需要存放到任务队列当中
!
执行栈
->异步任务处理
->任务队列(执行回调函数后调用)
->执行栈
如上图,
点击事件
,经过异步任务队列处理程序
,不会立即
存放到任务队列
当中,只有点击
的时候,会将异步回调函数添加到任务队列中并执行异步回调函数,执行完毕后,并清空任务队列中的回调函数,待下次新的异步回调函数进入!
当
执行栈
中的同步代码执行完毕
时,执行栈
会反复查看异步任务队列
中,是否有可执行的异步回调函数来执行!
location
location
主要用来处理和解析网页域名路径(URL)!
域名(URL)组成
http://127.0.0.1:5500/dom.html?id=1234591283
协议
://ip 地址
:port
(端口)?query
(查询参数)
常见的属性
属性 | 描述 |
---|---|
location.href |
获取 和设置 整个URL路径 |
location.host |
返回主机的域名 |
location.part |
返回端口号 ,未写返回空的字符串 |
location.pathname |
返回路径 |
location.search |
返回参数 |
location.hash |
返回 hash 路径 # 号后面的锚点 |
常见的方法
方法 | 描述 |
---|---|
location.assign(url) |
与 href 一样,可以跳转页面,会有浏览历史记录 ! |
location.replace(url) |
替换当前页面,不会被浏览历史记录 ! |
location.reload(true) |
重新加载页面 ,相当于 f5 刷新,如果参数 为true , 则为强制刷新 ctrl+f5 |
navigator
navigator
包含了浏览器的一些基本信息
,常用的属性有userAgent,
主要用来获取当前平台的信息
,可以判断是PC
还是移动端H5
!
history
history 对象
,主要用来和浏览器历史记录进行交互
,可以实现浏览记录前进后退跳转
的基本功能 !
back()
:后退
功能!forword()
:前进
功能!go(-1):
跳转
功能,参数如果是1
这是前进
功能,如果是-1
这是后退
功能!
本地存储
本地存储
是一种存储数据的一种方式
,平时我们的数据
在刷新浏览器
或者关闭浏览器
以及标签页
时,数据都会消失
,通过
本地存储
的方式,将数据
存储到浏览器本地内存
中,这样就不会因刷新
或者关闭
的情况下丢失数据
了!
本地存储
,有三种
方式,sessionStorage(会话存储)
localStorage(本地存储)
cookie
!
特性 | sessionStorage |
localStorage |
cooKie |
---|---|---|---|
存储容量 |
5M |
5M |
4kb |
声明周期 |
会话窗口关闭(浏览器或标签页) |
持久存储,至到手动清除 |
设置过期时间 (持久存储),会话结束(cookie) |
与服务器交互 |
不会自动发送到服务器 | 不会自动发送到服务器 | 每次http请求 都会自动发送到服务器 |
用途 |
主要用于在单个会话中存储临时数据 |
主要用于在客户端存储大量数据,如用户设置、缓存数据等 |
主要用于存储用户状态信息,如登录状态、用户偏好设置 等 |
安全性 |
不支持 HttpOnly ,安全性较低 |
不支持 HttpOnly ,安全性较低 |
可以设置 HttpOnly 和 Secure 属性,增加安全性 |
API |
提供简单的 setItem 和 getItem 方法 |
提供简单的 setItem 和 getItem 方法 | 需要使用 document.cookie 进行操作 |
sessonStorage
会话存储
,存储大小5M
,不支持httpOnly
,浏览器窗口关闭
或标签页关闭
时会话结束
!
api | 参数 | 描述 |
---|---|---|
sessionStorage.setItem() |
setItem(key,value) |
设置 存储数据,key为键 ,value为值 ! |
sessionStorage.getItem() |
getItem(key) |
根据key获取存储数据 ,key为键 ! |
sessionStorage.removeItem() |
removeItem(key) |
根据key删除存储数据 ,key为键 ! |
sessionStorage.clear() |
clear() |
清空 存储数据! |
localStorage
本地存储
,存储大小5M(因每个浏览器不一致,大小或更多,且通常最大为5M,最小为4M)
,不支持httpOnly
,手动清除
!
api | 参数 | 描述 |
---|---|---|
localStorage.setItem() |
setItem(key,value) |
设置 存储数据,key为键 ,value为值 ! |
localStorage.getItem() |
getItem(key) |
根据key获取存储数据 ,key为键 ! |
localStorage.removeItem() |
removeItem(key) |
根据key删除存储数据 ,key为键 ! |
localStorage.clear() |
clear() |
清空 存储数据! |
cookie
Cookie
是一种小型文本文件
,由服务器发送到用户的浏览器
,并在用户下次访问
同一网站时由浏览器自动发送回服务器
。Cookie
主要用于存储用户状态信息,如登录状态、用户偏好
设置等。
存储方式
每个
Cookie
都有名称、值、路径、域、过期时间
等属性。
cookie封装
设置 Cookie
function setCookie(name, value, days) { let expires = ""; if (days) { const date = new Date(); date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000); expires = "; expires=" + date.toUTCString(); } document.cookie = name + "=" + (value || "") + expires + "; path=/"; } setCookie('username', 'JohnDoe', 7); // 设置一个名为 'username' 的 Cookie,过期时间为 7 天
获取cookie
function getCookie(name) { const nameEQ = name + "="; const ca = document.cookie.split(';'); for(let i=0;i < ca.length;i++) { let c = ca[i]; while (c.charAt(0)==' ') c = c.substring(1,c.length); if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); } return null; } const username = getCookie('username'); console.log(username); // 输出: JohnDoe
删除cookie
function deleteCookie(name) { document.cookie = name + '=; Path=/; Expires=Thu, 01 Jan 1970 00:00:01 GMT;'; } deleteCookie('username'); // 删除名为 'username' 的 Cookie