JavaScript基础学习笔记

时间:2024-11-21 10:09:34

跟着pink老师课程写的笔记

JS基础

1.运算符

1.1 自增运算符

var e = 10;
var f = e++ + ++e; 
// 1. e++ =  10  e = 11  
// 2. e = 12  ++e = 12
        console.log(f); // 22
  • 1
  • 2
  • 3
  • 4
  • 5

1.2 逻辑运算符

1.2.1 逻辑与

有假必假,两边都是true才返回true

对于数值和表达式:

语法: 表达式1 && 表达式2

如果第一个表达式的值为真,则返回表达式2

如果第一个表达式的值为假,则返回表达式1

console.log( 123 && 456 );        // 456
console.log( 0 && 456 );          // 0
console.log( 123 && 456&& 789 );  // 789
  • 1
  • 2
  • 3
1.2.2 逻辑或

有真必真,两边都是false才返回false

对于数值和表达式:

l语法: 表达式1 || 表达式2

l 如果第一个表达式的值为真,则返回表达式1

l 如果第一个表达式的值为假,则返回表达式2

console.log( 123 || 456 );         //  123
console.log( 0 ||  456 );          //  456
console.log( 123 || 456 || 789 );  //  123
  • 1
  • 2
  • 3

1.3 运算符优先级

image-20201220003951505

优先级从上到下依次递减,最上面具有最高的优先级,逗号操作符具有最低的优先级。

1.4 不同类型数据相加减

1.4.1 同类型数据

①布尔类型相加减

true转化为数值1,false转化为数值0,进行数值运算

②字符型相加减

相加首尾连接

相减是根据Number()方法将字符型转化为数值型,再进行减法。如果转化后值为NaN,则最终结果为NaN

1.4.2 不同类型数据

与字符串相加都会变成字符串

数值型和数值字符串相加减,数值字符串会转化为数值,从而结果是数值

数值型和非数值字符串相减,根据Number()方法将字符型转化为数值型,结果是NaN(一般的非数值)

布尔类型和数值相加减,true转化为数值1,false转化为数值0,进行数值运算,结果是数值

undefined和数值相加减结果是NaN

1.5 浮点数的精度问题

var result = 0.1 + 0.2;    // 结果不是 0.3,而是:0.30000000000000004
console.log(0.07 * 100);   // 结果不是 7,  而是:7.000000000000001
  • 1
  • 2

所以:不要直接判断两个浮点数是否相等 !

2.函数

1.1 return注意事项

函数都是有返回值的

  1. 如果有return则返回return后面的值
  2. 如果没有return则返回undefined

break:结束当前的循环体(如?for、while)
continue:跳出本次循环,继续执行下次循环(如?for、while)
return:不仅可以退出循环,还能够返回?return?语句中的值,同时还可以结束当前的函数体内的代码

1.2 arguments

当我们不确定有多少个参数传递的时候,可以用arguments来获取(原来形参的位置留空)。

JavaScript中,arguments实际上它是当前函数的一个内置对象。所有函数都内置了一个arguments对象,arguments对象中存储了传递的所有实参。

伪数组:

  • 具有length属性
  • 按索引方式储存数据
  • 不具有数组的push,pop等方法
function maxValue() {
	var max = arguments[0];
	for (var i = 0; i < arguments.length; i++) {
		if (max < arguments[i]) {
		max = arguments[i];
		}
	}
	return max;
}
console.log(maxValue(2, 4, 5, 9));
console.log(maxValue(12, 4, 9));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

1.3 函数声明方式

1.3.1 自定义函数方式(命名函数)
// 声明定义方式
function fn() {...}
// 调用
fn()
  • 1
  • 2
  • 3
  • 4

因为有名字,所以也被称为命名函数
调用函数的代码既可以放到声明函数的前面,也可以放在声明函数的后面

1.3.2 函数表达式方式(匿名函数)
// 这是函数表达式写法,匿名函数后面跟分号结束
var fn = function(){...}// 调用的方式,函数调用必须写到函数体下面
fn();
  • 1
  • 2
  • 3
  • 4

其中fn是变量名不是函数名

因为函数没有名字,所以也被称为匿名函数
这个fn里面存储的是一个函数
函数表达式方式原理跟声明变量方式是一致的
函数调用的代码必须写到函数体后面

3.作用域

3.1 全局作用域

作用于所有代码执行的环境(整个script标签内部)或者一个独立的js文件。

在全局作用域下声明的变量叫做全局变量(在函数外部定义的变量)。

3.2 局部作用域(函数作用域)

作用于函数内的代码环境,就是局部作用域。?因为跟函数有关系,所以也称为函数作用域。

在局部作用域下声明的变量叫做局部变量(在函数内部定义的变量)

  • 局部变量只能在该函数内部使用
  • 在函数内部var声明的变量是局部变量
  • 函数的形参实际上就是局部变量

3.3 块级作用域

块作用域由{}包括。

Js中没有块级作用域(在ES6之前)

3.4 作用域链

l 只要是代码,就至少有一个作用域
l 写在函数内部的局部作用域
l 如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域
l 根据在内部函数可以访问外部函数变量的这种机制,用链式查找决定哪些数据能被内部函数访问,就称作作用域链

function f1() {
	var num = 123;
	function f2() {
		console.log( num );
	}
	f2();
}
var num = 456;
f1();
//输出123,就近原则(一层一层往外查找)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

image-20201222234352918

作用域链:采取就近原则的方式来查找变量最终的值。

4.预解析(变量提升)

console.log(num);	// 1.报错,num is not defined

console.log(num);
var num = 2;		//2.输出 undefined

fn();
function fn() {
	console.log('打印');
}					//3.输出 打印

fn();
var fn2 = function() {
console.log('想不到吧');
}					//4.报错 fn2 is not a function
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

JS代码是由浏览器中的JS解析器来执行的。

JS解析器在运行JS代码的时候分为两步:预解析代码执行

  • 预解析:在当前作用域下,JS代码执行之前,浏览器会默认把带有var和function声明的变量在内存中进行提前声明(不会进行赋值)或者定义。
  • 代码执行:从上到下执行JS语句。
fn();
var fn2 = function() {
	console.log('想不到吧');
}
//相当于
var fn2;
fn();
fn2 = function(){......}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

预解析有 变量预解析 和 函数预解析

        // 案例4
        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;//局部变量
            a = b = c = 9;
            // 相当于 var  a  = 9; b = 9; c = 9; 
            // b 和 c 直接赋值 没有var 声明 当 全局变量看
            // 集体声明  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);
//结果:9 9 9 9 9 报错
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

5.对象

在JavaScript中,对象是一组无序的相关属性和方法的集合,所有的事物都是对象,例如字符串、数值、数组、函数等。
对象是由属性和方法组成的。
l 属性:事物的特征,在对象中用属性来表示(常用名词)
l 方法:事物的行为,在对象中用方法来表示(常用动词)

例如保存一个人的完整信息,可以用数组

var arr = [‘张三疯’, ‘男', 128,154];
  • 1

用 对象 :

image-20201223003238417

person.name = ‘张三疯';
person.sex = ‘男';
person.age = 128;
person.height = 154
  • 1
  • 2
  • 3
  • 4

5.1 创建对象的三种方式

5.1.1 利用字面量创建对象

对象字面量:就是花括号{}里面包含了表达这个具体事物(对象)的属性和方法。
{}里面采取键值对的形式表示
l 键:相当于属性名
l 值:相当于属性值,可以是任意类型的值(数字类型、字符串类型、布尔类型,函数类型等)

var star = {
	name : 'pink',
	age : 18,
	sex : '男',
	sayHi : function(){
		alert('大家好啊~');
	}
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

5.1.2 利用new Object创建对象

var andy = new Obect();
andy.name = 'pink';
andy.age = 18;
andy.sex = '男';
andy.sayHi = function(){
	alert('大家好啊~');
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5.1.3 利用构造函数创建对象

构造函数:是一种特殊的函数,主要用来初始化对象,即为对象成员变量赋初始值,它总与new运算符一起使用。我们可以把对象中一些公共的属性和方法抽取出来,然后封装到这个函数里面。

在js中,使用构造函数要时要注意以下两点:

  • 构造函数用于创建某一类对象,其首字母要大写
  • 构造函数要和new一起使用才有意义
		function Star(name, age, sex) {
            this.name = name;
            this.age = age;
            this.sex = sex;
        }
        var ldh = new Star('刘德华', 18, '男');
        console.log(ldh);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

5.1.4 对象的调用

  • 对象属性调用: 对象.属性名
  • 属性的另一种调用方式: 对象[‘属性名’],注意方括号里面的属性必须加引号,我们后面会用
  • 对象方法调用:对象.方法名(),注意这个方法名字后面一定加括号

5.1.5 变量、属性、函数、方法的区别

变量:单独声明赋值,单独存在
属性:对象里面的变量称为属性,不需要声明,用来描述该对象的特征
函数:单独存在的,通过“函数名()”的方式就可以调用
方法:对象里面的函数称为方法,方法不需要声明,使用“对象.方法名()”的方式就可以调用,方法用来描述该对象的行为和功能。

5.1.6 遍历对象

for…in语句用于对数组或者对象的属性进行循环操作。

for (变量 in 对象名字) {
	//在此执行代码
}
  • 1
  • 2
  • 3

语法中的变量是自定义的,它需要符合命名规范,通常我们会将这个变量写为k或者key。

for (var k in obj) {
	console.log(k); 	// 这里的 k 是属性名
	console.log(obj[k]); // 这里的 obj[k] 是属性值
}
  • 1
  • 2
  • 3
  • 4

6.内置对象

6.1 Math对象

Math.PI // 圆周率

Math.floor()  // 向下取整,往小取值
Math.floor(1.1) //1
Math.floor(-1.1)//-2

Math.ceil() // 向上取整,往大取值

Math.round() // 四舍五入版 就近取整 注意 -3.5 结果是 -3(往大取)


Math.abs() // 绝对值

Math.max()/Math.min() // 求最大和最小值

random()//随机返回一个小数,其取值范围是[0,1),左闭右开0<=x<1
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

随机数方法

得到一个两数之间的随机数

function getRandomArbitrary(min, max) {
  return Math.random() * (max - min) + min;
}
//随机数乘以两数之差,随机取得两数之差之间的数,加上最小可取的数
  • 1
  • 2
  • 3
  • 4

得到一个两数之间的随机整数

这个例子返回了一个在指定值之间的随机整数。这个值不小于 min (如果 min 不是整数,则不小于 min 的向上取整数),且小于(不等于)max

function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min)) + min; 
  //不含最大值,含最小值
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

得到一个两数之间的随机整数,包括两个数在内

function getRandomIntInclusive(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min + 1)) + min; 
  //含最大值,含最小值 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

