JavaScript

JavaScript笔记--持续更新中....

JavaScript

一、输入与输出

输入输出语句:

  • 浏览器弹出信息框

    //格式:alert(输出信息)
    alert('你冲QB吗')
    

image-20220522192753702

  • 浏览器弹出输入框

    //格式:prompt(输出信息,默认提示信息)  可只写一个参数
    prompt('你特么想色色!','不去不去怕了怕了')
    

    image-20220522193121038

  • 浏览器控制台打印输出信息(面向开发者)

    //格式:console.log(输出信息)
    console.log('张三已转账5000');
    

    image-20220522193600083

二、变量

变量在使用时分为两步:

  • 声明变量
  • 赋值

var是一个js关键字,用来声明变量(注:var全称variable变量的意思),使用该关键字声明变量后,计算机会自动为变量分配内存空间。

/*声明变量*/
var name;
/*赋值*/
name = 'liuyu'
/*查看控制台是否有打印输出*/
console.log(name);

变量的初始化

声明变量与赋值,可以在一起书写,如下:

var name = 'liuyu'

声明多个变量

var name = 'liuyu',age = 21,addr = 'shanghai';是、
console.log(name,age,addr);
/*
逗号隔开即可,不需要再重复写很多var,但其实声明变量是,不加var也可以的。
*/

变量命名规范

  • 数字字母下划线组成。
  • 区分大小写,不能以数字开头,不能是关键字,如var、for
  • 变量名必须具有一定的意义。
  • 驼峰命名法,首字母小写,后面单词首字母大写,如:searchVideoList

2.1 let与var的区别

let与var的区别

  • var声明可是先使用再声明(不合理)
  • var声明过的变量可以重复声明(不合理)
  • 变量提升、全局变量、没有块级作用域等

所以在以后声明变量,尽量使用let

三、数据类型

Js数据类型整体分为两大类:

  • 基本数据类型

    基本数据类型
    number数字型
    string字符串型
    boolean布尔型
    undefined未定义型
    null空类型
  • 引用数据类型

    引用数据类型
    object对象
    function函数
    array数组

js与python一样,都是弱数据类型,变量到底属于那种类型,只有赋值之后,我们才可以确认。

而像Java这种强数据类型的,就需要在声明变量的时候就指定数据类型,如:

int age = 21

3.1 number数字

数字类型number,可以是整数、小数、正数、负数。

let age = 21;  //正整数
let weight = 65.3;  //小数
let temperature = -10 //负数

在js中,这些数据类型统称为数字类型number。

如何查看当前数据类型

// 如何查看当前数据类型
typeof '变量名';

//声明变量age,并赋值18
let age = 18
//查看当前数据类型,并输出在控制台
console.log(typeof age);
	//控制台输出数据类型为number数字类型
	"number"  

3.1.1 常用方法

汇总一览:

(带括号的表示需要将数据作为参数传入,而非“.”调用)

函数名实现效果
isNaN判断是不是数字

数字转字符串

函数名
toString数字转字符串
String()强制转换为字符串
利用拼接空字符串

利用拼接空字符串

1.判断是不是数字

该方法直接使用并不能准确的判断出结果,后续会更新完善 ----编辑于2022年8月11日

函数名:isNaN

用法:

let test = 123
console.log(isNaN(test));
	//控制台输出:“false”
	"false"

如果是数字为假,如果不是数字为真,注意这是反过来的。

3.1.2 数字转字符串

方法1:

函数名:toString

用法:

let num = 123;
console.log(typeof num.toString())
	//控制台输出:"string"
	"string"

数字类型的变量.toString()

方法2:

函数名:String() 强制转换

用法:

let num = 123;
console.log(typeof String(num))
	//控制台输出:"string"
	"string"

String( 变量 )

方法3:

利用拼接空字符串

用法:

let num = 123;
let num1 = num + ''
console.log(typeof num1)

原理:由于JS是弱数据类型,所以当两个不同数据类型相加时,会当做字符串相加。

'''
需要注意的是,利用拼接的方式实现Int转str,比如是使用的+加号,减乘除都不行。
+加号默认为字符串拼接

-减号 *乘号 / 除号 都是数字运算
'''

3.2 string字符串