6.2 日期对象

日期对象是构造函数,所以我们需要实例化后才能使用(要new)

image-20201223235639127

        var date = new Date();

        var year = date.getFullYear();
        var month = date.getMonth() + 1;
        var dates = date.getDate();
        var days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
        var day = date.getDay();
        var h = date.getHours();
        h = h < 10 ? '0' + h : h;
        var m = date.getMinutes();
        m = m < 10 ? '0' + m : m;
        var s = date.getSeconds();
        s = s < 10 ? '0' + s : s;
        console.log('今天是' + year + '年' + month + '月' + dates + '日' + days[day] + ' ' + h + ':' + m + ':' + s);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

6.3 数组对象

6.3.1 检测是否为数组

instanceof运算符,可以判断一个对象是否属于某种类型
()用于判断一个对象是否为数组,isArray()是HTML5中提供的方法

var arr = [1, 23];
var obj = {};
console.log(arr instanceof Array); // true
console.log(obj instanceof Array); // false
console.log(Array.isArray(arr)); // true
console.log(Array.isArray(obj)); // false
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
6.3.2 添加删除数组元素的方法

image-20201224185701921

var arr = [1500, 1200, 2000, 2100, 1800];
var newArr = [];
for (var i = 0; i < arr.length; i++) {
	if (arr[i] < 2000) {
	newArr.push(arr[i]);
	}
}
console.log(newArr);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
6.3.3 数组排序

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0FliWGdO-1618047172807)(/2021/03/23/)]

var arr = [1, 64, 9, 6];
arr.sort(function(a, b) {
	return b - a; // 降序
	// return a - b; // 升序
});
console.log(arr)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
6.3.4 数组索引方法

image-20201224210059434

lastIndexOf()从后面开始查找

6.3.5 数组转换为字符串

image-20201224222639433

arr.join(';')
arr.toString()
  • 1
  • 2
6.3.6 连接、截取、删除

image-20201224223027106

6.4 字符串对象

6.4.1 基本包装类型

为了方便操作基本数据类型,JavaScript?还提供了三个特殊的引用类型:String、Number和Boolean。
基本包装类型就是把简单数据类型包装成为复杂数据类型,这样基本数据类型就有了属性和方法。

//?下面代码有什么问题?
var str = 'andy';
console.log(str.length);
  • 1
  • 2
  • 3

按道理基本数据类型是没有属性和方法的,而对象才有属性和方法,但上面代码却可以执行,这是因为js会把基本数据类型包装为复杂数据类型,其执行过程如下:

//1.生成临时变量,把简单类型包装为复杂数据类型
var temp = new String('andy');
//2.赋值给我们声明的字符变量
str = temp;
//3.销毁临时变量
temp = null;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
6.4.2 字符串的不可变

指的是里面的值不可变,虽然看上去可以改变内容,但其实是地址变了,内存中新开辟了一个内存空间。

var str = 'abc';
str = 'hello';
// 当重新给 str 赋值的时候,常量'abc'不会被修改,依然在内存中
// 重新给字符串赋值,会重新在内存中开辟空间,这个特点就是字符串的不可变
// 由于字符串的不可变,在大量拼接字符串的时候会有效率问题
var str = '';
for (var i = 0; i < 100000; i++) {
str += i;
}
console.log(str); // 这个结果需要花费大量时间来显示,因为需要不断的开辟新的空间
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
6.4.3 根据字符返回位置

image-20201225010423416

var str = '改革春风吹满地,中国人民真争气!'
console.log(str.indexOf('中'));
console.log(str.indexOf('中', 5));
//输出结果是一样的,因为他只是不查找了满字之前(不包括满字)的字符
  • 1
  • 2
  • 3
  • 4
6.4.4 根据位置返回字符(重点)

image-20201225004228198

6.4.5 字符串操作方法(重点)

6.4.6 替换字符串-replace()方法

replace()方法用于在字符串中用一些字符替换另一些字符。

replace(被替换的字符串,要替换为的字符串);
  • 1

只替换第一个字符

6.4.7 切分字符串-split()方法
var str = 'a,b,c,d';
console.log(str.split(',')); // 返回的是一个数组 [a, b, c, d]
  • 1
  • 2
6.4.8 大小写转换
toUpperCase() //转换大写
toLowerCase()//转换小写
  • 1
  • 2

7.简单类型与复杂类型

7.1 定义

简单类型又叫做基本数据类型或者值类型,复杂类型又叫做引用类型。

  • 值类型:简单数据类型/基本数据类型,在存储时变量中存储的是值本身,因此叫做值类型string,number,boolean,undefined,null
  • 引用类型:复杂数据类型,在存储时变量中存储的仅仅是地址(引用),因此叫做引用数据类型
    通过new关键字创建的对象(系统对象、自定义对象),如Object、Array、Date等

7.2 堆和栈

1、栈(操作系统):由操作系统自动分配释放存放函数的参数值、局部变量的值等。其操作方式类似于数据结构中的栈;
简单数据类型存放到栈里面

2、堆(操作系统):存储复杂类型(对象),一般由程序员分配释放,若程序员不释放,由垃圾回收机制回收。
复杂数据类型存放到堆里面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3lTujijq-1618047249923)(/2021/03/25/)]

注意:JavaScript中没有堆栈的概念,通过堆栈的方式,可以让大家更容易理解代码的一些执行方式,便于将来学习其他语言。

7.3 简单类型的内存分配

值类型(简单数据类型):string,number,boolean,undefined,null

值类型变量的数据直接存放在变量(栈空间)中

image-20201225012141578

7.4 复杂类型的内存分配

引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间
image-20201225014832312

7.5 简单类型传参

函数的形参也可以看做是一个变量,当我们把一个值类型变量作为参数传给函数的形参时,其实是把变量在栈空间里的值复制了一份给形参,那么在方法内部对形参做任何修改,都不会影响到的外部变量。

function fn(a) {
	a++;
	console.log(a);//这里输出11
}
var x = 10;
fn(x);
console.log(x)//这里输出10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

7.6 复杂类型传参

函数的形参也可以看做是一个变量,当我们把引用类型变量传给形参时,其实是把变量在栈空间里保存的堆地址复制给了形参,形参和实参其实保存的是同一个堆地址,所以操作的是同一个对象。

function Person(name) {
	this.name = name;
}
function f1(x) { // x = p
	console.log(x.name); // 2. 输出刘德华
	x.name = "张学友";
	console.log(x.name); // 3. 输出张学友
}
var p = new Person("刘德华");
console.log(p.name); // 1. 输出刘德华
f1(p);
console.log(p.name); // 4. 输出张学友
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Web APIs

1.简介

1.1 JS组成

image-20201225235200438

1.2 API 和 Web API 总结

  1. API 是为我们程序员提供的一个接口,帮助我们实现某种功能,我们会使用就可以了,不必纠结内部如何实现
  2. Web API 主要是针对于浏览器提供的接口,主要针对于浏览器做交互效果。
  3. Web API 一般都有输入和输出(函数的传参和返回值),Web API 很多都是方法(函数)
  4. 学习 Web API 可以结合前面学习内置对象方法的思路学习

2.1 DOM简介

文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口。
W3C 已经定义了一系列的 DOM 接口,通过这些 DOM 接口可以改变网页的内容、结构和样式。

2.2 DOM树

image-20201225235431951

DOM树 又称为文档树模型,把文档映射成树形结构,通过节点对象对其处理,处理的结果可以加入到当前的页面。

  • 文档:一个页面就是一个文档,DOM中使用document表示
  • 节点:网页中的所有内容,在文档树中都是节点(标签、属性、文本、注释等),使用node表示
  • 标签节点:网页中的所有标签,通常称为元素节点,又简称为“元素”,使用element表示

DOM 把以上内容都看做是对象

2.3 获取元素

2.3.1 根据ID获取
语法:document.getElementById(id)
作用:根据ID获取元素对象
参数:id值,区分大小写的字符串
返回值:元素对象 或 null
  • 1
  • 2
  • 3
  • 4
2.3.2 根据标签名获取
语法:('标签名') 或者 			       ('标签名') 
作用:根据标签名获取元素对象
参数:标签名
返回值:元素对象集合(伪数组,数组元素是元素对象)
  • 1
  • 2
  • 3
  • 4

注意:

  1. 因为得到的是一个对象的集合,所以我们想要操作里面的元素就需要遍历。

  2. 得到元素对象是动态的

  3. 只获取到一个元素时,返回的依旧时伪数组,只是数组对象只有一个

  4. 若没有获取到元素,返回的是空的伪数组

    案例代码

<body>
    <div id="time">2019-9-9</div>
    <ul>
        <li>hey,man</li>
        <li>hey,man</li>
    </ul>
    <ol id="ol">
        <li>哦豁</li>
    </ol>
    <script>
        // 用getElementById获取
        var timer = document.getElementById('time');
        console.log(timer);
        console.log(typeof(timer));
        console.dir(timer);

        // 根据标签名获取元素
        var lis = document.getElementsByTagName('li');
        console.log(lis);
        console.log(lis[0]);
        for (var i = 0; i < lis.length; i++) {
            console.log(lis[i]);
        }

        // 选取某个父元素的子元素
        var ol = document.getElementById('ol');
        console.log(ol.getElementsByTagName('li'));
    </script>
</body>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
2.3.3 H5新增的方法获取
// 1 根据类名返回元素对象集合
document.getElementsByClassName('类名')// 2 根据指定选择器返回第一个元素对象
document.querySelector('选择器'); 
// 3 根据指定选择器返回
document.querySelectorAll('选择器'); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

案例

    <div class="box">盒子1</div>
    <div class="box">盒子2</div>
    <div class="nav">
        <ul>
            <li>首页</li>
            <li>产品</li>
        </ul>
    </div>

    <script>
        console.log(document.querySelector('.box'));
        console.log(document.querySelector('.nav'));
        console.log('---------------');
        console.log(document.querySelectorAll('.box'));
    </script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
2.3.4 获取特殊元素(body,html)
doucumnet.body // 返回body元素对象
document.documentElement // 返回html元素对象
  • 1
  • 2

2.4 事件基础

2.4.1 事件三要素
  • 事件源(谁):触发事件的元素
  • 事件类型(什么事件): 例如 click 点击事件
  • 事件处理程序(做啥):事件触发后要执行的代码(函数形式),事件处理函数
<body>
    <button id="btn">唐伯虎</button>
    <script>
        var btn = document.getElementById('btn')
        btn.onclick = function() {
            alert('点秋香')
        }
    </script>
</body>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
2.4.2 常见的鼠标事件

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZzIFhXLh-1618047322309)(/2021/03/23/)]

2.5 操作元素

2.5.1 改变元素内容

  • 1

从起始位置到终止位置的内容, 但它去除 html 标签, 同时空格和换行也会去掉


  • 1

起始位置到终止位置的全部内容,包括 html 标签,同时保留空格和换行

<body>
    <button>显示当前系统时间</button>
    <div>某个时间</div>
    <p>1123</p>
    <script>
        // 当我们点击了按钮,  div里面的文字会发生变化
        // 1. 获取元素 
        var btn = document.querySelector('button');
        var div = document.querySelector('div');
        // 2.注册事件
        btn.onclick = function() {
            //  = '2019-6-6';
            div.innerHTML = getDate();
        }
        function getDate() {
            var date = new Date();
            // 我们写一个 2019年 5月 1日 星期三
            var year = date.getFullYear();
            var month = date.getMonth() + 1;
            var dates = date.getDate();
            var arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
            var day = date.getDay();
            return '今天是:' + year + '年' + month + '月' + dates + '日 ' + arr[day];
        }
    </script>
</body>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
2.5.2 常用元素的属性操作
1. innerText、innerHTML 改变元素内容
2. src、href
3. id、alt、title
  • 1
  • 2
  • 3
<body>
    <button id="ldh">刘德华</button>
    <button id="zxy">张学友</button> <br>
    <img src="images/" alt="" title="刘德华">
    <script>
        // 修改元素属性  src
        // 1. 获取元素
        var ldh = document.getElementById('ldh');
        var zxy = document.getElementById('zxy');
        var img = document.querySelector('img');
        // 2. 注册事件  处理程序
        zxy.onclick = function() {
            img.src = 'images/';
            img.title = '张学友思密达';
        }
        ldh.onclick = function() {
            img.src = 'images/';
            img.title = '刘德华';
        }
    </script>
</body>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
2.5.3 表单元素的属性操作

利用 DOM 可以操作如下表单元素的属性:

type、value、checked、selected、disabled
  • 1
<body>
    <button>按钮</button>
    <input type="text" value="输入内容">
    <script>
        // 1. 获取元素
        var btn = document.querySelector('button');
        var input = document.querySelector('input');
        // 2. 注册事件 处理程序
        btn.onclick = function() {
            // 表单里面的值 文字内容是通过 value 来修改的
            input.value = '被点击了';
            // 如果想要某个表单被禁用 不能再点击 disabled  我们想要这个按钮 button禁用
            //  = true;
            this.disabled = true;
            // this 指向的是事件函数的调用者 btn
        }
    </script>
</body>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
2.5.4 样式属性操作
1.  行内样式操作
2.  类名样式操作
  • 1
  • 2

注意:
里面的样式采取驼峰命名法 比如 fontSize、 backgroundColor
修改 style 样式操作,产生的是行内样式,CSS 权重比较高

方式1:通过操作style属性

元素对象的style属性也是一个对象!

元素对象.style.样式属性 = 值;

	div.style.backgroundColor = 'purple';
  • 1
方式2:通过操作className属性

元素对象.className = 值;

因为class是关键字,所有使用className。

className 会直接更改元素的类名,会覆盖原先的类名。

div.className = 'first change';
  • 1
2.5.5 排他思想

如果有同一组元素,我们想要某一个元素实现某种样式, 需要用到循环的排他思想算法:

  1. 所有元素全部清除样式(干掉其他人)
  2. 给当前元素设置样式 (留下我自己)
  3. 注意顺序不能颠倒,首先干掉其他人,再设置自己
2.5.6 自定义属性
2.5.6.1 获取属性值
 element.属性
 ('属性');
  • 1
  • 2

区别:
element.属性 获取内置属性值(元素本身自带的属性)
 (‘属性’); 主要获得自定义的属性 (标准) 我们程序员自定义的属性

2.5.6.2 设置属性值
element.属性 = ‘值’ 设置内置属性值。
 ('属性', '值');
  • 1
  • 2

区别:
 element.属性 设置内置属性值
 (‘属性’); 主要设置自定义的属性 (标准)

2.5.6.3. 移除属性
 ('属性');
  • 1
2.5.7 H5自定义属性

自定义属性目的:是为了保存并使用数据。有些数据可以保存到页面中而不用保存到数据库中。

自定义属性获取是通过getAttribute(‘属性’) 获取。
但是有些自定义属性很容易引起歧义,不容易判断是元素的内置属性还是自定义属性。

H5规定自定义属性data-开头做为属性名并且赋值。

 <div data-index="1"></div>
  • 1

或者使用 JS 设置

('data-index', 2)
  • 1

获取属性

兼容性获取 (‘data-index’);
H5新增  
或者 ['index'] 
  • 1
  • 2
  • 3

dateset只能获取data-开头的自定义属性

2.6 节点操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-31Uh43UB-1618047359427)(/2021/03/23/)]

节点至少拥有nodeType(节点类型)、nodeName(节点名称)和nodeValue(节点值)这三个
基本属性。

  • 元素节点 nodeType 为 1
  • 属性节点 nodeType 为 2
  • 文本节点 nodeType 为 3 (文本节点包含文字、空格、换行等)
    我们在实际开发中,节点操作主要操作的是元素节点

节点层级

DOM HTML tree

2.6.1 父级节点
node.parentNode
  • 1

parentNode 属性可返回某节点最近的一个父节点

没有父节点则返回 null

2.6.2 子节点
1	 (标准)
  • 1

返回包含指定节点的子节点的集合,该集合为即时更新的集合。

注意:返回值里面包含了所有的子节点,包括元素节点,文本节点等。
如果只想要获得里面的元素节点,则需要专门处理。 所以我们一般不提倡使用childNodes