只要是用引号引起来的都叫字符串,引号包括单引号( ‘’) 、双引号( “”)或反引号( ` )

Js中推荐使用单引号

//声明字符串变量uname
let uname = 'liuyu'
//控制台打印变量的数据类型
console.log(typeof uname);
	//控制台输出数据类型
	"string"

js中不支持像python一样,可以使用三引号赋值一大段内容。

想要实现相同的效果需要使用“ ` ”反引号。

` 反引号,一般在键盘的左上角,ESC键的附近。

`` 又叫做模板字符串

let uname = `
不可以瑟瑟,哥哥。
不可以呦,不可以。`
console.log(uname,typeof uname);

控制台输出:

不可以瑟瑟,哥哥。
不可以呦,不可以。 string

可以看到,打印完整的内容没有问题,打印数据类型结果是string。

字符串拼接

let a = '1';
let b = '2';
let c = a + b;
console.log(c,typeof c);
	//控制台输出
	"12 string"

let a = '大家好,';
let b = '我是练习时长两年半';
let c = '我叫CXK。'
console.log(a + b + '的个人练习生,' + c);
	//控制台输出
	"大家好,我是练习时长两年半的个人练习生我叫CXK。"

格式化字符串

模版字符串除了可以定义多行文本之外还可以实现格式化字符串操作

格式:

`${变量}内容${变量}.....`

let a = '大家好,';
let b = '我是练习时长两年半';
let c = '我叫CXK。'
console.log(`${a}${b}的个人练习生${c}`);

书写${} 会自动去前面找大括号里面的变量名对应的值 如果没有定义直接报错

如:

var s4 = `my name is ${namemmmmm}`
VM1140:1 Uncaught ReferenceError: namemmmmm is not defined
at <anonymous>:1:24

关于字符串的拼接,在python中不推荐你使用+做拼接,推荐使用join。

而在js中推荐你直接使用+做拼接,如 let c = name + age

模板字符串案例

要求:
实现浏览器输入"XXX",弹窗信息为"AAAXXX"。

在章节1中,我们知道了输入是用的prompt(‘msg’)

那么就有了下列代码

prompt('请输入要表白的人')

此时浏览器有了输入框,那么输入信息点击确定,数据如何展示呢?

console.log(prompt('请输入要表白的人'));

通过控制台打印可得,prompt会接收来自浏览器输入的数据,那么就有了系列代码

let uMsg = prompt('请输入要表白的人')
alert(`我爱你,${uMsg}`)

通过索引取字符串的元素

    1. ​ 方法名: charAt(索引) 返回指定位置上字符的索引号。

    1. ​ 方法名: charCodeAt(索引) 回去指定位置上字符的ASCII码。

    1. H5新增的方式, 字符串[索引]

3.2.1 常用方法

汇总一览:

函数名实现效果
length返回字符串元素个数
trim返回去掉首尾空格之后的字符串
charAt通过索引得元素
indexOf通过元素的索引
concat字符串拼接
slice字符串切片
split字符串分割
replace字符串替换
toUpperCase字符串大写
toLowerCase字符串小写
match提取字符串中值为某某的元素

字符串转数字类型:

函数名/其他实现效果
parseInt数字转字符串,取整数,不保留小数
parseFloat数字转字符串,保留小数
Number字符串强制转换成数字
利用字符串运算

1.计算长度

函数名: length

用法:

let uname = 'liuyu'
alert(uname.length)
	//最终效果为:浏览器弹出窗口,展示数据为5
	"5"

注意:length不加括号

空格也算长度。

2.去除空格

函数名:trim

用法:

let uname = '  liu  yu  '
console.log(uname.trim());
	//最终效果为:控制台打印数据为liu  yu
	"liu  yu"

注意:trim() 需要加空格

trim只能去掉左右两侧的空格

//移除左边的空白:'liu yu '
uname.trimLeft()	

//移除右边的空白:' liu yu'
uname.trimRight()	

3.按索引取值

函数名: charAt

用法:

let uName = 'liu  yu'
console.log(uName.charAt(2));
	//控制台输出:u
	"u"

索引从0开始,按照下列对照。

l i u y u

0 1 2 3 4 5

4.字符串拼接

函数名: concat

用法:

​ 格式: 变量1.concat(变量2,变量3,…)

​ 表示字符串变量1与2、3进行拼接。

let uName = 'liuyu';
let uAge = '21';
let uAddr = 'shanghai'
// 变量uMsg,赋的值为UName+UAge+UAddr拼接而成的
let uMsg = uName.concat(uAge,uAddr);
console.log(uMsg);
	//控制台输出:liuyu21shanghai
	"liuyu21shanghai"

不通数据类型之间可以进行拼接吗?

let uName = 'liu'
let uAge = 719
let uMsg = uName.concat(uAge)
console.log(uMsg,typeof uMsg)
	//控制台输出:liu719 string
	"liu719 string"

答案是可以的,因为Js是弱类型,内部会自动转换成相同的数据类型做操作。

5.字符串切片

函数名: slice

作用:按照索引对字符串取某一段的数据

用法:

​ 格式: 字符串1.slice(起始索引,结束索引)

​ 索引从0开始,特点:顾头不顾腚,结束索引需要+1

let msg = '不可以瑟瑟呦哥哥'
console.log(msg.slice(1,6));
	//控制台输出:可以瑟瑟呦
	"可以瑟瑟呦"

slice(1,6),表示从第二个数字开始,到第六个数字之前结束,不包含第6个数字。

还以上面案例为主,如果要从第二位开始,结束索引为字符串的末尾,那么就应该是7+1=8

如果字符串很长,数不到结尾怎么办?

  • 1.使用length方法查询长度
  • 2.结束索引直接为空
let msg = '不可以瑟瑟呦哥哥'
console.log(msg.slice(1));
	//控制台输出:可以瑟瑟呦哥哥

那如果需要取这段字符串的第二位开始,到末尾的倒数第二位怎么办呢。

  • 1.还是使用length方法查询长度,然后修改下就好。
  • 2.结束索引为负数
let msg = '不可以瑟瑟呦哥哥'
console.log(msg.slice(1,-2));
	//控制台输出:可以瑟瑟呦
	"可以瑟瑟呦"

需要取到倒数第几位,就直接写负几就好。

6.字符串大写

函数名: toUpperCase

用法:

let msg = 'liuyu'
let bigMsg = msg.toUpperCase()
console.log(bigMsg);
	//控制台输出:LIUYU
	"LIUYU"

7.小写

函数名:toLowerCase

用法:

let msg = 'LIUyu'
console.log(msg.toLowerCase());
	//控制台输出:liuyu
	"liuyu"

8.分割

函数名:split

用法:

​ 格式 :split(以什么分割, 展示几位)

​ 默认分割完全部展示

let msg = '哥们哥们,上大学吗哥们,中国地质大学,国字头顶端211,人送外号五道口和尚庙,各式各样的猛男看个够。'
console.log(msg.split(',',2));
	//控制台输出:
	['哥们哥们', '上大学吗哥们']
	0: "哥们哥们"
	1: "上大学吗哥们"
	length: 2

分割完之后,会返回一个数组对象,数据类型就不再是string了。

let test = msg.split(',',2)
console.log(typeof test)
	//控制台输出:object
	"object"

9.通过元素找索引

函数名:indexOf

格式: indexOf( 要查找索引的元素 , 从那个索引处开始 )

用法:

let str = '我要当太空人,我爷爷奶奶可高兴了。'
console.log(str.indexOf('我',2));
	//控制台输出:
	"7"

因为指定了从索引从2开始,所以最终查询到该元素的索引为7.

10.替换字符串

函数名:replace

用法:

let str = 'abca';
console.log(str.replace('a','L'))
	//控制台输出:
	"Lbca"

replace只能替换第一个字符。

案例:将字符串"abandon"中的字符a去掉

let str = 'abandon';
while (str.indexOf('a') !== -1) {
    str = str.replace('a','')
}
console.log(str)
	//控制台输出:
	"bndon"

代码流程:

  • 判断字符串内部没有没有元素值是a的。
  • 然后如果有,那么就进行替换,此时开头的a已经被替换了。
  • 替换过后的数据再次赋值给str,此时abandon已变成了bandon。
  • 循环体代码走完,继续返回上面的判断,如果还有a就接着替换。

11.提取字符串中的所有字母

函数名: match

// 提取变量str1中值为a的元素
let str1 = 'abandon'
console.log(str1.match(/a/))
	//控制台输出:
	"['a', index: 0, input: 'abandon', groups: undefined]"
	
console.log(str1.match(/a/g))
	//控制台输出:
	"['a', 'a']"
  • 前者表示,拿到一个就停止了。
  • 后者为全局匹配,g表示全局模式

3.2.2 字符串转数字

如何将字符串类型转换成数字类型呢

1.parseInt与parseFloat

字符串转数字

//函数名,两个都可以
parseInt()
parseFloat()

//测试
let age = '18.8'
console.log(typeof parseInt(age));
	//控制台输出数据类型为number数字类型
	"number"  

parseInt() 取整数,不保留小数。

parseFloat() 保留小数

注:

  • 只要字符串的开头有数字,那么就可以转,如下:

    let age = '123abc'
    console.log(parseFloat(age));
    	//控制台输出:“123”
    
  • 开头只要不是数字,则输出NaN,表示不是一个数字NOT A NUMBER,如:

    let age = 'abc123'
    console.log(parseFloat(age));
    	//控制台输出:"NaN"
    

2.强制转换

函数名:Number

用法:

let str = '110'
console.log(typeof Number(str))
	//控制台输出:"number"

Number( 变量 )

3.利用运算

用法:

let str = '110'
let str1 = str * 1
console.log(typeof str1)

原理:由于JS是弱数据类型,所以当两个不同数据类型相减、乘、除时,会当做数字运算

'''
需要注意的是,利用拼接的方式实现str转int,比如是使用的减乘除,+加号不行。
+加号默认为字符串拼接

-减号 *乘号 / 除号 都是数字运算
'''

3.3 boolean布尔

1.在python中布尔值是首字母大写的
	True
	False
2.但是在js中布尔值是全小写的
	true
	false
    
3.布尔值是false的有:
	空字符串、0、null、undefined、NaN

null与undefined

"""
null
	表示值为空 一般都是指定或者清空一个变量时使用
		name = 'liuyu'
		name = null
undefined
	表示声明了一个变量 但是没有做初始化操作(没有给值)
	函数没有指定返回值的时候 返回的也是undefined

"""

3.4 arr数组

数组(类似于python里面的列表)

定义数组

var lis = [11,22,33,44,55]
typeof lis  
	//查看类型
	"object"

数组中逗号隔开的数据叫做元素

如何取数组中的元素呢?

var lis = [11,'ABC',11.11,true]
lis[1]
	//控制台输出
	"ABC"
lis[-1]  # 不支持负数索引

索引与字符串一样,都是从0开始。

遍历数组求最大值

思路:

  • 设置一个初始值,如索引为0的元素。
  • 随后拿这个元素与其他元素进行比较。
  • 如果大于初始值,则重新赋值,直到再次遇到更大的值。
  • 或者没遇到更大的,那边代表当前就已经是最大值了。
let lis = [1,3,4,5,1,2,5,1,10,5,7,8,12,]
let maxNum = lis[0]
for (let i = 1; i < lis.length; i++) {
    if (maxNum < lis[i]) {
        maxNum = lis[i]
    }
}

console.log(maxNum)

利用索引来新增数组

let lis = [111,222];
lis[0] = 333;
console.log(lis);
	//控制台输出
	"[333, 222]"

lis[2] = 666;
console.log(lis);
	//控制台输出
	"[333, 222, 666]"

lis[6] = 888;
console.log(lis);
	//控制台输出
	"[333, 222, 666, 空 ã3, 888]"

根据以上代码可得:

  • 当索引已被占用时,会覆盖掉原来的数据。
  • 当索引没有被占用时,会新增数据。
  • 当索引不是连着号时,中间会保留空索引。

3.4.1 常用方法

汇总一览:

函数名实现效果
length查看数组的元素个数
push添加元素,在末尾
unshift添加元素,在开头
shift删除元素,在头部
pop删除元素,在尾部
splice切片删除
slice切片
reverse数组反转
join列表转字符串
concat插入数据
sort从小到大排列
instanceof运算符,用来检测是否为数组
indexOf()、lastIndexOf()通过元素找索引

1.查看元素个数

函数名: length

用法:

var lis = [111,222,333,444,555,666];
console.log(lis.length);
	//控制台输出
	"6"	

2.添加元素(加在末尾)

函数名: push

用法:

let lis = [11,22,33];
lis.push(777);
lis.push([888,999]);   //由于添加的是个数组,所以会被视为一个整体,嵌套在内部
console.log(lis);
	//控制台输出
	"[11, 22, 33, 777, Array(2)]   注:Array(2)展开就是[888,999]"

console.log(lis[4]);
	//控制台输出
	"[888,999]"
	
console.log(lis.length);
	//控制台输出
	"5"

push方法有返回值,返回值为新数组的长度。

push方法可以写多个元素,一次添加多个。

3.添加元素(加在开头)

函数名: unshift

用法:

let lis = [11,22,33];
lis.unshift('AAA');
console.log(lis);
	//控制台输出
	"['AAA', 11, 22, 33]"

unshift的返回值同样是新数组的长度。

4.头部移除元素

函数名: shift

用法:

let lis = [11,22,33];
lis.shift();
console.log(lis);
	//控制台输出
	"[22, 33]"

返回值为删除的那个元素。

5.尾部移除元素

函数名: pop

用法:

let lis = [11,22,33];
lis.pop();
console.log(lis);
	//控制台输出
	"[11, 22]"

pop方法的返回值为:删除的那个元素。

6.切片

函数名: slice

格式: slice( 开始索引,结束索引 )

用法:

let lis = [11,22,33,444];
console.log(lis.slice(1,3));
	//控制台输出
	"[22, 33]"

需要注意,和字符串一样,在切片的时候顾头不顾腚,比如要切:

索引1—到索引3的元素,那么slice(1,4),这里就需要写成4。

注:

  • 结束索引不写默认取到最后。
  • 结束索引可为负数,负数表示从末尾倒着取。

7.列表反转

函数名: reverse

用法:

let lis = [11,22,33,444];
lis.reverse();
console.log(lis);
	//控制台输出
	"[444, 33, 22, 11]"

需要注意,reverse是直接对列表进行操作的,会改变整个列表。

8.列表转字符串

函数名: join

格式 : join( 连接符 )

  • 默认的连接符为逗号。

用法:

let lis = [1,2,3,4,5,6,7];
//可以切片拼接成字符串
console.log(lis.slice(3,6).join('-'));
	//控制台输出
	"4-5-6"

还有一种转字符串的方法,如下:

属性名: toString()

用法:

let arr = [1,2,3];
console.log(arr.toString(),typeof arr.toString()); 
	//控制台输出
	"1,2,3 string"

9.插入数据

函数名: concat

用法:

let lis = [1,2,3,4,5,6,7];
lis.concat([1111,2222,3333]);

lis = lis.concat('AAAA');  //concat会返回添加之后的数组,然后重新赋值给lis数组
console.log(lis)
//控制台输出: [1, 2, 3, 4, 5, 6, 7, 'AAAA']
console.log(lis[7])
//控制台输出: AAAAAAAA

10.从小到大排列

函数名: sort

用法:

let lis = [6,3,9,1,7,5];
lis.sort();
console.log(lis);
	//控制台输出
	"[1, 3, 5, 6, 7, 9]"

默认为从小到大。

从大到小需要在调用sort的时候,传入函数,如:

let lis = [6,3,9,1,7,5];
lis.sort(function (a,b) {
return b - a;
});
console.log(lis);
	//控制台输出
	"[9, 7, 6, 5, 3, 1]"

所以默认的从小到大,其实就是a - b,两个是反过来的。

11.检测是否为数组

instanceof 运算符,用来检测是否为数组

用法:

let arr = [];
console.log(arr instanceof Array);
	//控制台输出
	"true"

H5新增的方法,Array.isArray(参数),用法:

let arr = [];
console.log(Array.isArray(arr))
	//控制台输出
	"true"

返回值为布尔类型。

12.通过元素找索引

函数名: indexOf() lastIndexOf()

用法:

let arr = ['香蕉','橘子','苹果','鸭梨','苹果','菠萝','榴莲'];
console.log(arr.indexOf('苹果'));
console.log(arr.lastIndexOf('苹果'))
	//控制台输出
	"2"
	"4"
	"-1"

indexOf 正向查找,找到了就返回该元素的索引值,不会再接着往下找了。

lastIndexOf 反向查找。

所以同样是查询苹果这个元素的索引值,最后只返回了一个值,而且索引还都不一样。

  • 当索引不存在是,返回负数,-1。

indexOf() 可是设置从第几位索引开始,往后找。

代码如下:

let arr = ['香蕉','橘子','苹果','鸭梨','苹果','菠萝','榴莲'];
console.log(arr.indexOf('苹果',3));
console.log(arr.lastIndexOf('苹果'))
	//控制台输出
	"4"
	"4"

13.切片删除

格式:

  • array.splice(index, howmany, item1, ....., itemX)
    

    index: 从哪里开始删

    howmang:删几位

    item:可选,要添加到数组中的新项目。

代码示例:

let arr = [1,2,3,4,5,6,7,8]
arr.splice(2,3)
console.log(arr)
//控制台输出:
	"[1, 2, 6, 7, 8]"
    //从索引为2的元素开始删除,共删除3位。

3.5 object对象

对象类似于python中的字典

格式: let obj = {key:value,}

示例:

let obj = {'name':'刘煜','age':21,'addr':'上海','school':'中国地质大学(北京)'}

1.如何取对象中的数据

js对象中存放的数据为Key Value键值对,一个值对应一个值,例如name这个key对应的是张三,所以取对象中的数据,是通过key来取value的。

  • 1.通过obj.key的方式

    console.log(obj.name);
    	//控制台输出:“刘煜”
    	"刘煜"
    
  • 2.通过obj[]的方式

    console.log(obj['addr']);
    console.log(obj['school']);
    	//控制台输出:
    	"上海"
    	"中国地质大学(北京)"
    

2.如何给obj赋值

方法: obj.key = value

obj.motto = function () {
return '业精于勤疏于嬉,行成于思毁于随。'
}
res = obj.motto();
console.log(res);
	//控制台输出:
	"业精于勤疏于嬉,行成于思毁于随。"

可利用给key赋值的方式来添加键值对,但是当key存在时,会重写value

obj.name = '夏之悦'
console.log(obj);
	//控制台输出:
	"{name: '夏之悦', age: 21, addr: '上海', school: '中国地质大学(北京)', motto: ƒ}"

因为可以给对象赋值,所以创建对象还有另外一种创建对象的方式

1.先创建一个空object对象

let obj = new Object();

2.赋值

obj.uname = 'liuyu';
obj.age = 21;
obj.addr = 'shanghai';
console.log(obj);
	//控制台输出:
	"{uname: 'liuyu', age: 21, addr: 'shanghai'}"

3.5.1 类

在其他编程语言中,称做class类,在js中叫做构造函数

作用:

  • 当需要有多个对象,且这些对象拥有很多相同属性时,常规的复制粘贴会造成代码冗余,所以可以使用构造函数来快速创建出对象。

格式:

function 构造函数名( 形参1,形参2,形参3.... ) {
    this.形参1 = 形参1;
    this.形参2 = 形参2;
    this.形参3 = 形参3;
	......
}

用法:

function Student(uname,age,sex) {
    this.uname = uname;
    this.age = age;
    this.sex = sex;
    this.motto = function (msg) {
        console.log(msg)
    }
}

let zhangSan = new Student('张三','22','男');
console.log(zhangSan.uname)
zhangSan.motto('劳资就是法外狂徒')
	//控制台打印出张三的姓名,以及座右铭。

let liSi = new Student('李四','21','男');
console.log(liSi.uname)
liSi.motto('张三给劳资等着')
	//控制台打印出李四的姓名,以及座右铭。

let cuiHua = new Student('翠花','18','女');
console.log(cuiHua.uname)
cuiHua.motto('哎呀不要啦,哥哥。')
	//控制台打印出翠花的姓名,以及座右铭。

通过构造函数,快速的创建出三个人物类,这个创建的过程,叫做实例化。

实例化对象时,比如加new,上一章节中,创建空对象,使用的就是new Object()

let obj = new Object();

这个Object()是内置方法,而我们刚刚创建了自己的构造函数,所以 new Student即可。

let zhangSan = new Student('张三','22','男');

3.5.2 遍历对象

使用 for…in 语句。

具体用法:

let student = {'name':'刘煜','age':21,'addr':'上海','school':'中国地质大学(北京)'}

for (let key in student) {
    console.log(key,student[key]);
}	
	//控制台输出:
	"name 刘煜"
	"age 21"
	"addr 上海"
	"school 中国地质大学(北京)"

由此可得,变量key为student中的属性名,通过属性名取值,直接studengt[key]即可取出value

四、运算符

算数运算符,+加、-减、*乘、/除、%取余。

4.1 前置递增

1.前置递增

++num 表示自增加1,即: num = num +1

let num = 10
console.log(++num +10)
console.log(num)
	//结果:21、11
	"21"
	"11"

加号在前表示前置递增,先+1,再+10,最后返回值为21。

由于num加了1,所以再次打印就成了11

4.2 后置递增

2.后置递增

num++ 也是表示自增加1,及: num = num +1

let num = 10
console.log(num++ +10)
console.log(num)
	//结果:20、11
	"20"
	"11"

加号在后面表示后置递增,先把num=10返回,再加10,等于20,然后num再自增加一。

所以 x = i++ +1 其实就是 x = i +1而已,i虽然是自增了,但是不会影响到x的值。

4.3 比较运算符

1 == '1'  
	true  
// 弱等于,内部自动转换成相同的数据类型比较了


1 === '1' 
	false
// 强等于,内部不做类型转换

1 != '1'
	false
// 弱不等于

1 !== '2'
	true
// 强不等于

<= 
>=      
// 弱大于等于

4.4 逻辑运算符

逻辑运算符是用来进行布尔值运算的,返回值也是布尔值,常用用户条件判断

&&  与,and
||  或,or
!   非,not

&&,两侧都为真,结果才为真

||, 有一个是真,那么结果就是真

!, 不是真的,那就是假的,反之一样。

console.log(1>2 && 9<8)
console.log(6>3 || 5<7)
console.log(!true)
	//控制台输出:
	"false"
	"true"
	"false"

注意:

  • 当&与||连用是,先算&&再算||

短路运算

当&& or 的左右两边都是数字或者字符串时,会返回什么值呢。

表达式1 && 表达式2

  • 如果表达式1结果为真(非空字符串和0)时,返回表达式2

  • 如果表达式1结果为假,那么返回表达式1

console.log(1 && 1+3)
console.log(4*6 && 10/5)
console.log(0 && 9)
	//控制台输出:
	"4"
	"2"
	"0"

注意:

  • 当&与||连用是,先算&&再算||

表达式1 || 表达式2

  • 如果表达式1结果为真,那么返回表达式1

  • 如果表达式1结果为假,那么返回表达式2

console.log(1 || 1+3)
console.log(4*6 || 10/5)
console.log(0 || 9)
	//控制台输出:
	"1"
	"24"
	"9"

注意:

  • 当&与||连用是,先算&&再算||

短路运算的作用:

  • 以 1 || num++ 为例,由于表达式1位真,所以返回数字1,此时的Num是不会自增1的。
# 赋值运算符
+=      等同于   num = num + x
-= 		等同于   num = num - x
*= 		等同于   num = num * x

四、流程控制

4.1 if判断

格式:

  • if(条件){条件满足执行的代码}
  • if(条件){条件满足执行的代码} else {其他条件执行的代码}
  • if(条件){条件满足执行的代码} else if(条件){条件满足执行的代码}

用法:

  • 输入年龄,如果在16岁和28岁之间,弹出消息1,反之弹出消息2.
let age = Number(prompt('请输入你的年龄'));
if (age > 16 && age <28) {
    alert('可以深入了解下')
} else {
    alert('那我走?')
}

更多如下:

var age = 21;
if (age>18){
    console.log('来啊来啊')
}


if (age>18){
    console.log('来啊来啊')
}else{
    console.log('。。。')
}


if (age<18){
    console.log("培养一下")
}else if(age<26){
    console.log('哦豁')
}else{
    console.log('1')
}

4.2 三元运算

格式:

  • 条件表达式 ? 表达式1 : 表达式2
  • 如果条件表达式的结果为真,则返回表达式1,反之为2
let num = 7
let res = num > 8 ? '大于8' : '小于8';
console.log(res)
	//控制台输出:小于8
	"小于8"

使用三元运算,可以省略掉一些if判断。

4.3 switch语句

/*
	提前列举好可能出现的条件和解决方式
*/
//格式:
Switch( 表达式 ){
    case value1:
    	//表达式等于value1时要执行的代码
    	break;
	case value2:
    	//表达式等于value2时要执行的代码
    	break;
	case value3:
    	//表达式等于value3时要执行的代码
    	break;
	default:
    	//表达式不等于任何一个values1时要执行的代码
}

注意:表达式和value必须是全等“===”,需要注意数据类型。

案例:

var num = 2;
switch(num){
case 0:
	console.log('喝酒');
	break;  //不加break 匹配到一个之后 就一直往下执行
case 1:
	console.log('唱歌');
	break;
case 2:
	console.log('洗脚');
	break;
case 3:
	console.log('大保健');
	break;
default:
	console.log('条件都没有匹配上 默认走的流程')
}

因为表达式为2,所以当case的值全等于2时,就会执行case内部的代码。

4.4 for循环

'''
格式:
	for(条件){符合条件的执行代码}
'''
# 打印0-9数字
for(let i=0;i<10;i++){
  console.log(i)
}

for循环的执行过程:

  • 先执行计数器变量,let i = 0 ,这句话只执行一次。
  • 再到 i<10 来判断是否满足条件,如果满足就去执行循环体内部的代码,con.log
  • 最后再执行 i++ ,然后再接着到 i<10 进行判断,再执行循环体内部的代码,如此往复。

4.4.1 for循环案例

1.打印倒三角

需求:实现第一行10列“正”字,第二行9列,以此类推。

let str = ''
for (let i=1;i<=10;i++) {
for (let j=i;j<=10;j++) {
str += '正';
}
str += '\n';
}
console.log(str);

思路:

  • 变量 i 每次循环时都会增加,所以 j = i 可以减少内部循环,更快的等于10。

2.九九乘法表

需求:控制台输出九九乘法表

let str = ''

for (let i=1; i<=9; i++) {
	for (let j=1; j<=i; j++) {
	str += `${i}*${j}=`+(i*j)+ '   ';
}

str += '\n';
}
console.log(str)

思路:

  • 九九乘法表的列和行是有规律的,行i=1时为1列内容,当i=2时为2列内容。
  • 所以j可以设置为 j<=i

4.5 while循环

条件成立则一直循环

格式:

while (条件表达式) { 循环体代码 }

var i = 0
while(i<100){
  console.log(i)
  i++;
}

在使用while循环时,要防止死循环,如写上i++等。

4.6 do…while

与while的不同是,do…while不管满不满足条件表达式,都会先执行一遍循环体的代码。

至少会执行一遍循环体内部的代码

格式:

do { 代码 } while ( 条件表达式 )

let i = 1
do {
    console.log('hello world');
    i++;
} while (i<=10);

4.7 其他

continue与break

continue跳出本次循环,进入下一次循环。

break结束本次循环

四、函数

作用:将代码块封装成模块,以便后续多次调用

格式:

function 函数名(形参1,形参2,形参3...){函数体代码}
  • 1.无参函数

    //声明函数func1
    function func1(){
      console.log('hello world')
    }
    //调用函数
    func1()
    	//控制台输出:'hello world'
    	"hello world"
    
  • 2.有参函数

    function func2(a,b){
      console.log(a,b)
    }
    
    func2(1,2)
    	//控制台输出:'1 2'
    	"1 2"
    func2(1,2,3,4,5,6,7,8,9)  
    	//控制台输出:'1 2'
    	"1 2"
    func2(1)  
    	//控制台输出:'1 undefined'
    	"1 undefined"
    
    • 定义函数时的参数a与b为形参,实际调用时的1和2为实参。
    • 函数需要形参,但实参的个数超过个形参,不报错,多余的无效。
    • 实参个数小于形参,多出来的形参会未定义undefined

在日常书写函数时,如下:

function numAvg(lis) {
    let num = 0;
    for (let i = 0; i < lis.length; i++) {
        num += lis[i];
    }
    let avg = num / lis.length
    console.log(avg);
}

let lis = [1,2,3,4,5,6,7]
numAvg(lis)

这是一个很普通的计算数组内数据的平均值,可是在使用过程中,最后的平均值直接输出在控制台的。

但不是所有情况都适用于输出在控制台。

需要使用到return,设定返回值,再由变量接收,后续可以自行输出到控制台或者alert,或者将数值用于其他函数计算。

return返回值

特点:

  • 只能返回一个值,多个值需要返回数组。
  • return后面的代码不执行,直接结束函数。
  • 函数没有return返回undefined未定义

用法:

function numAvg(lis) {
    let num = 0;
    for (let i = 0; i < lis.length; i++) {
        num += lis[i];
    }
    let avg = num / lis.length
    return avg
}

let lis = [1,2,3,4,5,6,7]
let lisAvg = numAvg(lis)
console.log(lisAvg)
	//控制台输出:'4'
	"4"

关键字arguments

作用: 获取到函数接收到的所有实参

用法:

function func(a,b){
	console.log(arguments)  
	console.log(a,b)
}
console.log(func(1,2))
	//控制台输出:
	"[1,2]"
	"1,2"

调用函数时,传入的1和2会被arguments所接收,并形成一个伪数组,拥有者数组的多种方法。

利用关键字arguments进行判断传入参数的个数

function func2(a,b){
  if(arguments.length<2){
    console.log('传少了')
  }else if (arguments.length>2){
    console.log('传多了')
  }else{
    console.log('正常执行')
  }
}

以上定义函数的方式叫做关键字函数,还有一种叫做匿名函数*

匿名函数

格式:

  • let 变量名 = function () {}
var res = function(){
    console.log('哈哈哈');
}

res();

4.1 全局、局部变量

全局变量:

  • 在任何一个地方都可以使用,只有在浏览器关闭时在会被销毁,因此比较占内存。

局部变量:

  • 是有函数内部使用,当所在的代码块被执行时,会被初始化,当代码块运行结束后,会被销毁,因此更节省内存空间。

4.2 预解析

js引擎运行js会分为两步:

  • 预解析
  • 代码执行

预解析:

  • 预解析js引擎会把js里面所有的var还有function提升到当前作用域的最前面。

代码执行:

  • 按照代码书写的顺序从上往下执行。

预解析分为变量预解析(变量提升)和函数预解析(函数提升)

  • 变量提升,就是把所有的变量声明提升到当前的作用于最前面,不提升赋值。
  • 函数提升,就是把所有的函数声明提升到当前作用域的最前面,不调用函数。

预解析案例:

案例1

var num = 10;
fun();
function fun() {
    console.log(num);
    var num =20;
}

由于有预解析的存在,所以上列代码就变成了下列:

var num ;

function fun() {
    var num;
    console.log(num);
    num =20;
}
num = 10;
fun();
  • var和function会先做提升,由于提升的只是变量的声明,所以num=10这个赋值操作就被降到了下面。
  • 函数内部也是一样,声明变量跑到了函数内部的最顶部,但仍然没有赋值,赋值的代码在他原来的位置。
  • 所以最后打印出来的结果为未定义undefined

案例2

f1();
console.log(c);
console.log(b);
console.log(a);
function f1() {
    var a = b = c = 9;
    console.log(a);
    console.log(b);
    console.log(c);
}

由于有预解析,所以实际执行顺序变成了下列:

function f1() {
    var a = 9
    b = 9; c = 9;
    console.log(a);
    console.log(b);
    console.log(c);
}
f1();
console.log(c);
console.log(b);
console.log(a);
  • 需要注意一个地方,var a=b=c=9,其实就是var a=9;b=9;c=9;
  • var a=9,b=9,c=9 逗号隔开挨个赋值,这才是var a=9; var b=9; var c=9;
  • 所以变量b和c是直接赋值,没有声明的,在js中直接赋值的变量为全局变量。
  • 最终控制台输出为 9 9 9 9 9 报错

4.3 箭头函数

# 箭头函数(要了解一下),主要用来处理简单的业务逻辑,类似于python中的匿名函数
var func1 = v => v;  """箭头左边的是形参 右边的是返回值 当有多个形参时,需要用括号括起来"""
等价于
var func1 = function(v){
  return v
}


var func2 = (arg1,arg2) => arg1+arg2
等价于
var func1 = function(arg1,arg2){
  return arg1+arg2
}

4.4 立即执行函数

作用:

  • 创建一个独立的作用域,避免了命名冲突问题

格式:

  • (function(形参) {})(实参)
    
  • (function(形参) {}(实参))
    

代码:

(function(p) {
    console.log(p)
})(123);
	//控制台输出:
	"123"

(function(p) {
    console.log(p)
}(456))
	//控制台输出:
	"456"

五、内置对象

js本身内部自带的一些方法

5.1 Math对象

更多详情需要去MDN文档进行查阅

汇总预览:

方法名作用
Math.abs()返回绝对值
Math.floor()取整,往最小了取
Math.ceil()取整,往最大了取
Math.round()四舍五入
Math.random()返回一个随机数,不需要传参

1.绝对值

方法名: Math.abs( number )

console.log(Math.abs(1))
console.log(Math.abs('-1'))
console.log(Math.abs('str'))
	//控制台输出:
	"1"
	"1"
	"NaN"

隐式转换,会把字符串的-1,转换成数字类型。

当字符串无法通过隐式转换时,返回值为NaN

2.取整,往最小了取

方法名: Math.floor( number )

console.log(Math.floor(1.8))
console.log(Math.floor(-1.8))  //隐式转换
	//控制台输出:
	"1"
	"-2"

floor单词为地板、地面的意思。

向下取整,往最小的取值。

3.取整,往最大了取

方法名: Math.ceil( number )

console.log(Math.ceil(1.8))
console.log(Math.ceil(-1.8))  //隐式转换
console.log(Math.ceil(1.1))
	//控制台输出:
	"2"
	"-1"
	"2"

ceil单词为天花板、上限的意思。

向下取整,往最小的取值。

4.四舍五入

方法名: Math.round( number )

console.log(Math.round(1.8))
console.log(Math.round(-1.8))
console.log(Math.round(1.1))
console.log(Math.round(1.5)) //往大了取
console.log(Math.round(-1.5)) //往大了取
	//控制台输出:
	"2"
	"-2"
	"1"
	"2"
	"1"

注意:

  • 0.5比较特殊,它是往大了取。
  • -1.5取1 1.5取2

5.随机数

方法名: Math.random()

特点:

  • 默认返回一个大于等于0,小于1之间的随机浮点数。
console.log(Math.random())
	//控制台输出:
	"0.7225117498339189"

5.2 Date对象

Date 为构造函数(类),所以使用时需要先实例化对象。

let dateObj = new Date();
//直接打印对象,查看结果
console.log(dateObj)
	//控制台输出:
	"Wed Jun 01 2022 16:52:20 GMT+0800 (中国标准时间)"

在实例化的时候,可以传入参数

let date1 = new Date(2022,6,1,16,54,0)
let date2 = new Date('2022-6-1 16:54:00')
//附: 2022年6月1日为周三
console.log(date1)
	//控制台输出:周五  七月
	"Fri Jul 01 2022 16:54:00 GMT+0800 (中国标准时间)"
console.log(date2)
	//控制台输出:周三  六月
	"Wed Jun 01 2022 16:54:00 GMT+0800 (中国标准时间)"

注意:

  • 推荐使用传入字符串的方式(虽然MDN不推荐…),因为逗号隔开的方式可能会有事件上的偏差。

格式化日期,年、月、日

let dateObj = new Date();
console.log(dateObj.getFullYear());   //当前年份

console.log(dateObj.getMonth());   //当前月份,返回的月份小1个月(因为是从0开始的),需要再+1

console.log(dateObj.getDate());   //当前当日几号

console.log(dateObj.getHours());   //当天几时

console.log(dateObj.getMinutes());   //当天几分

console.log(dateObj.getSeconds());   //当天几秒

console.log(dateObj.getDay()); //当天周几,周一到周六,返回的是1-6,但是周日返回的不是7,而是0

//案例
let year = dateObj.getFullYear();
let mount = dateObj.getMonth() + 1;
let date = dateObj.getDate();
let day = dateObj.getDay();

let lis = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
console.log('今天是:' +year+ '年' +mount+ '月' +date+ '日,' + lis[day] +'。')

	//控制台输出:
	"今天是:2022年6月1日,星期三。"

5.2.1 返回当前总毫秒

let nowTime = +new Date();
	//控制台输出:
	"1654100382077"

该时间戳是,自1970年1月1日到现在的的总毫秒。

倒计时案例

function countDown(time) {
    let nowTime = +new Date();
    let endTime = +new Date(time);
    let times = (endTime - nowTime) / 1000;

    let day = parseInt(times / 60 / 60 /24);
    day = day < 10 ? '0'+day:day;
    
    let hours = parseInt(times / 60 / 60 % 24);
    hours = hours < 10 ? '0'+hours:hours;
    
    let minute = parseInt(times / 60 % 60);
    minute = minute < 10 ? '0'+minute:minute;
    
    let second = parseInt(times % 60);
    second = second < 10 ? '0'+second:second;

    return day + '天'+ hours + '时' +minute+ '分'+second+ '秒'
}

console.log(countDown('2022-6-2 00:40:00'))

思路:

  • 先计算出当前的毫秒数,再计算出倒计时结束的毫秒数,然后相减除以1000得到秒数。
  • 然后有了秒数之后进行取整运算,得到距离结束时间还有多少时间。
  • 当时间为个位数时,补上一个0,如4分钟–>04分钟
let d3 = new Date()
Thu Dec 02 2021 16:29:49 GMT+0800 (中国标准时间)
#查看时间对象的当前详细时间       
d3.toLocaleString()
'2021/12/2 下午4:29:49'
#年月日
d3.toLocaleDateString()
'2021/12/2'
#时分秒
d3.toLocaleTimeString()
'下午4:29:49' 

# 也支持自己手动输入时间
let d4 = new Date('2200/11/11 11:11:11')
d4.toLocaleString()


# 时间对象具体方法
let d6 = new Date();
d6.getDate()  获取日
d6.getDay()		获取星期
d6.getMonth()		获取月份(0-11)
d6.getFullYear()		获取完整的年份
d6.getHours()			获取小时
d6.getMinutes()		获取分钟
d6.getSeconds()		获取秒
d6.getMilliseconds()  获取毫秒
d6.getTime()					时间戳

5.3 JSON对象

作用:

  • 将object对象序列化成字符串。
  • 将字符串反序列化成对象

后端返回的数据通常都是字符串,前端获取到之后,将字符串反序列化成对象,

随后就可以利用对象的一些方法进行取值。

JSON.stringify('obj')     //序列化之后,单引变双引,数据类型变成string
JSON.parse('str')    //反序列化,返回值为对象

验证效果:

let obj = {"name":"jason","age":18}
console.log(JSON.stringify(obj),typeof JSON.stringify(obj))
	//控制台输出:
	{"name":"jason","age":18} string
	


let obj2 = '{"name":"jason","age":18}'
console.log(JSON.parse(obj2),typeof JSON.parse(obj2))
	//控制台输出:
	{name: 'jason', age: 18} 'object'

5.4 RegExp对象

正则表达式:

  • 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符、及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。

在其他编程语言,如python中,如何需要使用到正则表达式,可以借助re模块

但是在js中,需要先创建正则对象。

第一种方式:

let reg1 = new RegExp('(13\d|14[579]|15[^4\D]|17[^49\D]|18\d)\d{8}')

第二种方式:

let reg2 = /(13\d|14[579]|15[^4\D]|17[^49\D]|18\d)\d{8}/

正则表达式对象有了,那要如何校验字符串数据是否满足规则呢?

let reg1 = new RegExp('(13\\d|14[579]|15[^4\\D]|17[^49\\D]|18\\d)\\d{8}')
console.log(reg1.test('1306260693'))
	//控制台输出:
	"false"

该正则表达式为筛选出正确的手机号,而我们reg1.test()里面输入的手机号少了一位,所以是false

六、DOM -1

"""
DOM树的概念:
	所有的标签都可以称之为是节点

JavaScript 可以通过DOM创建动态的 HTML:
JavaScript 能够改变页面中的所有 HTML 元素
JavaScript 能够改变页面中的所有 HTML 属性
JavaScript 能够改变页面中的所有 CSS 样式
JavaScript 能够对页面中的所有事件做出反应
"""	

DOM操作操作的是标签,而一个html页面上的标签有很多 ,所以需要:

  • 先了解如何查找标签
  • 再了解DOM操作标签

DOM操作需要用关键字document起手

6.1 查找标签

通过id查找

get获取,element元素,by通过什么方式。

方法名:getElementById,就是通过id获取元素

  • DOM操作关键字document,连起来就是document.getElementById()
<div id="d1"></div>
<script>
 let id_d1 = document.getElementById('d1')
 console.log(id_d1)
 	//返回div标签
 console.log(typeof id_d1)
 	//类型为object
 console.dir(id_d1)
 	//查看该对象的所有方法
</script>

2.通过标签进行查找

方法名: document.getElementsByTagName()

因为标签不像id一样是唯一的,标签不是唯一的,所以查找的element要加s变复数。

<ul>
<li>111</li>
<li>222</li>
<li>333</li>
<li>444</li>
</ul>
<script>
let tag_li = document.getElementsByTagName('li')
console.log(tag_li)
</script>

	<!--控制台输出:

	HTMLCollection(4) [li, li, li, li]
	-->	

返回的值是个伪数组,因此可以通过 lis[索引]的方式获取数组内的数据。

let li = document.getElementsByTagName('li');
console.log(li[0])
	//控制台输出:
	"<li>
		::marker
"1110"
	</111>
	"

3.通过类名进行查找

方法名:

  • document.getElementsByClassName()
<div class="box"></div>
<div class="box"></div>

<script>
    let box = document.getElementsByClassName('box')
    console.log(box)
    /*控制台输出:
      HTMLCollection(2) [div.box, div.box]*/
    
    console.log(box[0])
    /*控制台输出:
      HTMLCollection(2) [div.box, div.box]
      <div class="box"></div>
    */
</script>

4.间接查找

作用:

  • 通过一个节点,沿着父子级/兄弟关系,拿到其他的标签对象

初始代码如下:

<div class="father">1
    <div class="son">2
        <p></p>
    </div>
</div>

<script>
	let pEle = document.querySelector('p')
    let divEle = document.querySelector('.father')
</script>
  • 拿到父级节点、爷爷级节点

    console.log(pEle.parentElement)
    	//控制台输出: “ <div class="son">...</div> ”
    
    console.log(pEle.parentElement.parentElement)
    	//控制台输出: “ <div class="father">...</div> ”
    
  • 拿到父节点,获取所有的子节点

    console.log(divEle.children[0])
    		//控制台输出: “ <div class="son">...</div> ”
    
    divEle.firstElementChild    //大儿子
    divEle.lastElementChild     //小儿子
    
  • 拿到同级别的下面第一个

    divEle.nextElementSibling  
    
  • 拿到同级别上面第一个

    divEle.previousElementSibling 
    

6.1.1 querySelector

HTML5新增的方法:

  • querySelector,能够返回选择器的第一个元素对象,不需要再[0]了
  • querySelectorAll,返回所有元素对象。

与 getElementsBy…的区别:

  • querySelector不管是通过标签、类、ID都是可以的。
  • 而后者就不行了,需要ById、ByClassName等,根据方式的不同更换方法。
<div class="box">1</div>
<div class="box" id="d2">2</div>
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>

<script>
    let box1 = document.querySelector('.box');
    console.log(box1)

    let box2 = document.querySelector('#d2')
    console.log(box2)

    let allLi = document.querySelectorAll('li')
    console.log(allLi[0])
</script>
<!--
控制台输出:
	<div class="box">1</div>
	<div class="box" id="d2">2</div>
	<li>1</li>
-->

6.1.2 获取body与html元素

1.获取body元素

let body = document.body
console.log(body)

2.获取html元素

let html = document.documentElement
console.log(html)

七、事件 -1

注:

  • 事件章节的内容将分为两段,另外一段在第十章节
  • DOM中间穿插事件,是为了更好的看出效果。

作用: 达到某个事先设定的条件时,自动触发的动作。

绑定事件的两种方式:

  • 1.标签内添加onclick属性。

    <button onclick="func1()">开始大保健</button>
    
    <script>
        function func1() {
            console.log('8号师傅为你服务。')
        }
    </script>
    
  • 2.通过标签查找然后绑定事件。

    <button id="d1">点我</button>
    
    <script>
        let btnEle = document.getElementById('d1');
        btnEle.onclick = function () {
            console.log('9号师傅为您服务')
        }
    </script>
    

案例:实现点击按钮之后,下方的盒子显示当前时间。

<!--css样式-->
<style>
    .times {
        width: 250px;
        height: 40px;
        background-color: skyblue;
        text-align: center;
        line-height: 40px;
    }
</style>

<!--HTML主体-->
<input type="button" value="当前时间" id="btn">
<div class="times"></div>


<!--Js-->
<script>
    let btnEle = document.querySelector('#btn');
    let timeEle = document.querySelector('.times');

    btnEle.onclick = function () {
        timeEle.innerText = getNowTime()
    }

    function getNowTime() {
        let dateObj = new Date();
        let year = dateObj.getFullYear();
        let mount = dateObj.getMonth() + 1;
        let date = dateObj.getDate();
        let day = dateObj.getDay();
        let lis = ['星期日','星期一','星期二','星期三','星期四','星期五','星期六'];
        return  '今天是' +year+ '年' +mount+ '月' +date+ '日,' + lis[day] +'。'
    }
</script>

7.1 常用事件

在上一章节中,使用的是onclick点击事件,但除了点击事件以外,还有其他的事件,以下为常用的几种事件

函数名事件名称及实现效果
click点击事件
focus获得焦点事件
blur失去焦点事件
change改变事件,当对象或选中区的内容改变时触发。
mouseover鼠标进入事件,当用户将鼠标指针移动到对象内时触发。
mouseout鼠标移出事件,当用户将鼠标指针移出对象边界时触发。
submit提交事件
mousemove鼠标移动事件(在10.4的案例中有使用到)
scroll滚动条滚动事件(在13.2章节中有使用到)
mouseenter鼠标进入事件(与mousemove的区别及详情见10.22章节)

注:

  • 本章节的事件绑定方式为传统方式,需要函数名需要加“on”,如onclick、onfocus
  • 具体详情,见第十章节。

1.点击事件

  • 实现效果,当用户点击提交时,控制台输出用户在Input框输入的信息。
<div>
    <input type="text" id="msg">
    <input type="button" id="btn" value="提交">
</div>

<script>
    let msgEle = document.getElementById('msg')
    let btnEle = document.getElementById('btn')
    btnEle.onclick = function () {
        console.log(msgEle.value)
    }
</script>

2.获得焦点事件

  • 实现效果,当选中输入框时执行代码
<input type="text" id="input1">

<script>
    let inputEle = document.getElementById('input1')
    inputEle.onfocus = function () {
        this.value = '想你所想'
    }
</script>

点击input框,自动填充默认值。

3.失去焦点事件

  • 实现效果,当用户输入完信息之后,点击input框以外的地方可以输出输入的信息。
<div>
    <input type="text" id="msg">
</div>

<script>
    let msgEle = document.getElementById('msg')
    msgEle.onblur = function () {
        console.log(msgEle.value)
    }
</script>

4.改变事件

  • 实现效果,当用户输入为非0时,控制台输出刚刚输入的数据。
<div>
    <input type="text" id="msg" value="0">
</div>

<script>
    let msgEle = document.getElementById('msg')
    msgEle.onchange = function () {
        console.log(msgEle.value)
    }
</script>

因为input标签的值默认为value=“0”,所以只要在input输入框内,输入其他的数据。

那么就会在失去焦点时,触发onchange。

5.鼠标进入事件

  • 实现效果,当鼠标移动到盒子上时,触发代码
<div>
    用户名:<input type="text" id="user">
    <br>
    密码:<input type="text" name="" id="pwd">
</div>

<script>
    let userEle = document.getElementById('user')
    let pwdEle = document.getElementById('pwd')
    pwdEle.onmouseover = function () {
        console.log(userEle.value)
    }
</script>

当鼠标移动到输入密码的盒子时,控制台打印输入的用户名,但是将鼠标来回移动时,控制台会打印多次。

6.鼠标移出事件

  • 实现效果,当鼠标移出盒子时,触发代码
<div>
    用户名:<input type="text" id="user">
    <br>
    密码:<input type="text" name="" id="pwd">
</div>

<script>
    let userEle = document.getElementById('user')
    let pwdEle = document.getElementById('pwd')
    userEle.onmouseout = function () {
        console.log(userEle.value)
    }
</script>

当鼠标移出输入用户名的盒子时,控制台打印输入的用户名,但是将鼠标来回进出盒子时,控制台会打印多次。

7.提交事件

  • 实现效果,当表单提交时,执行代码。
<form action="" id="form">
    用户名:<input type="text" id="user">
    <br>
    密码:<input type="text" name="" id="pwd">
    <br>
    <input type="submit" id="btn" >
</form>

<script>
    let userEle = document.getElementById('user')
    let pwdEle = document.getElementById('pwd')

    let formEle = document.getElementById('form')
    formEle.onsubmit = function () {
        alert('提交了,用户名:'+userEle.value+',密码:'+pwdEle.value)
    }
</script>

7.1.1 自动触发事件

例如:自动触发点击事件

document.onclick = function () {
    alert('123')
}
document.click()
//效果:页面并未点击就触发了点击事件函数

八、DOM -2

8.1 表单操作

获取input、select输入内容

  • 标签对象.value
let inputEle = document.getElementById('input')
    msgEle.onblur = function () {
        console.log(msgEle.value)
    }
//当鼠标失去焦点时,打印输入的数据。
<select name="" id="s1">
    <option value="练习">练习</option>
    <option value="时长">时长</option>
    <option value="两年半">两年半</option>
</select>

<script>
    let selectEle = document.getElementById('s1')
    selectEle.onblur = function () {
        alert(selectEle.value)
    }
</script>

禁用disable

let btnEle = document.getElementById('btn')
btnEle.onclick = function () {
    this.disabled = true
}

8.2 获取上传的文件

#如何获取用户上传的文件数据
let fileEle = document.getElementById('d3')
fileEle.value  # 无法获取到文件数据
	'C:\\fakepath\\测试.png' 只能获取到文件路径

#files对象
inputEle.files
	FileList {0: File, length: 1} 
#获取文件数据
inputEle.files[0]
File {name: '测试.png', lastModified: 1638241647821, lastModifiedDate: Tue Nov 30 2021 11:07:27 GMT+0800 (中国标准时间), webkitRelativePath: '', size: 144161,}

8.3 CSS类操作

#获取标签所有的类属性
let divEle = document.getElementsByClassName('col-md-8')[0]
divEle.classList
	//输出结果:DOMTokenList(2) ['col-md-8', 'col-md-offset-2', value: 'col-md-8 col-md-offset-2']

#移除某个类属性
	XXXEle.classList.remove('xxxx')  

#添加类属性(追加)
	XXXEle.classList.add('XXX')  

#验证是否包含某个类属性
XXXEle.classList.contains('XXX')  
	true
XXXEle.classList.contains('XXX')
	false

#有则删除无则添加
XXXEle.classList.toggle('XXX')  

8.4 CSS样式操作

DOM操作操作标签样式,统一先用style起手。

let pEle = document.querySelector('p')
pEle.style.width = '400px'     //设置宽高
pEle.style.height = '400px'
pEle.style.backgroundColor = 'pink'    //设置背景色
pEle.style.border = '3px solid red'   //设置边框

第二种方法

使用上述的第一种方法会有一个弊端,如果这个标签的样式狠多,重复的代码会很多,

需要多写很多遍的 .style

所以,我们可以书写类css,然后通过js进行绑定。

element.className 类名样式操作

直接给标签重新赋值类

  • 注意是重新赋值,会覆盖掉原来的样式。

案例:点击盒子变色

<!--CSS-->
<style>
    .box {
        width: 300px;
        height: 300px;
        background-color: pink;
    }
    
    .change {
        width: 300px;
        height: 300px;
        background-color: skyblue;
    }
</style>

<!--HTML-->
<div class="box"></div>

<!--JS-->
<script>
    let divEle = document.getElementsByTagName('div')[0]
    divEle.onclick = function () {
        this.className =  'change'
    }    
</script>

因为重新给的change类,会重新覆盖掉box的属性,所以我们的change类需要有宽高。

不直接替换,那么就让给该标签多个类即可。

优化:

let divEle = document.getElementsByTagName('div')[0]
divEle.onclick = function () {
    
    //将这两个类名都写上,这样change类就不需要加宽高了,改什么属性就写什么属性即可。
    this.className =  'box change'
}

8.5 修改元素文本内容

方法名:

  • innerText
  • innerHTML

两个方法的异同之处:

相同之处:

  • 都可以获取标签对象的文本内容
  • 但需要注意,只能修改div标签p标签这些普通盒子,input表单是不可以的。
    • 表单标签修改内容的方法是 标签对象.value = ‘’ 禁用是 disabled = true

不同之处:

  • 1.赋值时,一个识别html标签,一个不识得。

    • innerText 当重新给标签对象赋予文本内容时,书写的html代码会原封不动的传过去。

    • innerHTML 与innerText不同,使用innerHTML时,如果文本内容为HTML标签,则会展示对应标签的样式。

      <div class="box"></div>
      
      <script>
      let boxEle = document.querySelector('.box')
      boxEle.innerHTML = '<i>倾斜</i>'
      </script>
      <!--
      	页面上为倾斜状态。
      -->
      
    • 2.返回值不同

      • innerText 获取当前标签对象本身与子级的文本内容
      • innerHTML 获取当前标签对象的内容,以及子级整个HTML标签

返回值不同,检验:

innerText

<div>
<div class="b1">b1
<div>b2</div>
<div></div>
</div>
</div>

<script>
let b1Ele = document.querySelector('.b1')
console.log(b1Ele.innerText)
</script>

<!--控制台输出:
	"b1"
	"b2"   -->

innerHtml

let b1Ele = document.querySelector('.b1')
console.log(b1Ele.innerHtml)

//控制台输出:
	"b1"
		"<div>b2</div>"
		"<div></div>"

可以看到:

  • Text与HTML的区别在于一个只获取文本内容,一个会获取到整个子标签。

8.6 常用元素的属性操作

1.    innerText    innerHtml  改变元素文本内容
2.     src    href    
3.     id    alt   title 

例:

<img src="" alt="" style="width: 200px">
<a href=""></a>

<script>
   let imgEle = document.getElementsByTagName('img')[0]
   imgEle.src = 'https://n.sinaimg.cn/sinakd10116/524/w870h454/20200712/a3cc-iwhseit6543694.jpg'


   let aEle = document.getElementsByTagName('a')[0]
   aEle.innerHTML = '我在这儿~等你'
   aEle.href = 'http://www.mmzztt.com'
</script>

8.7 属性值操作

8.7.1 获取属性值

<div id="box1" index="1"></div>

以上面HTML代码为例,id为属性,box1为值。

那么如何通过属性拿到值呢

  • 第一种方法: element.属性
  • 第二种方法: element.getAttribute(‘属性’)

区别:

  • 第一种方法只能获取内置属性的值,如id,而自定义属性,如index,则获取不到。
let divEle = document.querySelector('div')
console.log(divEle.id)
console.log(divEle.index)
	//控制台输出:
	"box1"
	"undefined"
console.log(divEle.getAttribute('id'))
console.log(divEle.getAttribute('index'))
	//控制台输出:
	"box1"
	"1"

8.7.2 属性及值的操作

1.设置/改变,元素属性值

两种方法:

  • 内置的属性可以通过重新赋值的方式来实现。

    element.属性 = '值'
    
  • 使用setAttribute,通常用来设置或修改自定义属性的值。

    没有该属性则添加,有则更改属性的值

    element.setAttribute('属性','值')
    
    • setAttribute(‘class’,‘nav’) 修改类时,直接写class即可。

约定俗成:

  • 自定义属性的名字,开头最好为data,如 “ data-index = 1 ”

2.删除元素的属性

element.removeAttribute('属性')

8.8 节点操作

作用:

  • 通过一个标签对象,进而拿到他的父级或子级节点,DOM树中,一切都是节点。

拿到父节点(最近的)

格式:

  • node.parentNode
  • node.parentElement (只支持IE,其他浏览器可能有兼容性问题。)
<div class="father">
    <div class="son"></div>
</div>

<script>
    let sonEle = document.querySelector('.son')
    console.log(sonEle.parentNode)
    console.log(sonEle.parentElement)
</script>
<!--
	控制台输出:
	div.father
	div.father
-->

拿到子节点

格式:

  • node.children 拿到该节点下的子标签对象/元素节点
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
</ul>

<script>
    let liEle = document.querySelector('ul')
    console.log(liEle.children)
</script>
<!--
	控制台输出:
	HTMLCollection(4) [li, li, li, li]
-->

如何拿到第一个和最后一个元素节点呢

let liEle = document.querySelector('ul')
console.log(liEle.children[0])
console.log(liEle.children[liEle.children.length -1])
//控制台输出:
	"<li>1</li>"
	"<li>4</li>"

拿到兄弟节点

格式:

  • node.nextElementSibling
<div>
    <div>div</div>
    <span>span</span>
</div>

<script>
    let divEle = document.querySelector('div')
    let div = divEle.children[0]
    console.log(div.nextElementSibling)
</script>
<!--
控制台输出:
	"span"
-->

8.9 动态创建标签

可实现的效果:

  • 通过DOM操作,动态的创建出一个标签,然后给标签添加属性,以及文本

第一步:创建元素节点

格式:

  • document.createElement(‘标签’)
<div>
    <a href="http://www.baidu.com">点我去百度</a>
</div>
let aEle = document.createElement('a')
aEle.innerHTML = '点我去看妹子'
aEle.href = 'http://www.mmzztt.com'

创建a标签节点,并设置默认属性的值。

第二步:添加节点到什么位置

格式:

  • 父节点.appendChild(‘创建的节点’)
//先获取要加入到的节点
let divEle = document.querySelector('div')
//添加
divEle.appendChild(aEle)
	//浏览器页面内容:
	"点我去百度点我去看妹子"

默认插入的位置为末尾

修改插入的位置

格式:

  • 父节点.insertBefore(新建节点,兄弟节点)
    • 兄弟节点表示,要插入那个节点之前。
<div>
    <a href="http://www.baidu.com">点我去百度</a>
</div>
//创建节点
let aEle = document.createElement('a')
aEle.innerHTML = '点我去看妹子'
aEle.href = 'http://www.mmzztt.com'

//添加节点
let divEle = document.querySelector('div')
divEle.insertBefore(aEle,divEle.children[0])
	//浏览器页面内容:
	"点我去看妹子点我去百度"

8.9.1 删除节点

格式:

  • 父节点.removerChild(子节点)
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
let ulEle = document.querySelector('ul')
let  childLiEle = ulEle.children[0]
ulEle.removeChild(childLiEle)
	//控制台输出:
	"2"
	"3"

8.9.2 复制/克隆节点

格式:

  • 节点.cloneNode(布尔值)

深浅拷贝:

  • 浅拷贝

    <div>
        <a href="">我是一个a标签</a>
    </div>
    
    let divEle = document.querySelector('div')
    let aEle = document.querySelector('a')
    //克隆节点
    let cloneAEle = aEle.cloneNode()
    //添加到div内部
    divEle.appendChild(cloneAEle)
    

    浏览器F12检查

    image-20220618185128674

    浅拷贝:只拷贝标签,不拷贝属性及文本。

  • 深拷贝

    //节点.cloneNode(布尔值)  
    //当布尔值为true时,为深拷贝
    let cloneAEle = aEle.cloneNode(true)
    divEle.appendChild(cloneAEle)
    

    浏览器F12检查

    image-20220618201039149

九、DOM -3 案例

9.1 商品全选

实现效果:

jsDOM-案例-1

代码:

<!--引入BootStrap-->
<link rel="stylesheet" href="bootstrap-3.4.1-dist/css/bootstrap.min.css">
<script src="js/jquery.min.js"></script>
<script src="bootstrap-3.4.1-dist/js/bootstrap.min.js"></script>

<!--CSS代码-->
<style>
    .box {
        margin: 100px auto;
        width: 400px;
    }
</style>

<!--HTML代码-->
<div class="box">
    <table class="table table-bordered">
        <thead>
        <tr>
            <th>
                <input type="radio">
                全选
            </th>
            <th>设备名称</th>
            <th>价格</th>
        </tr>
        </thead>


    <tbody>
        <tr>
            <td><input type="checkbox"></td>
            <td>iPad Pro</td>
            <td>5999</td>
        </tr>
        <tr>
            <td><input type="checkbox" ></td>
            <td>iPad Air</td>
            <td>4999</td>
        </tr>
        <tr>
            <td><input type="checkbox" ></td>
            <td>iPhone 13 Pro Max</td>
            <td>8999</td>
        </tr>
        <tr>
            <td><input type="checkbox"></td>
            <td>iPad mini 6</td>
            <td>3999</td>
        </tr>
    </tbody>

    </table>
</div>


<!--JS代码-->
<script>
    let allIptEle = document.querySelector('table').querySelectorAll('input')

    let flag = 'true'
    allIptEle[0].onclick = function () {

        if (flag === 'true') {
            for (let i=1;i<allIptEle.length;i++) {
                allIptEle[i].checked = true
            }
            flag = 'false'
        } else {
            for (let i=1;i<allIptEle.length;i++) {
                allIptEle[i].checked = false
            }
            this.checked = false
            flag = 'true'
        }
    }

    for (let i=1;i<allIptEle.length;i++) {
        allIptEle[i].onclick = function () {
            for (let i=1;i<allIptEle.length;i++) {
                if (allIptEle[i].checked === true) {
                    allIptEle[0].checked = true
                } else {
                    allIptEle[0].checked = false
                    break
                }
            }

        }
    }
</script>

9.2 商品详情页

点击不同的按钮实现局部内容更新,如下图:

JSDOM-案例-2

<style>
    .main_box {
        width: 500px;
        height: 400px;

        margin: 50px auto;
    }

    .main_box ul {
        list-style: none;
        padding: 0;
        display: flex;
        justify-content: space-between;
        border-bottom: 4px solid red;
        margin-bottom: 0;
    }

    .main_box ul li {
        height: 30px;
        line-height: 20px;
        font-size: 18px;
        padding: 5px;
    }

    .items {
        display: none;
    }
</style>

<div class="main_box">
    <ul>
        <li style="background-color: red;">导航条1</li>
        <li>导航条2</li>
        <li>导航条3</li>
        <li>导航条4</li>
    </ul>
        <div class="items" style="display:block;">导航条1的内容</div>
        <div class="items">导航条2的内容</div>
        <div class="items">导航条3的内容</div>
        <div class="items">导航条4的内容</div>
</div>


<script>
    let menuEle = document.querySelectorAll('li')
    let contEle = document.querySelectorAll('.items')

    //循环给每个导航条按钮内容绑定点击事件
    for (let i=0;i<menuEle.length;i++) {
        //设置自定义属性index,稍后作为索引用来去对应的div标签对象
        menuEle[i].setAttribute('index',i)

        //点击菜单按钮变色
        menuEle[i].onclick = function () {
            for (let i=0;i<menuEle.length;i++) {
                menuEle[i].style.backgroundColor = ''
            }
            this.style.backgroundColor = 'red'

            //展示菜单下列对应的内容
            let menuIndex = menuEle[i].getAttribute('index')
            //获取点击按钮的索引号
            for (let i=0;i<contEle.length;i++) {
                if (i == menuIndex) {
                    for (let i=0;i<contEle.length;i++) {
                        contEle[i].style.display = 'none'
                    }
                    contEle[i].style.display = 'block'
                }
            }

        }
    }
</script>

十、事件 -2

给元素添加事件,称为注册事件或者绑定事件
注册事件有两种方式:

  • 传统方式
  • 方法监听注册方式

传统方式:

  • 如第七章节,利用on开头的事件,如onclick
  • 特点:注册事件的唯一性,同一个元素同一事件只能设置一个处理函数,最后注册的函数会覆盖掉前面的。

方法监听注册方式:

  • w3c标准推荐的方式
  • 利用addEventListener()方法实现
节点.addEventListener(type,listener[,useCapture])

参数:

  • type 事件类型字符串,如click、mouseover,但是注意不能带on
  • listener 事件处理函数,事件发生时,会调用该监听函数。
  • useCapture 可选参数,是否使用捕获,值类型为布尔值,默认为false

测试:

传统

<input type="button" value="提交">

<script>
    let iptEle = document.querySelector('input')
    iptEle.onclick = function () {
        alert('提交了')
    }
    iptEle.onclick = function () {
        alert('你在dog叫什么!')
    }
</script>
<!--
	最终效果为:
		浏览器提示第二个处理函数结果,alert提示'你在dog叫什么!'
-->

监听

<input type="button" value="提交">

<script>

    let iptEle = document.querySelector('input')
    
    //类型:点击事件; 处理函数:匿名函数
    iptEle.addEventListener("click", function () {
        alert('提交了')
    })
    iptEle.addEventListener("click", function () {
        alert('你在dog叫什么!')
    })

</script>
<!--
	最终结果为:
		浏览器两个alert弹窗都会依次弹出来。
-->

10.1 解绑事件

作用:

  • 当一个事件触发了之后,可通过解绑事件,来实现后续不会再触发。

一、传统方法

节点.事件类型 = null

二、方法监听方式

节点.removeEventListener(解绑事件的类型,函数名)

例:

传统方法

  • 点击提交按钮之后,事件后续失效。
<input type="button" value="提交">
let iptEle = document.querySelector('input')
iptEle.onclick = function () {
    alert('提交了')
    //可以再添加个禁用按钮
    this.disabled = 'true'
    //解绑事件
    this.onclick = null
}

监听方法

<input type="button" value="提交">
let iptEle = document.querySelector('input')

//这里不能再使用匿名函数了,因为解绑时需要指定函数名。
iptEle.addEventListener('click',rmAlert)

function rmAlert() {
    alert('提交了')
    this.disabled = 'true'
    //解绑事件。
    this.removeEventListener('click',rmAlert)
}

10.2 DOM事件流

什么是DOM事件流:

  • 事件发生时会在元素节点之间,按照特定的顺序传播,这个传播过程即DOM事件流

事件流的两个阶段

  • 事件捕获:事件开始时,由DOM最顶层节点开始,然后逐级向下传播到具体的元素接收过程。
  • 事件冒泡:事件开始时,由具体的元素接收,然后逐级向上传播到DOM最顶层节点的过程。
image-20220621015227805

注意:

  • 在JSdiamante中,只能执行捕获,或者冒泡的其中一个阶段。
  • onclick和attachEvent只能得到冒泡阶段
  • addEventListener方法中,如果第三个可选参数为TRUE,那么表示在事件捕获阶段调用事件处理程序。。
  • 如果第三个可选参数为FALSE(默认值),那么则表示在事件冒泡阶段调用事件处理程序。
  • 实际开发中,很少使用事件捕获,更关注事件冒泡
  • 有些事件没有冒泡,比如onblur、onfocus、onmouseenter、onmouseleave

验证:

1.捕获阶段

<div class="father">
    <div class="son"></div>
</div>
let father = document.querySelector('.father')
let son = document.querySelector('.son')

father.addEventListener('click',function () {
    alert('father')
},true)
//addEventListener(类型、函数、是否使用捕获)
//true表示使用捕获

son.addEventListener('click',function () {
    alert('son')
},true)

最终的效果为:

  • 点击father盒子时,弹窗内容为father
  • 当点击son盒子时,会先弹出father,再弹出son

调用事件处理程序时,为事件捕获阶段,所以当点击son盒子时,会从HTML—>body—>father---->son一层一层的找。

于是便先找到了father这个盒子,也是具有onclick点击事件的,所以才先执行的father。

随后再找到目标盒子,也就是son,然后再执行son内部的函数

2.冒泡阶段

<div class="father">
    <div class="son"></div>
</div>
let father = document.querySelector('.father')
let son = document.querySelector('.son')

father.addEventListener('click',function () {
    alert('father')
},false)
//默认就为false,表示不使用捕获(冒泡)。

son.addEventListener('click',function () {
    alert('son')
},false)

最终的效果为:

  • 点击father盒子时,弹窗为father
  • 当点击son盒子时,会先弹出son,再弹出father

调用事件处理程序为事件冒泡阶段,所以当点击son盒子时,会先找到son盒子的事件并触发函数。

随后从 son---->father---->body—>HTML 一层一层的上去。

于是在执行完son之后,便找到了father这个盒子,也是具有onclick点击事件的,所以就又执行的father。

10.2.1 阻止事件冒泡

利用事件对象阻止冒泡:

  • 这是在10.3事件对象章节中的内容,查看详情请跳转。
  • 本章节为了排版,方便查询,单独拎出来。

阻止事件冒泡:

  • box2.onmouseover = function (event) {
        event.stopPropagation()
    }
    
  • box2.addEventListener('mouseover',function (event) {
        event.stopPropagation()
    })
    

要注意书写的位置,该方法阻止的是向上面冒泡,但是子集冒的泡还是能接收的。

10.2.2 mouseenter

作用:

  • 鼠标移入事件,与mouseover相似

不同之处:

  • mouserover,鼠标经过自身盒子会触发,经过子盒子也会触发,因为经过子盒子时会因为冒泡冒上去。
  • mouseenter,不会冒泡,与之搭配的mouseleave同样不会冒泡

验证代码:

<style>
    .box1 {
        width: 200px;
        height: 200px;
        background-color: pink;
        overflow: hidden;
    }
    .box2 {
        margin: 50px auto;
        width: 100px;
        height: 100px;
        background-color: skyblue;
    }
</style>

<div class="box1">
    <div class="box2"></div>
</div>

<script>
    let box1 = document.querySelector('.box1')
    let box2 = document.querySelector('.box2')

    box1.onmouseover = function () {
        console.log('box1触发')
    }
	box2.onmouseover = function (event) {
    	event.stopPropagation()
	}
    // box1.addEventListener('mouseover',function () {
    //     console.log('box1触发')
    // })
    // box2.addEventListener('mouseover',function (e) {
    //     e.stopPropagation()
    // })
</script>

10.3 事件对象

什么是事件对象:

  • 事件对象是事件的一系列相关数据的集合,包含了如鼠标点击事件的相关信息(鼠标坐标等)
<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
let ulELe = document.querySelector('ul')
ulELe.onclick = function (event) {
    console.log(event)
}
//控制台输出:
"PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0, …}"
//里面就包含了例如鼠标坐标等数据。
  • event就是事件对象,这个对象可以自己命名,如“event”、“evt”、“e”,等。

10.3.1 常见属性和方法

e.target     返回触发事件的对象
e.type      返回事件的类型,如click(没有On)   
e.preventDefault()   该方法阻止默认事件,比如阻止链接跳转
e.stopPropagation()   阻止冒泡
//ie678版本不支持,由于微软2022年6月停止了对ie的支持,所以略。

一、target

作用:

  • 返回触发事件的对象

target与this的区别

target与this

  • 前者是返回触发事件的对象
  • 后者返回的是绑定事件的对象

案例:

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
let ulELe = document.querySelector('ul')
ulELe.onclick = function (event) {
    console.log(event.target)
    console.log(this)
}

最终效果:

  • 点击li标签控制台输出li标签对象和ul标签对象

target返回的是触发事件的对象(谁触发了这个对象),点击那个li,返回的就是那个li标签对象。

而this表示返回绑定事件的对象,因为绑定事件的ul,所以自然返回的就是ul。

  • li没有绑定事件,但是由于默认为事件冒泡,所以仍然会触发点击事件。

二、type

作用:

-  返回事件的类型
<div>1111</div>
let divELe = document.querySelector('div')
divELe.onclick = function (e) {
    console.log(e.type)
}
	//点击div标签,控制台输出
	"click"

三、preventDefault()

作用:

  • 该方法阻止默认事件

案例:

  • 当用户点击a标签时需要进行身份验证,通过了才可以进一步访问。
<a href=>VIP才能看妹子呦~</a>
let aELe = document.querySelector('a')
aELe.onclick = function (event) {
    //禁用a标签的默认事件
    event.preventDefault()
    let user = prompt('请输入用户名')
    if (user === '123') {
        
        //由于没找到如何恢复默认事件,所以就用BOM操作代替,BOM在下一章节
        window.open('http://www.mmzztt.com/','','height=400px,width=400px,top=400px,left=400px')
    }
}

四、stopPropagation()

作用:

  • 阻止冒泡
<div class="father">父级div
    <div class="son">子级div</div>
</div>
let fEle = document.querySelector('.father')
let sEle = document.querySelector('.son')
fEle.onclick = function () {
    alert('father')
}
sEle.onclick = function (event) {
    alert('son')
    //阻止事件冒泡
    event.stopPropagation()
}
	//最终效果:  弹窗内容为“son”
	//点击son盒子,只会触发son绑定的事件,不会再冒泡到上一级了。

10.3.2 事件委托

什么是事件委托:

  • 事件委托也称为事件代理,目的是减少操作DOM,题号程序性能。

事件委托的原理:

  • 子节点不早单独绑定事件,而是将事件把你的那个在父节点上,然后利用冒泡来影响每个子节点。

案例

实现下列效果

Js-事件委托-案例-1

<div>
    <ul>点击变色
        <li>绿色</li>
        <li>蓝色</li>
        <li>粉色</li>
    </ul>
</div>
let ulELe = document.querySelector('ul')

//给父节点绑定事件。
ulELe.onclick = function (event) {
    //ul先取消冒泡,以免div标签也有绑定事件从而触发多余的事件。
    event.stopPropagation()
	
    //如果触发事件的节点,内部文本数据为“绿色”,那么该节点的背景色也为“绿色”。
    if (event.target.innerHTML === '绿色') {
        //调用函数
        rmBgc(ulELe.children)
        //将触发事件节点的背景色进行修改。
        event.target.style.backgroundColor = 'green'
    }

    if (event.target.innerHTML === '蓝色') {
        rmBgc(ulELe.children)
        event.target.style.backgroundColor = 'skyblue'
    }

    if (event.target.innerHTML === '粉色') {
        rmBgc(ulELe.children)
        event.target.style.backgroundColor = 'pink'
    }
    
    
	//利用排他思想,将其他的li背景色都去掉,然后再上色。
    function rmBgc(arr) {
        for (let i=0;i < arr.length;i++) {
            arr[i].style.backgroundColor = ''
        }
    }
}

10.4 鼠标事件

前文中提到,事件对象包含了鼠标的一些信息,那么如何利用已经封装好的方法呢。

10.4.1 获取鼠标xy轴坐标

可视区的x和y坐标

event.clientX
event.clientY

案例:

document.onclick = function (e) {
console.log(e.clientX)
console.log(e.clientY)
}
	//点击页面输出当前坐标。

鼠标在整个页面文档

document.onclick = function (e) {
console.log(e.pageX)
console.log(e.pageY)
}

当文档的高度为1000px时,在页面底部点击盒子,输出的高度为(Y轴)1000px

鼠标点击的坐标= 鼠标到窗口顶部的像素 + 窗口顶部到整个页面顶部的像素。

了解:

  • 鼠标在电脑屏幕的x和y坐标
e.screenX
e.screenY

案例:

实现效果展示:

JS-鼠标事件对象-案例-1

代码:

<style>
    img {
        /*绝对定位,后续控制图片移动时,直接给left、top属性即可*/
        position: absolute;
    }
</style>

<img src="https://ts1.cn.mm.bing.net/th/id/R-C.9458c0435c185976a3e3f13d015ace42?rik=EDNGrGJ03Y28TA&riu=http%3a%2f%2fn.sinaimg.cn%2fsinakd20201013ac%2f416%2fw640h576%2f20201013%2f1d25-kakmcxe4670030.jpg&ehk=rooJzZRqcJiJDD0IfsPuA728IBCZ3INcFY7ixloByBA%3d&risl=&pid=ImgRaw&r=0" alt="" style="width: 100px">
let imgEle = document.querySelector('img')
//mousemove鼠标移动事件,只要鼠标移动1px就触发函数
document.onmousemove = function (e) {
    //获取到鼠标当前坐标
    	//使用page或者client都行
    let x = e.pageX
    let y = e.pageY
    //这里-50和-20是为了稍微居中点,好看
    imgEle.style.left = x -50 + 'px'
    imgEle.style.top = y -20 + 'px'
}

10.4.2 禁止鼠标右键

事件名称:

  • contextmenu

事件对象方法

  • preventDefault() 加括号
<p>2022年6月23日 16:20</p>
//第一种:传统方法
let pEle = document.querySelector('p')
pEle.oncontextmenu = function (event) {
    event.preventDefault()
}
//第二种:监听事件
let pEle = document.querySelector('p')
pEle.addEventListener('contextmenu',function (event) {
    event.preventDefault()
})

10.4.3 禁止鼠标复制

事件名称:

  • selectstart (onselectstart )

事件对象方法

  • preventDefault() 加括号
<p>2022年6月23日 16:23</p>
//第一种:传统方法
let pEle = document.querySelector('p')
pEle.oncontextmenu = function (event) {
    event.preventDefault()
}
//第二种:监听事件
let pEle = document.querySelector('p')
pEle.onselectstart = function (event) {
    event.preventDefault()
}

10.5 键盘事件

10.5.1 按键按下与松开

一、按下键盘触发

  • 事件名称: keydown

二、按下键盘触发(不支持ctrl、shift等功能性按键)

  • 事件名称: keypress

三、松开键盘触发

  • 事件名称: keyup

注:

  • 三个事件的执行顺序: keydown -----> keypress ------> keyup

代码:

keydown

document.addEventListener('keydown',function () {
    console.log('按下了键盘')
})
	//当在页面的任意位置按下按键时,即可触发函数。

keypress

document.addEventListener('keypress',function () {
    console.log('按下了键盘')
})
	//当在页面的任意位置按下非功能性按键时,即可触发函数。

keyup

document.addEventListener('keyup',function () {
    console.log('松开了按键')
})
	//当在页面的任意位置按下按键时,第一时间无反应
	//需要将收松开,即可触发函数。

10.5.1 获取按下的键

document.addEventListener('keydown',function (e) {
    console.log(e.key)
})

案例-1

页面的输入框按s时可以自动获取焦点。

<div class="container">
    <div class="row">
        <div class="col-md-3">
            <input type="text" class="form-control">
        </div>
    </div>
</div>
let iptEle = document.querySelector('input')
//因为要彻底等到按键按完结果,所以使用的keyup。
document.addEventListener('keyup',function (e) {
    if (e.key === 's') {
        //获取焦点
        iptEle.focus();
    }
})

案例-2

在案例1的基础之上添加个新功能,用户输入信息会在上方放大显示。

<style>
    .box1 {
        margin-top: 100px;
        position: relative;
    }
    .box1 .cont {
        /*利用定位来确认位置,不会影响到标准流*/
        position: absolute;
        top: -40px;
        width: 264px;
        padding: 0 10px 0 ;
        border: 2px solid rgb(204,204,204);
        border-radius: 10px;
        font-size: 20px;
        display: none;
    }
</style>

<!--需要引入bootstrap-->
<!--栅格系统-->
<div class="container box1">
    <div class="row">
        <div class="col-md-3">
            <div class="cont"></div>
            <input type="text" class="form-control" placeholder="请输入快递单号">
        </div>
    </div>
</div>
//案例1:页面上任意地方输入s,则输入框聚焦。
let iptEle = document.querySelector('input')
document.addEventListener('keyup',function (e) {
    if (e.key === 's') {
        iptEle.focus();
    }
})


//案例2
let contEle = document.querySelector('.cont')
iptEle.addEventListener('keyup',function () {
    //CSS里已经将display改成none了
    //这里的判断表示当输入的数据清空时,顶部盒子可以隐藏
    if (this.value === '') {
        contEle.style.display = 'none'
    } else {
        //如果输入的有内容,那么contELe的内部文本与之相同。
        contEle.innerHTML = this.value
        contEle.style.display = 'block'
    }
})

优化:

  • 输入框失去焦点,隐藏子盒子,获得焦点就重新展示。
let iptEle = document.querySelector('input')
document.addEventListener('keyup',function (e) {
    if (e.key === 's') {
        iptEle.focus();
    }
})

let contEle = document.querySelector('.cont')
iptEle.addEventListener('keyup',function () {
    if (this.value === '') {
        contEle.style.display = 'none'
    } else {
        contEle.innerHTML = this.value
        contEle.style.display = 'block'
        
		//失去焦点事件。
        iptEle.onblur = function () {
            contEle.style.display = 'none'
        }

    }
	
    //获得叫焦点事件
    iptEle.onfocus = function () {
        contEle.style.display = 'block'
    }
})

十一、BOM

DOM

  • 文档对象模型。
  • DOM就是把文档当做一个对象来看待。
  • DOM的顶级对象时document
  • DOM主要学习的是操作页面元素。
  • DOM是W3C标准规范。

BOM

  • 浏览器对象模型
  • 浏览器当做一个对象来看待。
  • BOM的顶级对象时window
  • BOM学习的是浏览器窗口交互的一些对象。
  • BOM是浏览器厂商在各自浏览器上定义的,兼容性差。

简单来说就是DOM—JS代码操作标签; BOM—JS代码操作浏览器。

11.1 BOM常用事件

常用事件:

函数名执行条件
window.load页面全部加载完毕之后执行
window.DOMContentLoaded页面DOM加载完毕执行
window.resize页面尺寸发生变化执行
window.pageshow页面重新加载完毕之后执行

load与pageshow的区别:

  • load在火狐浏览器中,通过链接跳转再返回,无法再执行函数内部的代码。
  • pageshow则不会,跳转过去再跳转回来,函数内部的代码会再次执行。

11.1.1 加载完毕执行

load

作用:页面全部加载完毕之后触发函数

事件名称:

  • load
//传统方法
window.onload = function () {
    alert('加载完毕')
}

//监听方法
window.addEventListener('load',function () {
        alert('加载完毕')
})

推荐使用监听方法,因为可以绑定多个事件函数。

而使用传统方法,则以最后一个为准。

DOMContentLoaded

当DOM加载完成(不包括样式表,图片,flash等)时,即可触发。

如果页面的图片很多用户访问到load触发的时间可能会过长,那么就会影响交互效果,影响用户体验。

所以在有些地方用DOMContentLoaded事件更合适。

window.addEventListener('DOMContentLoaded',function () {
    alert('1111')
})

当DOMContentLoaded与load都存在时,前者会先执行。

11.1.2 标签页缩放执行

resize

事件名称:

  • resize
  • 页面每缩放1px就会触发。
window.addEventListener('resize',function () {
    console.log('尺寸改变')
})

通常用来做响应式布局(window.innerWidth当前屏幕宽度)

11.1.3 定时器

定时器分为两种:

  • 到点了只执行一次
  • 间隔时间一直执行

一、到点了执行一次

**1.1 添加定时器 **

作用:

  • 可实现定时执行函数代码(到点了执行一次)

格式:

// window.setTimeout(调用函数,[延迟毫秒数])
setTimeout(调用函数,[延迟毫秒数])

window可以省略,变成下面这种,[ ]内表示可选参数,默认为0,单位毫秒

测试代码:

//第一种方法
function func() {
    console.log('定时器测试')
}
let test = setTimeout(func,2000)   //这里的func函数可以加括号,但是画蛇添足。
//赋值给一个变量,是为了用作定时器标识ID,后续取消定时器需要用到。
//第二种方法
let test = setTimeout(function () {
    console.log('定时器测试')
},2000)

1.2 清除定时器

作用:

  • 清除定时器回调函数

格式:

// window.clearTimeout(定时器ID)   同样的,window可以省略不写。
clearTimeout(定时器ID)

案例:

<!--
	样式是BootStrap的
-->
<input type="button" class="btn btn-danger" value="点击拆除炸弹">
//设置定时器,“炸弹”3秒后爆炸。
let boom = setTimeout(function () {
    alert('BOOM!灰飞烟灭')
},3000)


//给“拆弹”按钮绑定事件。
let  btnEle = document.querySelector('input')
btnEle.addEventListener('click',function () {
    //取消定时器
    clearTimeout(boom)
})

二、间隔时间一直执行

2.1 添加定时器

作用:

  • 每间隔一段时间就执行一次

格式:

setInterVal(回调函数,[间隔毫秒数])

测试:每2秒执行控制台打印

let test = setInterval(function () {
    console.log('定时器测试')
},2000)

2.2 清除定时器

作用:

  • 清除定时器回调函数

格式:

// window.clearInterval(定时器ID)   
clearInterval(定时器ID)

测试:

  • 控制塔每间隔一秒就输出信息,当点击按钮时,不在继续输出。
<input type="button" class="btn btn-danger" value="点击">
let test = setInterval(function () {
    console.log('定时器测试')
},1000)

let btnELe = document.querySelector('.btn')
btnELe.onclick = function () {
    clearInterval(test)
}
11.1.3.1 手机发送验证码

实现以下效果:

Js-定时器-案例-1

<p>手机号:<input type="text"></p>
<p>验证码:<input type="text"><input id="ipt1" type="button" value="发送验证码"></p>
let iptEle = document.querySelector('#ipt1')
iptEle.onclick = function () {
    //先完成按钮点击之后不可用
    this.disabled = true

    //再来完成动态的时间倒计时
    let time = 5
    let timer = setInterval(function () {
        if (time === 0) {
            clearInterval(timer)
            iptEle.disabled = false
            iptEle.value = '发送验证码'
            time = 5
        } else {
            iptEle.value = time+'秒后可重新发送'
            time -= 1
        }
    },1000)
}
11.1.3.2 实时显示当前时间

点击开始按钮,实时显示当前时间。

<input type="text" id="d1" style="display: block;height: 50px;width: 200px">
<button id="d2">开始</button>
<button id="d3">结束</button>

<script>
    // 先定义一个全局存储定时器的变量
    let t = null
    let inputEle = document.getElementById('d1')
    let startBtnEle = document.getElementById('d2')
    let endBtnEle = document.getElementById('d3')
    // 1 访问页面之后 将访问的时间展示到input框中
    // 2 动态展示当前时间
    // 3 页面上加两个按钮 一个开始 一个结束
    function showTime() {
        let currentTime = new Date();
        inputEle.value = currentTime.toLocaleString()
    }

    startBtnEle.onclick = function () {
        // 限制定时器只能开一个
        if(!t){
            t = setInterval(showTime,1000)  // 每点击一次就会开设一个定时器 而t只指代最后一个
        }
    }
    endBtnEle.onclick = function () {
        clearInterval(t)
        // 还应该将t重置为空
        t = null
    }
</script>
11.1.3.3 省市联动

实现以下效果:

js-定时器综合案例-3

<select name="" id="d1">
    <option value="" selected disabled>--请选择--</option>
</select>
<select name="" id="d2"></select>

<script>
    let proEle = document.getElementById('d1')
    let cityEle = document.getElementById('d2')
    // 先模拟省市数据
    data = {
        "北京": ["朝阳区", "海淀区",'昌平区'],
        "广州": ["荔湾区", "越秀区",'天河区'],
        '上海':['浦东新区','静安区','黄浦区'],
        '深圳':['南山区','宝安区','福田区']
    };
    // 选for循环获取省
    for(let key in data){
        // 将省信息做成一个个option标签 添加到第一个select框中
        // 1 创建option标签
        let opEle = document.createElement('option')
        // 2 设置文本
        opEle.innerText = key
        // 3 设置value
        opEle.value = key  // <option value="省">省</option>
        // 4 将创建好的option标签添加到第一个select中
        proEle.appendChild(opEle)
    }
    // 文本域变化事件  change事件
    proEle.onchange = function () {
        // 先获取到用户选择的省
        let currentPro = proEle.value
        // 获取对应的市信息
        let currentCityList = data[currentPro]
        // 清空市select中所有的option
        cityEle.innerHTML = ''
        // 自己加一个请选择
        let ss = "<option disabled selected>请选择</option>"
        // let oppEle = document.createElement('option')
        // oppEle.innerText = '请选择'
        // oppEle.setAttribute('selected','selected')
        cityEle.innerHTML = ss

        // for循环所有的市 渲染到第二个select中
        for (let i=0;i<currentCityList.length;i++){
            let currentCity = currentCityList[i]
            // 1 创建option标签
            let opEle = document.createElement('option')
            // 2 设置文本
            opEle.innerText = currentCity
            // 3 设置value
            opEle.value = currentCity  // <option value="省">省</option>
            // 4 将创建好的option标签添加到第一个select中
            cityEle.appendChild(opEle)
        }
    }
</script>

11.2 location对象

汇总一览:

location对象属性返回值
location.href获取或者设置,整个URL
location.host返回域名(host)
location.port返回端口号(port)
location.pathname返回路径(path)
location.search返回参数(query)
location.hash返回片段,#后面的内容,链接、锚点等(fragment)
location.assign重定向页面,跟href一样。
location.replace替换当前页面,但是不能回退到初识页面
location.reload刷新页面

URL

  • 统一资源定位符,是互联网上标准资源的地址,互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。

    URL的语法格式:

    protocol://host[:port]/path/[?query]#fragment

    例如:

    http://www.baodu.com/login.html?name=liuyu&pwd=123#link

assign、replace与href的区别

<button id="btn1" class="btn btn-success">点击看妹子</button>
<button id="btn2" class="btn btn-danger">点击去百度</button>
<button id="btn3" class="btn btn-warning">点击刷抖音</button>
<button id="btn4">刷新</button>
let btn1 = document.querySelector('#btn1')
let btn2 = document.querySelector('#btn2')
let btn3 = document.querySelector('#btn3')
let btn4 = document.querySelector('#btn4')

btn1.onclick = function () {
    location.href = 'http://www.mmzztt.com'
}

btn2.onclick = function () {
    location.assign('https://www.baidu.com')
}

btn3.onclick = function () {
    location.replace('https://www.douyin.com/?enter=guide')
}

btn4.onclick = function () {
    location.reload()
}

最终的效果为:

  • href与assign的效果相同,跳转到指定页面后,可回退至初识页面。
  • replace则不可回退,相当于直接访问的就是该页面。

11.3 navigator对象

navigator对象包含了有关浏览器的信息,它有很多属性,最常用的是userAgent

该属性可以返回由客户机发送服务器的user-agent头部的值,可以判断用户是PC端还是移动端访问的。

console.log(window.navigator.userAgent)
//控制台输出:
//Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36

扩展:仿爬措施

  • 最简单最常用的一个就是校验当前请求的发起者是否是一个浏览器
    • userAgent
      • user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36

11.4 history对象

window对象给我们提供了一个history对象,与浏览器历史记录进行交互,该对象包含用户访问过的URL

方法汇总一览

history对象方法作用
back()可以后退功能
forward()前进
go(参数)参数如果是1,前进一个页面,如果是-1,后退一个页面

案例:

  • 实现目录页面点击前进,可跳转至文件页面,文件页面也返回到目录页面。

HTML-1

<a href="文件列表.html">查看所有文件</a>
<ul>目录列表
    <li>前端从入门到放弃</li>
    <li>运维从入门到硬盘全红</li>
    <li>office从入门到手写</li>
    <li>Python人工智障</li>
</ul>

<script>
	let aEle = document.querySelector('a')
	aEle.onclick = function () {
    	history.forward()
	}
</script>

HTML-2

<a href="目录列表.html">返回目录</a>
<ul>文件
    <li>day1 什么是HTML</li>
    <li>day2 什么是CSS</li>
    <li>day3 什么是JS</li>
    <li>......</li>
</ul>

<script>
    let aEle = document.querySelector('a')
    aEle.onclick = function () {
        history.back()
    }
</script>

十二、JS同步异步

JS执行机制:

  • 单线程,也就是说,同一时间只能做一件事。比如对某DOM元素进行添加和删除操作,不能同时进行,应该先进行添加再删除。
  • 为了解决单线程的问题,利用多核CPU的计算能力,HTML5提出Web Worker标准,允许JS创建多个线程,于是出现了同步异步

概念:什么是同步与异步

同步:

  • 前一个任务执行结束之后,再去执行另外一个任务,程序执行顺序与任务的排列顺序是一致的、同步的。

异步:

  • 多个任务并行执行,比如煮饭时间长,那我们可以在这段时间把菜炒了。

同步/异步任务

同步任务:

  • 同步任务都在主线程上执行,形成一个执行栈

异步任务:

  • JS的异步是通过回调函数实现的。
  • 一般异步任务有这三种
    • 普通事件,如click、resize等
    • 资源加载,如load、error等
    • 定时器,setInterval、setTimeout

JS执行顺序:

<button>点击</button>
let btnEle = document.querySelector('button')

console.log('1')

btnEle.onclick = function () {
    console.log('3')
}

window.onload = function () {
    console.log('4')
}

console.log('2')

执行分析图:

									|--------------|
|-----------------|					|  异步进程处理  |
|    主线程执行栈   |    ------->     | onclick      |
|console.log('1') |                 |--------------|
|console.log('2') |							|			
|-----------------|							|
     同步任务								|
									|--------------|
        							|	消息队列	|
            						|load	       |
									|--------------|
                    					异步任务

同步任务:

  • console.log(‘1’)
  • console.log(‘2’)

异步任务:

  • btnEle.onclick
  • window.onload
1.先执行同步任务
2.执行到异步任务时,交给异步进程处理,异步进程会放到消息队列中
3.当同步任务完成时,会到消息队列中查看是否有需要执行的,如果有,那就拿到主线程执行栈,运行。

4.类似于click这种点击事件,如果不点击,那么会一直存在于异步进程处理当中,不会存在于消息队列
  当点击触发之后,才会放到消息队列,然后再被拿到住线程执行栈,完成运行。
'''
由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环(event loop)
'''

image-20220627230928254

所以控制台输出为:
“1”

​ “2”

​ “4”

​ “3” (点击了按钮才会出现)

十三、PC端网页特效

13.1 元素偏移量offset

offset翻译过来就是偏移量,我们使用offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等。

  • 获得元素,距离带有定位父元素的位置
  • 获得元素自身的大小(宽度高度)
  • 注意:返回的数值都不带单位

汇总一览

offset系列属性作用
标签对象.offsetParent返回作为该元素带有定位的父级元素,如果父级没有定位,则返回body。
标签对象.offsetTop返回元素相对与父元素上方的偏移,但是父元素需要定位。
标签对象.offsetLeft返回元素相对与父元素左方的偏移,但是父元素需要定位。
标签对象.offsetWidth返回自身盒子的宽度。
标签对象.offsetHeight返回自身盒子的高度。

offsetParent

作用:

  • 当父级是定位过的,那么子级调用该方法时,返回父级元素节点
  • 如果父标签没定位,那么返回的是body

代码:

<style>
    /*父级定位*/
    .father {
        position: relative;
    }
</style>


<div class="father">
    <div class="son">son</div>
</div>


<script>
    let father = document.querySelector('.father')
    let son = document.querySelector('.son')
    //打印父节点
    console.log(son.offsetParent)
    //打印父节点的大儿子节点的文本内容,也就是son的文本。
    console.log(son.offsetParent.children[0].innerText)
</script>
<!--
	控制台输出:
		div.father
		son
-->

与parentNode的区别:

  • parentNode,返回父级,但是是最近一级的父级,不管父级有没有定位。
  • offsetParent,返回的是最近且定位的父级,如果没有,就返回body

offsetTop与offsetLeft

作用:

  • 当父节点定位过时,子节点调用该方法可获得与父节点的顶部偏移距离(margin)
  • 如果父标签没定位,那么返回的是距离body的

代码:

<style>
    .father {
        position: relative;
        width: 400px;
        height: 400px;
        background-color: pink;
        margin: 100px auto;
        overflow: hidden;
    }
    .son {
        margin-top: 100px;
        margin-left: 100px;
        width: 200px;
        height: 200px;
        background-color: skyblue;
    }
</style>


<div class="father">
    <div class="son">son</div>
</div>


<script>
    let father = document.querySelector('.father')
    let son = document.querySelector('.son')
    console.log(son.offsetTop)
    console.log(son.offsetLeft)
</script>
<!--
	控制台输出:
		100
		100
-->

如果父标签没有定位,那么offsetTop输出就为200

注意,返回值没有单位。

offsetWidth与offsetHeight

作用:

  • 得到盒子的大小(宽高)

代码:

<style>
    .box {
        width: 100px;
        height: 100px;
        border: 10px solid black;
    }
</style>

<script>
    let box = document.querySelector('.box')
    console.log(box.offsetWidth)
    console.log(box.offsetHeight)
</script>
<!--
	控制台输出:
		100
		100
-->

13.1.1 offset与style的区别

offsetstyle
offset可以得到任意样式表中的样式值style只能得到行内样式表中的样式值
offset系列获得的数值没有单位style.width获得的是带有单位的字符串
offsetWidth等属性为只读属性,只能获取不能赋值style.width是可读写属性,可赋值
所以,当想要获取元素代销位置,用offset更合适所以,当想要给元素更改值时,需要用到style

测试:

<style>
    .box {
        margin: 100px auto;
        width: 100px;
        height: 100px;
        border: 20px solid black;
    }
</style>

<div class="box"></div>

<script>
    let box = document.querySelector('.box')
    console.log(box.offsetWidth)
    console.log(box.style.width)
</script>
<!--
控制台输出:
	100
	(空)
-->

因为style不能获取到内嵌式的样式值,只能获取行内的,行内样式如:

<div class="box" style="width: 200px"></div>

13.2 scroll系列

作用:

  • scroll翻译过来就是滚动的意思,使用相关属性可以动态的得到该元素的大小,滚动距离等。

相关属性一览:

scroll系列属性作用
标签对象.scrollTop返回被滚动条卷上去的上侧距离,返回值不带单位
标签对象. scrollLeft返回被滚动条卷上去的左侧距离,返回值不带单位
标签对象. scrollWidth返回自身实际的内容宽度(包含padding,不含边框),返回值不带单
标签对象.scrollHeight返回自身实际的内容高度(包含padding,不含边框),返回值不带单位

在CSS文章中的第十一章装饰的第11.4overflow章节中,有提到scroll

属性名:overflow

值:

  • visible 默认值,溢出部分可见

  • hidden 溢出部分隐藏(最常用)

  • scroll 无论是否溢出,都显示滚动条

  • auto 当溢出时,显示滚动条

所以呢,这个scroll就是对滚动条进行一些操作的。

  • 滚动事件名称,也叫作scroll

srollTop

<style>
    .cont {
        width: 100px;
        height: 100px;
        background-color: skyblue;
        overflow: scroll;
    }
</style>

<div class="cont">上海卫健委7月3日通报:2022年7月2日0-24时,上海新增2例本土确诊病例,均在隔离管控中发现;无新增本土无症状感染者。新增5例境外输入性确诊病例和7例无症状感染者,均在闭环管控中发现,详情如下</div>

<script>
    let cont = document.querySelector('.cont')
    
   //滚动事件
    cont.addEventListener('scroll',function () {
        //输出文字数据的顶部,距离盒子顶部的距离,初始为1,
        //但随着滚动条的向下的滚动,这个数字会越来越大。
        console.log(cont.scrollTop)
    })
</script>

13.2.1 scrollY

与scrollTop的区别:

  • 元素被卷去的头部是element.scrollTop,如果是页面被卷去的头部则是scrollY(x为水平,y为垂直)

scrollX案例:

  • 当页面滚动到某处时,展示返回顶部标签

(仅供参考,并未做适配)

<style>
    header {
        width: 800px;
        height: 300px;
        margin: 0 auto;
        background-color: skyblue;
    }
    article {
        width: 800px;
        height: 1300px;
        margin: 0 auto;
        background-color: pink;
    }
    .back {
        position: fixed;
        top: 500px;
        right: 259px;
        width: 100px;
        height: 50px;
        background-color: plum ;
        text-decoration: none;
        color: black;
        text-align: center;
        line-height: 50px;
        display: none;
    }
</style>

<!--
利用a标签设置锚点,实现点击返回。
-->
<header id="header">首页</header>
<a href="#header" class="back">返回顶部</a>
<article>内容</article>
//页面加载完之后执行
window.addEventListener('load',function () {
    let aEle = document.querySelector('a')
    let headerEle = document.querySelector('header')

    //给整个页面添加滚动事件
    document.addEventListener('scroll',function () {
        //当垂直滚动的距离,大于等于了首页盒子的高度时,展示返回顶部盒子。
        if (window.scrollY >= headerEle.offsetHeight) {
            aEle.style.display = 'block'
        } else {
            aEle.style.display = 'none'
        }
    })
})

13.2.2 以上总结

  • offset系列经常用于获得元素的位置
  • client经常用于获取元素大小
  • scroll经常用于获取滚动距离
    • 注意:页面滚动距离用的是scrollY

13.3 案例

实现动画效果,盒子从左依次到右,移动距离700像素

思路:

  • 利用offsetLeft获取到盒子的水平位置,然后定时器内,不断的+1像素,最后将该值赋给element.style.left

代码:

<style>
    img {
        position: relative;
        width: 200px;
        height: 200px;
        background-color: pink;
    }
</style>

<img src="https://ts1.cn.mm.bing.net/th/id/R-C.9458c0435c185976a3e3f13d015ace42?rik=EDNGrGJ03Y28TA&riu=http%3a%2f%2fn.sinaimg.cn%2fsinakd20201013ac%2f416%2fw640h576%2f20201013%2f1d25-kakmcxe4670030.jpg&ehk=rooJzZRqcJiJDD0IfsPuA728IBCZ3INcFY7ixloByBA%3d&risl=&pid=ImgRaw&r=0" alt="">

<script>
    let imgEle = document.querySelector('img')
    function imgMoveX(obj,target) {
        //这里为了防止后续多个盒子调用,而造成名称混乱,所以不再定义函数,而是赋予盒子属性。
        obj.imgMove = setInterval(function () {
            //定时器函数,每隔2毫秒执行一次
            
            //当盒子距离左侧的距离,等于目标到达的距离时,关闭定时器。
            if (obj.offsetLeft === target) {
                //清除定时器
                clearInterval(obj.imgMove)
            }
            
            //如果还没有达到指定位置,则每2毫秒执行left加一,反复更改定位来实现移动。
            obj.style.left = obj.offsetLeft + 1 +'px'
        },2)

    }
    
    //调用,到500像素的时候停下。
    imgMoveX(imgEle,500)
</script>

展示效果:

Js-网页特效-案例-1

13.3.1 缓动动画

缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来。

效果展示:

Js-网页特效-案例-2

思路:

  • 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。
  • 核心算法:(目标的值 - 现在的位置) / 10
  • 由上面的算法,来计算出每次移动的距离步长。

代码:

let imgEle = document.querySelector('img')
function imgMoveX(obj,target) {
    obj.imgMove = setInterval(function () {

        //算出定时器每次执行时,盒子移动的距离。
        let step = (target - obj.offsetLeft) / 10
        //防止因为小数造成动画一来一回的数值不一。
        step = step > 0 ? Math.ceil(step) : Math.floor(step)


        if (obj.offsetLeft === target) {
            clearInterval(obj.imgMove)
        }
        
        //原来写死的1,需要更改为step
        obj.style.left = obj.offsetLeft + step +'px'
    },40)

}

imgMoveX(imgEle,500)

十四、移动端网页特效

14.1 触屏事件

移动端浏览器兼容性较好,不需要考虑JS的兼容性问题,但是移动端有自己独特的地方

比如触屏事件touch,也称做触摸事件,Android和IOS都有

touch对象代表一个触摸点,触摸点可能是一根手指,也可能是一根电容笔,触屏事件可响应用户手指或笔,对屏幕或者触摸板操作。

触屏touch事件说明
touchstart手指触摸到一个DOM元素时触发
touchmove手指在一个DOM元素上滑动时触发
touchend手指从一个DOM元素上移开时触发

touchstart触摸执行

<style>
    div {
        width: 100px;
        height: 100px;
        background-color: skyblue;
    }
</style>

<div></div>

<script>
    let div = document.querySelector('div')
    div.ontouchstart = function () {
        console.log('摸一下')
    }
</script>
<!--
	实现效果:
			在盒子内部刚按下鼠标就执行函数内部代码,控制台输出数据。
-->

touchmove触摸不松手一直执行

let div = document.querySelector('div')
div.ontouchmove = function () {
    console.log('左右上下滑动一直摸')
/*
	实现效果:
			鼠标在盒子内部按住并滑动,会一直执行函数代码。
*/

touchend触摸松手才执行

let div = document.querySelector('div')
div.ontouchend = function () {
    console.log('摸爽了')
}
/*
	实现效果:
			鼠标在盒子内部点击,在松开时会执行代码。
*/

14.1.1 触摸事件对象

touchEvent:

  • 是一类描述手指再触摸平面(触摸屏、触摸板)的状态变化的事件。

  • 这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少,等等。

  • touchstart、touchmove、touchend三个事件都会有各自的事件对象

触摸列表说明
touches返回正在触摸平面的所有手指
targetTouches返回证明触摸当前DOM元素上的所有手指(常用)
changedTouches记录手指状态是点击了还是离开了

通过事件对象,获取到触摸手指列表,得到指定手指当前的X轴坐标。

event.targetTouches[0].pageX

手指触摸对象Touch,常用的方法有:

  • clientX、clientY
  • pageX、pageY

在10.4.1 获取鼠标xy轴坐标中,该方法用来获取鼠标坐标,这里用来获取手指坐标

14.1.2 案例

案例:鼠标拖动盒子

  • 第一步,利用touches的pageX获取手指当前的坐标,同时获取盒子原来的位置
  • 第二步,计算手指的滑动距离,并且移动盒子

代码:

<style>
    div {
        position: absolute;
        width: 100px;
        height: 100px;
        background-color: skyblue;
    }
</style>

<div></div>

<script>

let div = document.querySelector('div')

//绑定触摸事件
div.ontouchstart = function (event) {
    //当手指在盒子上触摸时,获取手指的坐标,以及盒子的位置
    let startX = event.targetTouches[0].pageX
    let startY = event.targetTouches[0].pageY
    let boxX = this.offsetLeft
    let boxY = this.offsetTop

	//绑定手指移动事件
    div.ontouchmove = function (event) {
        //当手指移动时,获取移动的距离
        let moveX = event.targetTouches[0].pageX
        let moveY = event.targetTouches[0].pageY
        //移动之后的坐标 - 初始的坐标 = 手指实际移动的距离
        //所以,盒子移动之后的坐标 = 盒子初始的坐标+手指实际移动的坐标
        this.style.left = boxX + moveX - startX  + 'px'
        this.style.top = boxY + moveY - startY + 'px'

        //手指移动也会触发滚动屏幕,所以这里要阻止默认事件。
        event.preventDefault()

    }
}
</script>

14.2 FastClick插件

什么是FastClick:

  • 移动设备上的浏览器默认会在用户点击屏幕大约延迟300毫秒后才会触发点击事件。

  • 这是为了检查用户是否在做双击。为了能够立即响应用户的点击事件,才有了FastClick。

    项目地址:https://github.com/ftlabs/fastclick

什么是插件:

  • 插件就是js文件,引入之后按照要求使用即可

FastClick使用:

  • JS引入

    <script type='application/javascript' src='/path/to/fastclick.js'></script>
    
  • 初始化FastClick实例

    //原生JS
    if ('addEventListener' in document) {
        document.addEventListener('DOMContentLoaded', function() {
            FastClick.attach(document.body);
        }, false);
    }
    
    //jQuery
    $(function() {
        FastClick.attach(document.body);
    });
    

14.3 Swiper轮播图插件

Swiper插件是什么:

  • 纯javascript打造的滑动特效插件,面向手机、平板电脑等移动终端。
  • 能快速帮助开发者,实现触屏焦点图、触屏Tab切换、触屏轮播图切换等常用效果。

官网:https://www.swiper.com.cn/

用法:

1.下载swiper

进入官网—>获取Swiper—>下载Swiper

image-20220713184440849

(本文档以8.3版本为准)

2.找到自己想要的轮播图

在线演示这里可以看到各式各样的轮播图,找到自己想要的,如编号200的

image-20220713184916270

3.使用

使用步骤:

  • 解压刚刚下载的Swiper压缩包,随后进入demo文件夹,里面有很多html文件,找到自己想要的轮播图源码,如编号200。

    image-20220713184704604
  • 打开html文件,查看引入的css文件和js文件是什么,并将该文件拷贝到项目中,随后页面引入。

    <!-- Swiper CSS -->
    <link rel="stylesheet" href="css/swiper-bundle.min.css"/>
    <!-- Swiper JS -->
    <script src="js/swiper-bundle.min.js"></script>
    
  • 拷贝HTML源码(左侧为demo源码,右侧为项目代码)

    将demo源码的HTML代码,拷贝到布局好的容器内,并将文件修改为轮播图的图片

    image-20220713185810908

  • 拷贝JS源码(左侧为demo源码,右侧为项目代码)

    image-20220713185943850

经过上述步骤以后,一个轮播图就已经实现了,如果想要其他功能,后续可以再进行参数修改,或者自己手动书写Css、Js代码。

一下为使用Swiper的源码

<!-- Swiper CSS -->
<link rel="stylesheet" href="css/swiper-bundle.min.css"/>

<style>
.carousel {
width: 512px;
height: 360px;
margin: 50px auto;
}


.swiper-slide a img {
float: left;
width: 512px;
height: 360px;
}

.swiper-button-next,
.swiper-button-prev {
display: none;
}

</style>

<!--轮播图布局-->
<div class="carousel">
<!--swiper HTML-->
<div class="swiper mySwiper">
<div class="swiper-wrapper">
   <div class="swiper-slide">
       <a href=""><img src="https://i02piccdn.sogoucdn.com/fcf1805a507356b0" alt=""></a>
   </div>
   <div class="swiper-slide">
       <a href=""><img src="https://i03piccdn.sogoucdn.com/3abc8ffaadcd7126" alt=""></a>
   </div>
   <div class="swiper-slide">
       <a href=""><img src="https://i04piccdn.sogoucdn.com/3d0018cd22669b8d" alt=""></a>
   </div>
   <div class="swiper-slide">
       <a href=""><img src="https://i03piccdn.sogoucdn.com/556f8649e6c1ab9e" alt=""></a>
   </div>
   <div class="swiper-slide">
       <a href=""><img src="https://i01piccdn.sogoucdn.com/cf5ba0c4ecf814ed" alt=""></a>
   </div>
</div>
<div class="swiper-button-next"></div>
<div class="swiper-button-prev"></div>
<div class="swiper-pagination"></div>
</div>
<!--swiper HTML-->
</div>
<!--轮播图布局-->


<!-- Swiper JS -->
<script src="js/swiper-bundle.min.js"></script>

<script>
/*swiper  JS*/
let swiper = new Swiper(".mySwiper", {
slidesPerView: 1,
spaceBetween: 30,
loop: true,
pagination: {
   el: ".swiper-pagination",
   clickable: true,
},
navigation: {
   nextEl: ".swiper-button-next",
   prevEl: ".swiper-button-prev",
},
});
	/*swiper  JS*/


	/*非swiper,为自行书写的JS代码*/
let leftBtn = document.querySelector('.swiper-button-prev')
let rightBtn = document.querySelector('.swiper-button-next')
let carousel = document.querySelector('.carousel')
carousel.onmouseover = function () {
leftBtn.style.display = 'block'
rightBtn.style.display = 'block'
}

carousel.onmouseout = function () {
leftBtn.style.display = 'none'
rightBtn.style.display = 'none'
}
/*非swiper,为自行书写的JS代码*/

</script>

14.3.1 superslide插件

详情可见官网

http://www.superslide2.com

14.3.2 iscroll插件

详情可见官网

https://github.com/cubiq/iscroll

14.4 zymedia.js插件

作用:

  • 由于vide标签在不同浏览器所展示的插件按钮均不相同,所以zyMedia解决的正是这些。

    下载地址:https://gitee.com/tujiawang/zyMedia/tree/master/src

使用方法略,具体和Swiper差不多

  • 引入文件
  • 复制源代码并进行替换。

十五、本地存储

15.1 sessionStorage

sessionStorage存储的特点:

  • 生命周期为关闭浏览器窗口
  • 在同一个页面下,数据可以共享
  • 存储方式为键值对的形式

方法汇总一览:

(k为key,v为value)

方法说明
sessionStorage.setltem(k,v)存储数据
sessionStorage.getltem(k)获取数据
sessionStorage.removeltem(k)删除数据
sessionStorage.clear()删除所有数据

存储与获取数据

<input type="text" class="data">
<input type="button" class="btn-success btn" value="提交存储">
<input type="button" class="btn-warning btn" value="获取数据">

<script>
    let setData = document.querySelector('.data')
    let submit = document.querySelector('.btn-success')
    let getData = document.querySelector('.btn-warning')

    submit.onclick = function () {
        let cont = setData.value
        sessionStorage.setItem('username',cont)
    }
    getData.onclick = function () {
        let cont = sessionStorage.getItem('username')
        alert(cont)
    }
</script>

提交存储的数据,可在控制台的application里查看

image-20220714011022491

sessionStorage存储的数据后续可关闭浏览器,清除浏览器缓存。

或者利用sessionStorage.removeltem(k)或sessionStorage.clear()清除。

15.2 localStorage

localStorage存储的特点:

  • 生命周期永久生效,除非手动删除,否则及时浏览器关闭,数据也会存在
  • 可以多页面共享(同一浏览器可以共享)
  • 以键值对的形式存储

方法汇总一览:

方法说明
localStorage.setltem(k,v)存储数据
localStorage.getltem(k)获取数据
localStorage.removeltem(k)删除数据
localStorage.clear()删除所有数据
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿煜酱~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值