var ul = document. querySelector(‘ul’);
for(var i = 0; i < ul.childNodes.length;i++) {
	if (ul.childNodes[i].nodeType == 1) {
	// [i] 是元素节点
	console.log(ul.childNodes[i]);
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
2	 (非标准)
  • 1

是一个只读属性,返回所有的子元素节点。它只返回子元素节点,其余节点不返
回 (这个是我们重点掌握的)

3	
  • 1

firstChild返回第一个子节点,找不到则返回null。同样,也是包含所有的节点。

4	
  • 1

lastChild 返回最后一个子节点,找不到则返回null。同样,也是包含所有的节点。

5	
  • 1

返回第一个子元素节点,找不到则返回null。

 6	
  • 1

返回最后一个子元素节点,找不到则返回null。

firstElementChild和lastElementChild有兼容性问题,ie9以上才支持

解决方案:

  1. 如果想要第一个子元素节点,可以使用 [0]
  2. 如果想要最后一个子元素节点,可以使用 [ - 1]
2.6.3 兄弟节点

  • 1

返回当前元素的下一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点。

2. 
  • 1

previousSibling 返回当前元素上一个兄弟元素节点,找不到则返回null。同样,也是包含所有的节点。

3. 
  • 1

nextElementSibling 返回当前元素下一个兄弟元素节点,找不到则返回null。

4. 
  • 1

previousElementSibling 返回当前元素上一个兄弟节点,找不到则返回null。

3和4方法有兼容性问题, IE9 以上才支持,可如下自行封装兼容性函数兼容性问题

function getNextElementSibling(element) {
	var el = element;
	while (el = el.nextSibling) {
		if (el.nodeType === 1) {
		return el;
		}
	}
	return null;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
2.6.4 创建添加节点
创建节点
document.createElement('tagName')
//例如
document.createElement('li')
  • 1
  • 2
  • 3

创建由 tagName 指定的 HTML 元素。因为这些元素原先不存在,
是根据我们的需求动态生成的,所以我们也称为动态创建元素节点。

创建后需要添加节点才有作用

添加节点
 1. node.appendChild(child)
  • 1

node.appendChild() 方法将一个节点添加到指定父节点的子节点列表末尾。类似于 CSS 里面的after 伪元素

2. node.insertBefore(child, 指定元素)
  • 1

() 方法将一个节点添加到父节点的指定子节点前面。类似于 CSS 里面的 before伪元素。

2.6.5 删除节点
(child)
  • 1

() 方法从 DOM 中删除一个子节点,返回删除的节点。

2.6.6 复制节点
()
  • 1

() 方法返回调用该方法的节点的一个副本,且是动态的。

注意:

  1. 如果括号参数为空或者为 false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
  2. 如果括号参数为 true ,则是深度拷贝,会复制节点本身以及里面所有的子节点。
2.6.7 创建元素总结

 ()
 innerHTML
 ()

区别

  1. 是直接将内容写入页面的内容流,会导致页面全部重绘

  2. innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘

  3. innerHTML 复制节点的时候,不会复制原先节点的事件,会存在内存泄露问题

  4. 如果页面创建元素很多,建议使用 innerHTML 因其效率更高(不要拼接字符串,采取数组形式拼接)

  5. 如果页面创建元素较少,建议使用 createElement()

总结:不同浏览器下,innerHTML 效率要比 creatElement 高

3.事件高级

3.1 注册事件

给元素添加事件,称为注册事件或者绑定事件。
注册事件有两种方式:传统方式和方法监听注册方式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZCEgDe6V-1618047392677)(/2021/03/23/)]

3.1.1 addEventListener 事件监听方式(IE9及以上)
eventTarget.addEventListener(type, listener[, useCapture])
  • 1

该方法接收三个参数:
 type:事件类型字符串,比如 click 、mouseover ,注意这里不要带 on
 listener:事件处理函数,事件发生时,会调用该监听函数
 useCapture:可选参数,是一个布尔值,默认是 false。学完 DOM 事件流后,我们再进一步学习

3.1.2 attachEvent 事件监听方式(了解就行)
(eventNameWithOn, callback)
  • 1

()方法将指定的监听器注册到 eventTarget(目标对象) 上,当该对象触
发指定的事件时,指定的回调函数就会被执行。
该方法接收两个参数:
 eventNameWithOn:事件类型字符串,比如 onclick 、onmouseover ,这里要带 on
 callback: 事件处理函数,当目标触发事件时回调函数被调用
注意:IE8 及早期版本支持

3.1.3 注册事件兼容性解决方案
function addEventListener(element, eventName, fn) {
	// 判断当前浏览器是否支持 addEventListener 方法
	if (element.addEventListener) {
		element.addEventListener(eventName, fn); // 第三个参数 默认是false
	} else if (element.attachEvent) {
		element.attachEvent('on' + eventName, fn);
	} else {
		// 相当于  = fn;
		element['on' + eventName] = fn;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.2 删除事件

3.2.1 删除事件方式

image-20210318170803605

// 传统注册事件
        divs[0].onclick = function() {
                alert('111');
                // 解绑事件,因为在事件里面,所有触发事件之后才会解绑
                divs[0].onclick = null;
            }
        // 监听事件,function没有名字,是匿名函数,不能直接remove
        // divs[1].addEventListener('click', function() {
        //     alert('2');
        // })
        // 应该这样写
        function fn() {
            alert('222');
            divs[1].removeEventListener('click', fn);
        }
        divs[1].addEventListener('click', fn);//里面的fn不需要写调用的()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
3.2.2 兼容性解决方法
function removeEventListener(element, eventName, fn) {
	// 判断当前浏览器是否支持 removeEventListener 方法
	if (element.removeEventListener) {
		element.removeEventListener(eventName, fn); // 第三个参数 默认是false
	} else if (element.detachEvent) {
		element.detachEvent('on' + eventName, fn);
	} else {
		element['on' + eventName] = null;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

3.3 DOM事件流

html中的标签都是相互嵌套的,我们可以将元素想象成一个盒子装一个盒子,document是最外面的大盒子。
当你单击一个div时,同时你也单击了div的父元素,甚至整个页面。

那么是先执行父元素的单击事件,还是先执行div的单击事件 ???
  • 1
  • 2
  • 3
  • 4

事件流描述的是从页面中接收事件的顺序。
事件发生时会在元素节点之间按照特定的顺序传播,这个传播过程即 DOM 事件流。

比如:我们给页面中的一个div注册了单击事件,当你单击了div时,也就单击了body,单击了html,单击了document。
  • 1

image-20210319162602484

事件冒泡: IE 最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点的过程。
事件捕获: 网景最早提出,由 DOM 最顶层节点开始,然后逐级向下传播到到最具体的元素接收的过程。

注意

  1. JS 代码中只能执行捕获或者冒泡其中的一个阶段。
  2. onclick 和 attachEvent 只能得到冒泡阶段。
  3. addEventListener(type, listener[, useCapture])第三个参数如果是 true,表示在事件捕
    获阶段调用事件处理程序;如果是 false(不写默认就是false),表示在事件冒泡阶段调用事件处理
    程序。
  4. 实际开发中我们很少使用事件捕获,我们更关注事件冒泡。
  5. 有些事件是没有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave
  6. 事件冒泡有时候会带来麻烦,有时候又会帮助很巧妙的做某些事件,我们后面讲解。
// 3. 捕获阶段 如果addEventListener 第三个参数是 true 那么则处于捕获阶段  document -> html -> body -> father -> son
        var son = document.querySelector('.son');
        son.addEventListener('click', function() {
            alert('son');
        }, true);
        var father = document.querySelector('.father');
        father.addEventListener('click', function() {
            alert('father');
        }, true);
        // 4. 冒泡阶段 如果addEventListener 第三个参数是 false 或者 省略 那么则处于冒泡阶段  son -> father ->body -> html -> document
        var son = document.querySelector('.son');
        son.addEventListener('click', function() {
            alert('son');
        }, false);
        var father = document.querySelector('.father');
        father.addEventListener('click', function() {
            alert('father');
        }, false);
        document.addEventListener('click', function() {
            alert('document');
        })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

3.4 事件对象

3.4.1 什么是事件对象
eventTarget.onclick = function(event) {}
eventTarget.addEventListener('click', function(event) {}// 这个 event 就是事件对象,我们还喜欢的写成 e 或者 evt
  • 1
  • 2
  • 3

事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象。是自己生成的,不需要我们去写。

比如:

  1. 谁绑定了这个事件。
  2. 鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置。
  3. 键盘触发事件的话,会得到键盘的相关信息,如按了哪个键。
3.4.2 事件对象的使用语法

事件触发发生时就会产生事件对象,并且系统会以实参的形式传给事件处理函数。

所以,在事件处理函数中声明1个形参用来接收事件对象。
image-20210320171207677

3.4.3 事件对象兼容问题

事件对象本身的获取存在兼容问题

  1. 标准浏览器中是浏览器给方法传递的参数,只需要定义形参 e 就可以获取到。

  2. 在 IE6~8 中,浏览器不会给方法传递参数,如果需要的话,需要到 中获取查找。
    解决:

e = e || window.event;
//只要“||”前面为false, 不管“||”后面是true 还是 false,都返回 “||” 后面的值。
//只要“||”前面为true, 不管“||”后面是true 还是 false,都返回 “||” 前面的值。
  • 1
  • 2
  • 3
3.4.4 事件对象的常见属性和方法

image-20210320182140039

image-20210320182446882

        //  返回触发事件的对象(在这里是鼠标点击的对象)
        //   this返回的是绑定事件的对象
        var div = document.querySelector('div');
        div.addEventListener('click', function(e) {
            console.log(e.target);
            console.log(this);
        })

        var ul = document.querySelector('ul');
        ul.addEventListener('click', function(e) {
            // 我们给ul绑定了事件,那么this就指向ul
            console.log(this);
            // 我们点击的是li,所以指向li
            console.log(e.target);
        })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

3.5 阻止默认行为

        var a = document.querySelector('a');
        a.addEventListener('click', function(e) {
            // DOM标准写法
            e.preventDefault();
        })

        // 传统注册方式
        a.onclick = function(e) {

            // 普通浏览器
            e.preventDefault();

            // 低版本历览器
            // ;

            // 所有都能用,无兼容性问题
            // return false;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

3.6 阻止事件冒泡

标准写法:利用事件对象里面的 stopPropagation()方法

e.stopPropagation()
  • 1

非标准写法:IE 6-8 利用事件对象 cancelBubble 属性

 = true;
  • 1
        var son = document.querySelector('.son');
        son.addEventListener('click', function(e) {
            alert('son');
            // 阻止冒泡
            e.stopPropagation();
        })
        //father没有阻止冒泡,依旧会触发document的点击事件
        var father = document.querySelector('.father');
        father.addEventListener('click', function(e) {
            alert('father');
        })
        document.addEventListener('click', function(e) {
            alert('document')
        })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

兼容性处理

if(e && e.stopPropagation){
	e.stopPropagation();
}else{
	window.event.cancelBubble = true;
}
  • 1
  • 2
  • 3
  • 4
  • 5

3.7 事件委托

3.7.1 什么是事件委托
把事情委托给别人,代为处理。
  • 1

事件委托也称为事件代理,在 jQuery 里面称为事件委派。

说白了就是,不给子元素注册事件,给父元素注册事件,把处理代码在父元素的事件中执行。
  • 1

image-20210321131911687

3.7.2 事件委托的原理

不是每个子节点单独设置事件监听器,而是事件监听器设置在其父节点上,然后利用冒泡原理影响设置每个子节点。
以上案例:给 ul 注册点击事件,然后利用事件对象的 target 来找到当前点击的 li,因为点击 li,事件会冒泡到 ul 上,
ul 有注册事件,就会触发事件监听器。

3.7.3 事件委托的作用

只操作了一次 DOM ,提高了程序的性能

案例

        var ul = document.querySelector('ul');
        ul.addEventListener('mouseover', function(e) {
            e.target.style.color = 'pink';
        })
        ul.addEventListener('mouseout', function(e) {
            e.target.style.color = 'black';
        })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4 常用鼠标事件

image-20210321133236288

4.1 禁止鼠标右键和选中
// 1.阻止右键菜单
document.addEventListener('contextmenu', function(e) {
	e.preventDefault();
})
// 2.禁止鼠标选中
document.addEventListener('selectstart', function(e) {
    e.preventDefault();
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
4.2 鼠标事件对象

event对象代表事件的状态,跟事件相关的一系列信息的集合。现阶段我们主要是用鼠标事件对象MouseEvent键盘事件对象 KeyboardEvent

image-20210321135549194

        // 鼠标事件对象 MouseEvent
        document.addEventListener('click', function(e) {
            // 1. client 鼠标在可视区的x和y坐标
            console.log(e.clientX);
            console.log(e.clientY);
            console.log('---------------------');

            // 2. page 鼠标在页面文档的x和y坐标
            console.log(e.pageX);
            console.log(e.pageY);
            console.log('---------------------');

            // 3. screen 鼠标在电脑屏幕的x和y坐标
            console.log(e.screenX);
            console.log(e.screenY);
        })

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

5 常用的键盘事件

5.1 键盘事件

image-20210321142429117

注意: onkeypress 和前面2个的区别是,它不识别功能键,比如左右箭头,shift 等。

执行顺序:down -> press -> up

5.2 键盘时间对象

键盘事件对象 说明
keycode 返回该键的ASCII值

注意: onkeydown 和 onkeyup 不区分字母大小写,onkeypress 区分字母大小写。

案例:按下s键,光标就定位到搜索框

    var input = document.querySelector('input');
    document.addEventListener('keyup', function(e) {
       // keyup和keydown不区分字母大小写
       if (e.keyCode == 83) {
           input.focus();
       }
    })
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

案例:京东快递单号查询

image-20210321154912421

<div class="search">
    <div class="con"></div>
    <input type="text" placeholder="请输入您的快递单号" class="jd">
</div>

<script>
var con = document.querySelector('.con');
var jd_input = document.querySelector('.jd');
jd_input.addEventListener('keyup', function(e) {
   if (this.value == '') {
     con.style.display = 'none'
   } else {
     con.style.display = 'block'
     con.innerText = this.value;
    }
})
// 获得焦点且内容不为空时,显示con盒子
jd_input.addEventListener('focus', function() {
    if (this.value != '') {
       con.style.display = 'block'
    }
})
// 失去焦点隐藏con盒子
jd_input.addEventListener('blur', function() {
   con.style.display = 'none';
})
</script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

6 BOM

6.1 什么是BOM

​ BOM(Browser Object Model)即浏览器对象模型,它提供了独立于内容而与浏览器窗口进行交互的对象,其核心对象是 window。

​ BOM 由一系列相关的对象构成,并且每个对象都提供了很多方法与属性。

​ BOM 缺乏标准,JavaScript 语法的标准化组织是 ECMA,DOM 的标准化组织是 W3C,BOM 最初是Netscape 浏览器标准的一部分。
image-20210321153725053

6.2 BOM的构成

BOM比DOM更大,它包含DOM

image-20210321154729617

6.3 *对象window

window 对象是浏览器的*对象,它具有双重角色。

  1. 它是 JS 访问浏览器窗口的一个接口。
  2. 它是一个全局对象。定义在全局作用域中的变量、函数都会变成 window 对象的属性和方法。
    在调用的时候可以省略 window,前面学习的对话框都属于 window 对象方法,如 alert()、prompt() 等。

注意:window下的有一个特殊属性

6.4 window对象的常见事件

6.4.1 窗口加载事件

①onload

 = function(){}
或者
("load",function(){});
  • 1
  • 2
  • 3

是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS文件等), 就调用的处理函数。

注意:

  1. 有了 就可以把 JS 代码写到页面元素的上方,因为 onload 是等页面内容全部加载完毕,再去执行处理函数。
  2. 传统注册事件方式 只能写一次,如果有多个,会以最后一个 为准。
  3. 如果使用 addEventListener 则没有限制
window.onload = function() {
var btn = document.querySelector('button');
btn.addEventListener('click', function() {
    alert('哦豁~');
})
}

window.addEventListener('load', function() {
  var btn = document.querySelector('button');
  btn.addEventListener('click', function() {
      alert('哦豁~');
  })
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

②DOMContentLoaded

document.addEventListener('DOMContentLoaded',function(){})
  • 1

DOMContentLoaded 事件触发时,仅当DOM加载完成,不包括样式表,图片,flash等等。(Ie9以上才支持)
如果页面的图片很多的话, 从用户访问到onload触发可能需要较长的时间, 交互效果就不能实现,必然影响用户的体验,此时用DOMContentLoaded 事件比较合适。

6.4.2 调整窗口大小事件
window.onresize = function(){}
window.addEventListener("resize",function(){});
  • 1
  • 2

是调整窗口大小加载事件, 当触发时就调用的处理函数。

注意:

  1. 只要窗口大小发生像素变化,就会触发这个事件。

  2. 我们经常利用这个事件完成响应式布局。

    当前屏幕的宽度

//宽度超过800显示div,小于800隐藏div   
<script>
        window.addEventListener('load', function() {
            var div = document.querySelector('div');
            window.addEventListener('resize', function() {
                if (window.innerWidth <= 800) {
                    div.style.display = 'none'
                } else {
                    div.style.display = 'block'
                }
            })
        })
    </script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

6.5 定时器

6.5.1 setTimeout() 炸弹定时器
(调用函数, [延迟的毫秒数]);
  • 1

setTimeout() 方法用于设置一个定时器,该定时器在定时器到期后执行调用函数。

image-20210321200200695

注意:

  1. window 可以省略。
  2. 这个调用函数可以直接写函数,或者写函数名或者采取**字符串‘函数名()’**三种形式。第三种不推荐
  3. 延迟的毫秒数省略默认是 0,如果写,必须是毫秒。
  4. 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符。
function callback() {
    console.log('2');
}
// 直接写函数,timer1是标识符
var timer1 = window.setTimeout(function() {
    console.log('1');
}, 5000)
// 写函数名
var timer2 = window.setTimeout(callback, 5000)
//字符串‘函数名()',不提倡
var timer3 = window.setTimeout('callback()', 5000)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
6.5.2 停止 setTimeout() 定时器
(timeoutID)
  • 1

clearTimeout()方法取消了先前通过调用 setTimeout() 建立的定时器。
注意:

  1. window 可以省略。
  2. 里面的参数就是定时器的标识符 。
6.5.3 setInterval() 闹钟定时器
(回调函数, [间隔的毫秒数]);
  • 1

setInterval() 方法重复调用一个函数,每隔这个时间,就去调用一次回调函数。

注意:

  1. window 可以省略。
  2. 这个调用函数可以直接写函数,或者写函数名或者采取字符串 ‘函数名()’ 三种形式。
  3. 间隔的毫秒数省略默认是 0,如果写,必须是毫秒,表示每隔多少毫秒就自动调用这个函数。
  4. 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符。
  5. 第一次执行也是间隔毫秒数之后执行,之后每隔毫秒数就执行一次。

案例:倒计时

<div class="hour"></div>
    <div class="minute"></div>
    <div class="second"></div>

    <script>
        var hour = document.querySelector('.hour');
        var minute = document.querySelector('.minute');
        var second = document.querySelector('.second');
        // 返回的是用户输入时间总的毫秒数
        var inputTime = +new Date('2021-3-21 18:00:00')

        // 开启定时器,因为定时器第一次执行也是一秒后,所以需要先执行一次       
        countDown();
        setInterval(countDown, 1000)

        function countDown() {
            // 返回的是当前时间总的毫秒数
            var nowTime = +new Date();
            // times是剩余时间总的秒数 
            var times = (inputTime - nowTime) / 1000;
            var h = parseInt(times / 60 / 60 % 24); //时
            h = h < 10 ? '0' + h : h;
            // 把剩余的小时给 小时黑色盒子
            hour.innerHTML = h;
            // 分
            var m = parseInt(times / 60 % 60);
            m = m < 10 ? '0' + m : m;
            minute.innerHTML = m;
            // 当前的秒
            var s = parseInt(times % 60);
            s = s < 10 ? '0' + s : s;
            second.innerHTML = s;
        }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
6.5.4 停止 setInterval() 定时器
(intervalID);
  • 1

clearInterval()方法取消了先前通过调用 setInterval()建立的定时器。

案例:短信发送冷却

    手机号码: <input type="text"> <button>发送</button>
    <!-- ① 按钮点击之后,会禁用 disabled 为true
    ② 同时按钮里面的内容会变化, 注意 button 里面的内容通过 innerHTML修改
    ③ 里面秒数是有变化的,因此需要用到定时器
    ④ 定义一个变量,在定时器里面,不断递减
    ⑤ 如果变量为0 说明到了时间,我们需要停止定时器,并且复原按钮初始状态。 -->
    <script>
        var btn = document.querySelector('button');
        btn.addEventListener('click', function() {
            btn.disabled = 'true';
            var s = 3;
            btn.innerHTML = s + 's'
            var timer = setInterval(function() {
                if (s == 0) {
                    clearInterval(timer);
                    btn.disabled = false;
                    btn.innerHTML = '发送';
                } else {
                    s--;
                    btn.innerHTML = s + 's';
                }
            }, 1000)
        })
    </script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

6.6 this指向问题

this的指向在函数定义的时候是确定不了的,只有函数执行的时候才能确定this到底指向谁,一般情况下this的最终指向的是那个调用它的对象。

现阶段,我们先了解一下几个this指向

  1. 全局作用域或者普通函数中this指向全局对象window(注意定时器里面的this指向window)

  2. 方法调用中谁调用this指向谁

  3. 构造函数中this指向构造函数的实例

6.7 JS执行队列

6.7.1 JS是单线程

JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。这是因为 Javascript 这门脚本语言诞生的使命所致——JavaScript 是为处理页面中用户的交互,以及操作 DOM 而诞生的。比如我们对某个 DOM 元素进行添加和删除操作,不能同时进行。 应该先进行添加,之后再删除。

单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。这样所导致的问题是: 如果JS 执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。

 //以下代码执行的结果是什么?
 console.log(1);
 
 setTimeout(function () {
     console.log(3);
 }, 0);
 
 console.log(2);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

打印1,后打印2,最后打印3

6.7.2 同步任务和异步任务

为了解决这个问题,利用多核 CPU 的计算能力,HTML5 提出 Web Worker 标准,允许 JavaScript 脚本创建多个线程。于是,JS 中出现了同步和异步。

同步
前一个任务结束后再执行后一个任务,程序的执行顺序与任务的排列顺序是一致的、同步的。比如做饭的同步做
法:我们要烧水煮饭,等水开了(10分钟之后),再去切菜,炒菜。
异步
你在做一件事情时,因为这件事情会花费很长时间,在做这件事的同时,你还可以去处理其他事情。比如做
饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。
他们的本质区别: 这条流水线上各个流程的执行顺序不同。
image-20210321203157758

6.7.3 JS执行机制(事件循环)
  1. 先执行执行栈中的同步任务。

  2. 异步任务(回调函数)放入任务队列中。

  3. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任
    务结束等待状态,进入执行栈,开始执行。

image-20210321203038160

1551435398306

由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环( event loop)。

6.8 location 对象

6.8.1 什么是location对象

window 对象给我们提供了一个 location 属性用于获取或设置窗体的 URL,并且可以用于解析 URL 。 因为这个属性返回的是一个对象,所以我们将这个属性也称为 location 对象。

6.8.2 URL

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

URL 的一般语法格式为:

protocol://host[:port]/path/[?query]#fragment
/?name=andy&age=18#link
  • 1
  • 2

image-20210321202754445

6.8.3 location 对象的属性

image-20210321204251373

重点记住: href 和 search

案例:url传参

//
<form action="">
        <!-- name属性一定不能漏掉,不然search为空 -->
        <input type="text" name='uname' class="uname">
        <input type="submit" value="登录" class="login">
    </form>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
//
        var div = document.querySelector('div');
        console.log(location.search);
        var params = location.search.substr(1);
        console.log(params);
        var strs = params.split('=');
        console.log(strs);
        div.innerHTML = '欢迎回来,' + strs[1] + '!';
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
6.8.4 location 对象的方法

image-20210321204240243

var btn = document.querySelector('button');
btn.addEventListener('click', function() {
 // 记录浏览历史,所以可以实现后退功能
location.assign('');
// 不记录浏览历史,所以不可以实现后退功能
location.replace('');
    location.reload(true);
})

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

6.9 navigator对象

navigator 对象包含有关浏览器的信息,它有很多属性,我们最常用的是 userAgent,该属性可以返回由客户机发送服务器的 user-agent 头部的值。

下面前端代码可以判断用户那个终端打开页面,实现跳转

if((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
    window.location.href = "";     //手机
 } else {
    window.location.href = "";     //电脑
 }
  • 1
  • 2
  • 3
  • 4
  • 5

6.10 history对象

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

image-20210321212120008

history对象一般在实际开发中比较少用,但是会在一些 OA 办公系统中见到。


7 PC端网页特效

7.1 元素偏移量offset系列

7.1.1 offset概述

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

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

image-20210321215230447

image-20210322192933252

7.1.2 offset 与 style 区别
offset
  • offset 可以得到任意样式表中的样式值

  • offset 系列获得的数值是没有单位的

  • offsetWidth 包含padding+border+width

  • offsetWidth 等属性是只读属性,只能获取不能赋值

  • 所以,我们想要获取元素大小位置,用offset更合适

style
  • style 只能得到行内样式表中的样式值

  • 获得的是带有单位的字符串

  • 获得不包含padding和border 的值

  • 是可读写属性,可以获取也可以赋值

  • 所以,我们想要给元素更改值,则需要用style改变

因为平时我们都是给元素注册触摸事件,所以重点记住 targetTocuhes

7.1.3 案例

案例:获取鼠标在盒子内的坐标

var div = document.querySelector('div');
div.addEventListener('mousemove', function(e) {
   var x = e.pageX - this.offsetLeft;
   var y = e.pageY - div.offsetTop;
   div.innerHTML = x + ',' + y;
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

案例 :模态框拖拽

<body>
    <div class="login-header"><a id="link" href="javascript:;">点击,弹出登录框</a></div>
    <div class="login">
        <div id="title" class="login-title">会员登录
            <span><a id="closeBtn" href="javascript:void(0);" class="close-login">关闭</a></span>
        </div>
        <div class="login-input-content">
            <div class="login-input">
                <label for="">用户名:</label>
                <input type="text" placeholder="请输入用户名" name='info[username]' id="username" class="list-input">
            </div>
            <div class="login-input">
                <label for="">登录密码:</label>
                <input type="password" placeholder="请输入密码" name="info[password]" id="password" class="list-input">
            </div>
        </div>
        <button id="logingBtn" class="login-button">登录</button>
    </div>
    <!-- 遮盖层 -->
    <div id="bg" class="login-bg"></div>

    <script>
        var login = document.querySelector('.login');
        var mask = document.querySelector('.login-bg');
        var link = document.querySelector('#link');
        var closeBtn = document.querySelector('.close-login');
        // 显示登录框
        link.addEventListener('click', function() {
                mask.style.display = 'block';
                login.style.display = 'block';
            })
            // 关闭按钮
        closeBtn.addEventListener('click', function() {
                mask.style.display = 'none';
                login.style.display = 'none';
            })
            // 拖拽模态框
            // 鼠标按下
        var titile = document.querySelector('#title');
        titile.addEventListener('mousedown', function(e) {
            // 鼠标在盒子内的坐标=鼠标在页面坐标-盒子距页面坐标
            var x = e.pageX - login.offsetLeft;
            var y = e.pageY - login.offsetTop;
            // (x + ',' + y);
            // 移动事件注意要在按下事件内
            var move = function(e) {
                // 注意单位,赋值要用style不能用offset
                login.style.left = e.pageX - x + 'px';
                login.style.top = e.pageY - y + 'px';
            }
            this.addEventListener('mousemove', move);

            this.addEventListener('mouseup', function() {
                titile.removeEventListener('mousemove', move)
            })
        })
    </script>
</body>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

案例:仿京东放大镜

window.addEventListener('load', function() {
    var preview_img = document.querySelector('.preview_img')
    var mask = document.querySelector('.mask')
    var big = document.querySelector('.big');

    // 鼠标经过显示透明黄色区域和放大区域
    preview_img.addEventListener('mouseover', function() {
            mask.style.display = 'block';
            big.style.display = 'block';
        })
        // 鼠标经过隐藏透明黄色区域和放大区域
    preview_img.addEventListener('mouseout', function() {
        mask.style.display = 'none';
        big.style.display = 'none';
    })

    preview_img.addEventListener('mousemove', function(e) {
        // 鼠标在盒子内的距离。注意看父亲是否有定位,如果有定位需要把父亲的加进去
        var x = e.pageX - this.offsetLeft;
        var y = e.pageY - this.offsetTop;
        // 把鼠标坐标 - 盒子宽高一半 给mask,先不直接赋值
        maskX = x - mask.offsetWidth / 2;
        maskY = y - mask.offsetHeight / 2;

        // 如果盒子左侧距离和顶部距离小于0或超过大盒子宽度-小盒子宽度
        // 遮挡层的最大移动距离
        var maskMax = preview_img.offsetWidth - mask.offsetWidth
        if (maskX <= 0) {
            maskX = 0
        }
        // 超过大盒子-小盒子
        else if (maskX >= maskMax) {
            maskX = maskMax;
        }
        if (maskY <= 0) {
            maskY = 0;
        } else if (maskY >= maskMax) {
            maskY = maskMax;
        }
        // 赋值
        mask.style.left = maskX + 'px';
        mask.style.top = maskY + 'px';

        // 放大模块
        // 大图片的移动距离 = 遮挡层移动距离 * 大图片最大移动距离 / 遮挡层的最大移动距离
        var bigImg = document.querySelector('.bigImg');
        // 大图片最大移动距离
        var bigMax = bigImg.offsetWidth - big.offsetWidth;
        var bigX = maskX * bigMax / maskMax;
        var bigY = maskY * bigMax / maskMax;
        // (bigX);
        bigImg.style.left = -bigX + 'px';
        bigImg.style.top = -bigY + 'px';
    })
})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

7.2 元素可视区 client 系列

7.2.1 client概述

client 翻译过来就是客户端,我们使用 client 系列的相关属性来获取元素可视区的相关信息。通过 client系列的相关属性可以动态的得到该元素的边框大小、元素大小等。

image-20210322144517376

clintWidth和offsetWidth的区别就是client不包含边框等

7.2.2 淘宝 源码分析

立即执行函数,不需要调用,立马就能自己执行的函数,也可以传递参数

(function(){})()或者( function(){} () )

主要作用: 创建一个独立的作用域,里面所有的变量都是局部变量, 避免了命名冲突问题

下面三种情况都会刷新页面都会触发 load 事件。

标签的超链接

2.F5或者刷新按钮(强制刷新)

3.前进后退按钮

但是 火狐中,有个特点,有个“往返缓存”,这个缓存中不仅保存着页面数据,还保存了DOM和JavaScript的状态;实际上是将整个页面都保存在了内存里。

所以此时后退按钮不能刷新页面。

此时可以使用 pageshow事件来触发,这个事件在页面显示时触发,无论页面是否来自缓存。在重新加载页面中,pageshow会在load事件触发后触发;根据事件对象中的persisted来判断是否是缓存中的页面触发的pageshow事件

注意这个事件给window添加。

7.3 元素滚动 scroll 系列

7.3.1. scroll 概述

scroll 翻译过来就是滚动的,我们使用 scroll 系列的相关属性可以动态的得到该元素的大小、滚动距离等。

image-20210322204227838

scrollWidth不包含边框,包含padding和内容区

image-20210322205229369

7.3.2 页面被卷去的头部

如果浏览器的高(或宽)度不足以显示整个页面时,会自动出现滚动条。当滚动条向下滚动时,页面上面被隐藏掉的高度,我们就称为页面被卷去的头部。滚动条在滚动时会触发 onscroll事件。

页面被卷去的头部:可以通过 获得

如果是被卷去的左侧

注意,元素被卷去的头部是 , 如果是页面被卷去的头部 则是

案例:仿淘宝固定右侧侧边栏

    <script>
        var sliderBar = document.querySelector('.slider-bar')
        var goTop = document.querySelector('.goTop')
        var banner = document.querySelector('.banner');
        var main = document.querySelector('.main');
        // 顶部距页面边距的距离,写在函数外面更加安全
        var bannerTop = banner.offsetTop;
        var mainTop = main.offsetTop;
        // 侧边栏在函数触发时的位置
        var sliderBarTop = sliderBar.offsetTop - bannerTop;

        // 页面滚动时触发函数
        document.addEventListener('scroll', function() {
            // 如果页面被卷曲上部值大于banner顶部距离
            if (window.pageYOffset >= bannerTop) {
                sliderBar.style.position = 'fixed';
                sliderBar.style.top = sliderBarTop + 'px';
            } else {
                sliderBar.style.position = 'absolute';
                sliderBar.style.top = '250px';
            }
            if (window.pageYOffset >= mainTop) {
                goTop.style.display = 'block';
            } else {
                goTop.style.display = 'none';

            }
        })
    </script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
7.3.3 页面被卷去的头部兼容性解决方案

需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法:

  1. 声明了 DTD,使用
  2. 未声明 DTD,使用
  3. 新方法 和 ,IE9 开始支持
function getScroll() {
return {
left: window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft||0,
top: window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0
};
}
使用的时候 getScroll().left
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

7.4 三大系列总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QbxW8Afa-1618047757305)(/2021/03/24/)]

image-20210324122728334
image-20210323155453382
image-20210323155435632

他们主要用法:

系列 经常用于获得元素位置 offsetLeft offsetTop

经常用于获取元素大小 clientWidth clientHeight

经常用于获取滚动距离 scrollTop scrollLeft

4.注意页面滚动的距离通过 获得

7.5 mouseenter和mouseover的区别

  • 当鼠标移动到元素上时就会触发mouseenter 事件

  • 类似 mouseover,它们两者之间的差别是:

    mouseover 鼠标经过自身盒子会触发,经过子盒子还会触发。mouseenter 只会经过自身盒子触发

    之所以这样,就是因为mouseenter不会冒泡

    跟mouseenter搭配鼠标离开 mouseleave 同样不会冒泡

7.6 动画函数封装

7.6.1 动画实现原理

核心原理:通过定时器 setInterval() 不断移动盒子位置。

实现步骤:

  1. 获得盒子当前位置
  2. 让盒子在当前位置加上1个移动距离
  3. 利用定时器不断重复这个操作
  4. 加一个结束定时器的条件
  5. 注意此元素需要添加定位,才能使用
7.6.2. 动画函数给不同元素记录不同定时器

如果多个元素都使用这个动画函数,每次都要var 声明定时器。我们可以给不同的元素使用不同的定时器(自己专门用自己的定时器)。

//需要传递 obj(动画对象)和target(移动的距离)
function animate(obj, target) {
  // 先清除原先的定时器,防止多个定时器同时执行
  clearInterval(obj.timer);
  //每次都需要声明timer,可以给不同的动画对象添加不同的timer属性
  //var timer = setInterval(function() {
  obj.timer = setInterval(function() {
     if (obj.offsetLeft >= target) {
       // 停止动画
       clearInterval(obj.timer)
     } else {
       obj.style.left = obj.offsetLeft + 5 + 'px';
      }
  }, 50)
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
7.6.3 缓动效果原理

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

  1. 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。
  2. 核心算法: (目标值 - 现在的位置 ) / 10 做为每次移动的距离 步长
  3. 停止的条件是: 让当前盒子位置等于目标位置就停止定时器
  4. 注意步长值需要取整
//仍有bug,被移动对象最终停止位置可能不是我们规定的位置,因为除法可能除不尽
var step = (target - obj.offsetLeft) / 10;
//我们需要把步长改为整数,向上取整
step = Math.ceil(step);
obj.style.left = obj.offsetLeft + step + 'px';
  • 1
  • 2
  • 3
  • 4
  • 5
7.6.4 动画函数多个目标值之间移动

可以让动画函数从 800 移动到 500。

当我们点击按钮时候,判断步长是正值还是负值

​ 1.如果是正值,则步长往大了取整

​ 2.如果是负值,则步长 向小了取整

//通过三元函数对步长取整进行调整,因为步长有正负,所以不需要特地判断最终位置和现在位置,直接+step就可以
step = step > 0 ? Math.ceil(step) : Math.floor(step);
  • 1
  • 2
7.6.5 动函数添加回调函数

**回调函数原理:**函数可以作为一个参数。将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调。

**回调函数写的位置:**定时器结束的位置。

function animate(obj, target, callback) {
    // (callback);  callback = function() {}  调用的时候 callback()

    // 先清除以前的定时器,只保留当前的一个定时器执行
    clearInterval(obj.timer);
    obj.timer = setInterval(function() {
        // 步长值写到定时器的里面
        // 把我们步长值改为整数 不要出现小数的问题
        // var step = ((target - ) / 10);
        var step = (target - obj.offsetLeft) / 10;
        step = step > 0 ? Math.ceil(step) : Math.floor(step);
        if (obj.offsetLeft == target) {
            // 停止动画 本质是停止定时器
            clearInterval(obj.timer);
            // 回调函数写到定时器结束里面
            // if (callback) {
            //     // 调用函数
            //     callback();
            // }
            callback && callback();
        }
        // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
        obj.style.left = obj.offsetLeft + step + 'px';

    }, 15);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
7.6.6 节流阀

防止轮播图按钮连续点击造成播放过快。

节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。

核心实现思路:利用回调函数,添加一个变量来控制,锁住函数和解锁函数。

开始设置一个变量var flag= true;

If(flag){flag = false; do something} 关闭水龙头

利用回调函数动画执行完毕, flag = true 打开水龙头

7.7 常见PC端特效案例

轮播图
window.addEventListener('load', function() {
    // 1. 获取元素
    var arrow_l = document.querySelector('.arrow-l');
    var arrow_r = document.querySelector('.arrow-r');
    var focus = document.querySelector('.focus');
    var focusWidth = focus.offsetWidth;
    // 2. 鼠标经过focus 就显示隐藏左右按钮
    focus.addEventListener('mouseenter', function() {
        arrow_l.style.display = 'block';
        arrow_r.style.display = 'block';
        clearInterval(timer);
        timer = null; // 清除定时器变量
    });
    focus.addEventListener('mouseleave', function() {
        arrow_l.style.display = 'none';
        arrow_r.style.display = 'none';
        timer = setInterval(function() {
            //手动调用点击事件
            arrow_r.click();
        }, 2000);
    });
    // 3. 动态生成小圆圈  有几张图片,我就生成几个小圆圈
    var ul = focus.querySelector('ul');
    var ol = focus.querySelector('.circle');
    // ();
    for (var i = 0; i < ul.children.length; i++) {
        // 创建一个小li 
        var li = document.createElement('li');
        // 记录当前小圆圈的索引号 通过自定义属性来做 
        li.setAttribute('index', i);
        // 把小li插入到ol 里面
        ol.appendChild(li);
        // 4. 小圆圈的排他思想 我们可以直接在生成小圆圈的同时直接绑定点击事件
        li.addEventListener('click', function() {
            // 干掉所有人 把所有的小li 清除 current 类名
            for (var i = 0; i < ol.children.length; i++) {
                ol.children[i].className = '';
            }
            // 留下我自己  当前的小li 设置current 类名
            this.className = 'current';
            // 5. 点击小圆圈,移动图片 当然移动的是 ul 
            // ul 的移动距离 小圆圈的索引号 乘以 图片的宽度 注意是负值
            // 当我们点击了某个小li 就拿到当前小li 的索引号
            var index = this.getAttribute('index');
            // 当我们点击了某个小li 就要把这个li 的索引号给 num  
            num = index;
            // 当我们点击了某个小li 就要把这个li 的索引号给 circle  
            circle = index;
            // num = circle = index;
            console.log(focusWidth);
            console.log(index);

            animate(ul, -index * focusWidth);
        })
    }
    // 把ol里面的第一个小li设置类名为 current
    ol.children[0].className = 'current';
    // 6. 克隆第一张图片(li)放到ul 最后面
    var first = ul.children[0].cloneNode(true);
    ul.appendChild(first);
    // 7. 点击右侧按钮, 图片滚动一张
    var num = 0;
    // circle 控制小圆圈的播放
    var circle = 0;
    // flag 节流阀
    var flag = true;
    arrow_r.addEventListener('click', function() {
        if (flag) {
            flag = false; // 关闭节流阀
            // 如果走到了最后复制的一张图片,此时 我们的ul 要快速复原 left 改为 0
            if (num == ul.children.length - 1) {
                ul.style.left = 0;
                num = 0;
            }
            num++;
            animate(ul, -num * focusWidth, function() {
                flag = true; // 打开节流阀
            });
            // 8. 点击右侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
            circle++;
            // 如果circle == 4 说明走到最后我们克隆的这张图片了 我们就复原
            if (circle == ol.children.length) {
                circle = 0;
            }
            // 调用函数
            circleChange();
        }
    });

    // 9. 左侧按钮做法
    arrow_l.addEventListener('click', function() {
        if (flag) {
            flag = false;
            if (num == 0) {
                num = ul.children.length - 1;
                ul.style.left = -num * focusWidth + 'px';

            }
            num--;
            animate(ul, -num * focusWidth, function() {
                flag = true;
            });
            // 点击左侧按钮,小圆圈跟随一起变化 可以再声明一个变量控制小圆圈的播放
            circle--;
            // 如果circle < 0  说明第一张图片,则小圆圈要改为第4个小圆圈(3)
            // if (circle < 0) {
            //     circle =  - 1;
            // }
            circle = circle < 0 ? ol.children.length - 1 : circle;
            // 调用函数
            circleChange();
        }
    });

    function circleChange() {
        // 先清除其余小圆圈的current类名
        for (var i = 0; i < ol.children.length; i++) {
            ol.children[i].className = '';
        }
        // 留下当前的小圆圈的current类名
        ol.children[circle].className = 'current';
    }
    // 10. 自动播放轮播图
    var timer = setInterval(function() {
        //手动调用点击事件
        arrow_r.click();
    }, 2000);

})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
返回顶部
  1. 带有动画的返回顶部
  2. 此时可以继续使用我们封装的动画函数
  3. 只需要把所有的left 相关的值改为 跟 页面垂直滚动距离相关就可以了
  4. 页面滚动了多少,可以通过 得到
  5. 最后是页面滚动,使用 (x,y)
  //1. 获取元素
        var sliderbar = document.querySelector('.slider-bar');
        var banner = document.querySelector('.banner');
        //  就是被卷去头部的大小 一定要写到滚动的外面
        var bannerTop = banner.offsetTop
            // 当我们侧边栏固定定位之后应该变化的数值
        var sliderbarTop = sliderbar.offsetTop - bannerTop;
        // 获取main 主体元素
        var main = document.querySelector('.main');
        var goBack = document.querySelector('.goBack');
        var mainTop = main.offsetTop;
        // 2. 页面滚动事件 scroll
        document.addEventListener('scroll', function() {
                // (11);
                //  页面被卷去的头部
                // ();
                // 3 .当我们页面被卷去的头部大于等于了 172 此时 侧边栏就要改为固定定位
                if (window.pageYOffset >= bannerTop) {
                    sliderbar.style.position = 'fixed';
                    sliderbar.style.top = sliderbarTop + 'px';
                } else {
                    sliderbar.style.position = 'absolute';
                    sliderbar.style.top = '300px';
                }
                // 4. 当我们页面滚动到main盒子,就显示 goback模块
                if (window.pageYOffset >= mainTop) {
                    goBack.style.display = 'block';
                } else {
                    goBack.style.display = 'none';
                }

            })
            // 3. 当我们点击了返回顶部模块,就让窗口滚动的页面的最上方
        goBack.addEventListener('click', function() {
            // 里面的x和y 不跟单位的 直接写数字即可
            // (0, 0);
            // 因为是窗口滚动 所以对象是window
            animate(window, 0);
        });

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
筋斗云案例
  1. 利用动画函数做动画效果
  2. 原先筋斗云的起始位置是0
  3. 鼠标经过某个小li,把当前小li的offsetLeft 位置做为目标值即可
  4. 鼠标离开某个小li,就把目标值设为 0
  5. 如果点击了某个小li, 就把li当前的位置存储起来,做为筋斗云的起始位置
 window.addEventListener('load', function() {
            // 1. 获取元素
            var cloud = document.querySelector('.cloud');
            var c_nav = document.querySelector('.c-nav');
            var lis = c_nav.querySelectorAll('li');
            // 2. 给所有的小li绑定事件 
            // 这个current 做为筋斗云的起始位置
            var current = 0;
            for (var i = 0; i < lis.length; i++) {
                // (1) 鼠标经过把当前小li 的位置做为目标值
                lis[i].addEventListener('mouseenter', function() {
                    animate(cloud, this.offsetLeft);
                });
                // (2) 鼠标离开就回到起始的位置 
                lis[i].addEventListener('mouseleave', function() {
                    animate(cloud, current);
                });
                // (3) 当我们鼠标点击,就把当前位置做为目标值
                lis[i].addEventListener('click', function() {
                    current = this.offsetLeft;
                });
            }
        })

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

8 移动端网页特效

8.1 触屏事件

8.1.1 触屏事件概述

移动端浏览器兼容性较好,我们不需要考虑以前 JS 的兼容性问题,可以放心的使用原生 JS 书写效果,但是移动端也有自己独特的地方。比如触屏事件 touch(也称触摸事件),Android和 IOS 都有。

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

常见的触屏事件如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h7uKE5MD-1618047817045)(/2021/03/24/)]

8.1.2 触摸事件对象(TouchEvent)

TouchEvent 是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少,等等

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

触摸事件对象重点我们看三个常见对象列表:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eTeognrt-1618047822076)(/2021/03/24/)]

因为平时我们都是给元素注册触摸事件,所以重点记住 targetTocuhes

        // 触摸事件对象
        // 1. 获取元素
        // 2. 手指触摸DOM元素事件
        var div = document.querySelector('div');
        div.addEventListener('touchstart', function(e) {
            // (e);
            // touches 正在触摸屏幕的所有手指的列表 
            // targetTouches 正在触摸当前DOM元素的手指列表
            // 如果侦听的是一个DOM元素,他们两个是一样的
            // changedTouches 手指状态发生了改变的列表 从无到有 或者 从有到无
            // 因为我们一般都是触摸元素 所以最经常使用的是 targetTouches
            console.log(e.targetTouches[0]);
            // targetTouches[0] 就可以得到正在触摸dom元素的第一个手指的相关信息比如 手指的坐标等等


        });
        // 3. 手指在DOM元素身上移动事件
        div.addEventListener('touchmove', function() {


        });
        // 4. 手指离开DOM元素事件
        div.addEventListener('touchend', function(e) {
            // (e);
            // 当我们手指离开屏幕的时候,就没有了 touches 和 targetTouches 列表
            // 但是会有 changedTouches
        });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
8.1.3 移动端拖动元素
  1. touchstarttouchmovetouchend 可以实现拖动元素

  2. 但是拖动元素需要当前手指的坐标值 我们可以使用 targetTouches[0] 里面的pageXpageY

  3. 移动端拖动的原理: 手指移动中,计算出手指移动的距离。然后用盒子原来的位置 + 手指移动的距离

  4. 手指移动的距离: 手指滑动中的位置 减去 手指刚开始触摸的位置
    拖动元素三步曲:

(1) 触摸元素 touchstart: 获取手指初始坐标,同时获得盒子原来的位置
(2) 移动手指 touchmove: 计算手指的滑动距离,并且移动盒子
(3) 离开手指 touchend:

注意: 手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动 ();

8.2 移动端常见特效

8.2.1 案例: 移动轮播图

移动端轮播图功能和基本PC端一致。

  1. 可以自动播放图片
  2. 手指可以拖动播放轮播图
8.2.2 案例分析:
  1. 自动播放功能
  2. 开启定时器
  3. 移动端移动,可以使用translate 移动
  4. 想要图片优雅的移动,请添加过渡效果

1551795152(1)

  1. 自动播放功能-无缝滚动

  2. 注意,我们判断条件是要等到图片滚动完毕再去判断,就是过渡完成后判断

  3. 此时需要添加检测过渡完成事件 transitionend

  4. 判断条件:如果索引号等于 3 说明走到最后一张图片,此时 索引号要复原为 0

  5. 此时图片,去掉过渡效果,然后移动

  6. 如果索引号小于0, 说明是倒着走, 索引号等于2

  7. 此时图片,去掉过渡效果,然后移动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KpojBiQu-1618047851368)(/2021/03/25/)]

8.3 classList 属性

classList属性是HTML5新增的一个属性,返回元素的类名。但是ie10以上版本支持。

该属性用于在元素中添加,移除及切换 CSS 类。有以下方法

添加类:

(’类名’);

focus.classList.add('current');
  • 1

移除类:

(’类名’);

focus.classList.remove('current');
  • 1

切换类:

(’类名’);

focus.classList.toggle('current');
  • 1

注意:以上方法里面,所有类名都不带点

8.4 click 延时解决方案

移动端 click 事件会有 300ms 的延时,原因是移动端屏幕双击会缩放(double tap to zoom) 页面。

解决方案:

  1. 禁用缩放。 浏览器禁用默认的双击缩放行为并且去掉300ms 的点击延迟。
<meta name="viewport" content="user-scalable=no">
  • 1

​ 2.利用touch事件自己封装这个事件解决300ms 延迟

/*原理就是:
1.  当我们手指触摸屏幕,记录当前触摸时间
2.  当我们手指离开屏幕, 用离开的时间减去触摸的时间
3.  如果时间小于150ms,并且没有滑动过屏幕, 那么我们就定义为点击*/
//封装tap,解决click 300ms 延时
function tap (obj, callback) {
        var isMove = false;
        var startTime = 0; // 记录触摸时候的时间变量
        obj.addEventListener('touchstart', function (e) {
            startTime = Date.now(); // 记录触摸时间
        });
        obj.addEventListener('touchmove', function (e) {
            isMove = true;  // 看看是否有滑动,有滑动算拖拽,不算点击
        });
        obj.addEventListener('touchend', function (e) {
            if (!isMove && (Date.now() - startTime) < 150) {  // 如果手指触摸和离开时间小于150ms 算点击
                callback && callback(); // 执行回调函数
            }
            isMove = false;  //  取反 重置
            startTime = 0;
        });
}
//调用  
  tap(div, function(){   // 执行代码  });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

​ 3.使用插件。fastclick 插件解决300ms 延迟。

1551797533(1)

9 移动端常用开发插件

9.1 什么是插件

动端要求的是快速开发,所以我们经常会借助于一些插件来帮我完成操作,那么什么是插件呢?

JS 插件是 js 文件,它遵循一定规范编写,方便程序展示效果,拥有特定功能且方便调用。如轮播图和瀑布流插件。

特点:它一般是为了解决某个问题而专门存在,其功能单一,并且比较小。

我们以前写的 也算一个最简单的插件

fastclick 插件解决 300ms 延迟。 使用延时

GitHub官网地址: /ftlabs/fastclick

9.2 插件的使用

  1. 引入 js 插件文件。
  2. 按照规定语法使用。
if ('addEventListener' in document) {
            document.addEventListener('DOMContentLoaded', function() {
                       FastClick.attach(document.body);
            }, false);
}
  • 1
  • 2
  • 3
  • 4
  • 5

9.3 Swiper 插件的使用

中文官网地址: /

  1. 引入插件相关文件。
  2. 按照规定语法使用

9.4 其他移动端常见插件

 superslide: http:///
 iscroll: /cubiq/iscroll

9.5 移动端视频插件

H5 给我们提供了 video 标签,但是浏览器的支持情况不同。

不同的视频格式文件,我们可以通过source解决。

但是外观样式,还有暂停,播放,全屏等功能我们只能自己写代码解决。

这个时候我们可以使用插件方式来制作。

我们可以通过 JS 修改元素的大小、颜色、位置等样式。

9.6 插件的使用总结

  1. 确认插件实现的功能
  2. 去官网查看使用说明
  3. 下载插件
  4. 打开demo实例文件,查看需要引入的相关文件,并且引入
  5. 复制demo实例文件中的结构html,样式css以及js代码

10 移动端常用开发框架

10.1 框架概述

框架,顾名思义就是一套架构,它会基于自身的特点向用户提供一套较为完整的解决方案。框架的控制权在框架本身,使用者要按照框架所规定的某种规范进行开发。

插件一般是为了解决某个问题而专门存在,其功能单一,并且比较小。

前端常用的框架有 Bootstrap、Vue、Angular、React 等。既能开发PC端,也能开发移动端

前端常用的移动端插件有 swiper、superslide、iscroll等。

框架: 大而全,一整套解决方案

插件: 小而专一,某个功能的解决方案

10.2 Bootstrap

Bootstrap 是一个简洁、直观、强悍的前端开发框架,它让 web 开发更迅速、简单。

它能开发PC端,也能开发移动端

Bootstrap JS插件使用步骤:

1.引入相关js 文件

2.复制HTML 结构

3.修改对应样式

4.修改相应JS 参数

11 本地存储

随着互联网的快速发展,基于网页的应用越来越普遍,同时也变的越来越复杂,为了满足各种各样的需求,会经常性在本地存储大量的数据,HTML5规范提出了相关解决方案。

11.1 本地存储特性

1、数据存储在用户浏览器中

2、设置、读取方便、甚至页面刷新不丢失数据

3、容量较大,sessionStorage约5M、localStorage约20M

4、只能存储字符串,可以将对象() 编码后存储

11.2

1、生命周期为关闭浏览器窗口

2、在同一个窗口(页面)下数据可以共享

3、以键值对的形式存储使用

存储数据:

sessionStorage.setItem(key, value)
  • 1

获取数据:

sessionStorage.getItem(key)
  • 1

删除数据:

sessionStorage.removeItem(key)
  • 1

清空数据:(所有都清除掉)

sessionStorage.clear()
  • 1

11.3

1、声明周期永久生效,除非手动删除,否则关闭页面也会存在

2、可以多窗口(页面)共享(同一浏览器可以共享)

3、以键值对的形式存储使用

存储数据:

localStorage.setItem(key, value)
  • 1

获取数据:

localStorage.getItem(key)
  • 1

删除数据:

localStorage.removeItem(key)
  • 1

清空数据:(所有都清除掉)

localStorage.clear()
  • 1