跟着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 运算符优先级
优先级从上到下依次递减,最上面具有最高的优先级,逗号操作符具有最低的优先级。
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注意事项
函数都是有返回值的
- 如果有return则返回return后面的值
- 如果没有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
作用域链:采取就近原则的方式来查找变量最终的值。
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
用 对象 :
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)
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 添加删除数组元素的方法
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 数组索引方法
lastIndexOf()从后面开始查找
6.3.5 数组转换为字符串
arr.join(';')
arr.toString()
- 1
- 2
6.3.6 连接、截取、删除
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 根据字符返回位置
var str = '改革春风吹满地,中国人民真争气!'
console.log(str.indexOf('中'));
console.log(str.indexOf('中', 5));
//输出结果是一样的,因为他只是不查找了满字之前(不包括满字)的字符
- 1
- 2
- 3
- 4
6.4.4 根据位置返回字符(重点)
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
值类型变量的数据直接存放在变量(栈空间)中
7.4 复杂类型的内存分配
引用类型变量(栈空间)里存放的是地址,真正的对象实例存放在堆空间
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组成
1.2 API 和 Web API 总结
- API 是为我们程序员提供的一个接口,帮助我们实现某种功能,我们会使用就可以了,不必纠结内部如何实现
- Web API 主要是针对于浏览器提供的接口,主要针对于浏览器做交互效果。
- Web API 一般都有输入和输出(函数的传参和返回值),Web API 很多都是方法(函数)
- 学习 Web API 可以结合前面学习内置对象方法的思路学习
2.1 DOM简介
文档对象模型(Document Object Model,简称 DOM),是 W3C 组织推荐的处理可扩展标记语言(HTML或者XML)的标准编程接口。
W3C 已经定义了一系列的 DOM 接口,通过这些 DOM 接口可以改变网页的内容、结构和样式。
2.2 DOM树
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
注意:
-
因为得到的是一个对象的集合,所以我们想要操作里面的元素就需要遍历。
-
得到元素对象是动态的
-
只获取到一个元素时,返回的依旧时伪数组,只是数组对象只有一个
-
若没有获取到元素,返回的是空的伪数组
案例代码
<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 排他思想
如果有同一组元素,我们想要某一个元素实现某种样式, 需要用到循环的排他思想算法:
- 所有元素全部清除样式(干掉其他人)
- 给当前元素设置样式 (留下我自己)
- 注意顺序不能颠倒,首先干掉其他人,再设置自己
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 (文本节点包含文字、空格、换行等)
我们在实际开发中,节点操作主要操作的是元素节点
节点层级
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以上才支持
解决方案:
- 如果想要第一个子元素节点,可以使用 [0]
- 如果想要最后一个子元素节点,可以使用 [ - 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
() 方法返回调用该方法的节点的一个副本,且是动态的。
注意:
- 如果括号参数为空或者为 false ,则是浅拷贝,即只克隆复制节点本身,不克隆里面的子节点。
- 如果括号参数为 true ,则是深度拷贝,会复制节点本身以及里面所有的子节点。
2.6.7 创建元素总结
()
innerHTML
()
区别
-
是直接将内容写入页面的内容流,会导致页面全部重绘
-
innerHTML 是将内容写入某个 DOM 节点,不会导致页面全部重绘
-
innerHTML 复制节点的时候,不会复制原先节点的事件,会存在内存泄露问题
-
如果页面创建元素很多,建议使用 innerHTML 因其效率更高(不要拼接字符串,采取数组形式拼接)
-
如果页面创建元素较少,建议使用 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 删除事件方式
// 传统注册事件
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
事件冒泡: IE 最早提出,事件开始时由最具体的元素接收,然后逐级向上传播到到 DOM 最顶层节点的过程。
事件捕获: 网景最早提出,由 DOM 最顶层节点开始,然后逐级向下传播到到最具体的元素接收的过程。
注意
- JS 代码中只能执行捕获或者冒泡其中的一个阶段。
- onclick 和 attachEvent 只能得到冒泡阶段。
- addEventListener(type, listener[, useCapture])第三个参数如果是 true,表示在事件捕
获阶段调用事件处理程序;如果是 false(不写默认就是false),表示在事件冒泡阶段调用事件处理
程序。- 实际开发中我们很少使用事件捕获,我们更关注事件冒泡。
- 有些事件是没有冒泡的,比如 onblur、onfocus、onmouseenter、onmouseleave
- 事件冒泡有时候会带来麻烦,有时候又会帮助很巧妙的做某些事件,我们后面讲解。
// 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
事件发生后,跟事件相关的一系列信息数据的集合都放到这个对象里面,这个对象就是事件对象。是自己生成的,不需要我们去写。
比如:
- 谁绑定了这个事件。
- 鼠标触发事件的话,会得到鼠标的相关信息,如鼠标位置。
- 键盘触发事件的话,会得到键盘的相关信息,如按了哪个键。
3.4.2 事件对象的使用语法
事件触发发生时就会产生事件对象,并且系统会以实参的形式传给事件处理函数。
所以,在事件处理函数中声明1个形参用来接收事件对象。
3.4.3 事件对象兼容问题
事件对象本身的获取存在兼容问题:
-
标准浏览器中是浏览器给方法传递的参数,只需要定义形参 e 就可以获取到。
-
在 IE6~8 中,浏览器不会给方法传递参数,如果需要的话,需要到 中获取查找。
解决:
e = e || window.event;
//只要“||”前面为false, 不管“||”后面是true 还是 false,都返回 “||” 后面的值。
//只要“||”前面为true, 不管“||”后面是true 还是 false,都返回 “||” 前面的值。
- 1
- 2
- 3
3.4.4 事件对象的常见属性和方法
// 返回触发事件的对象(在这里是鼠标点击的对象)
// 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
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 常用鼠标事件
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。
// 鼠标事件对象 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 键盘事件
注意: 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
案例:京东快递单号查询
<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 浏览器标准的一部分。
6.2 BOM的构成
BOM比DOM更大,它包含DOM
6.3 *对象window
window 对象是浏览器的*对象,它具有双重角色。
- 它是 JS 访问浏览器窗口的一个接口。
- 它是一个全局对象。定义在全局作用域中的变量、函数都会变成 window 对象的属性和方法。
在调用的时候可以省略 window,前面学习的对话框都属于 window 对象方法,如 alert()、prompt() 等。
注意:window下的有一个特殊属性
6.4 window对象的常见事件
6.4.1 窗口加载事件
①onload
= function(){}
或者
("load",function(){});
- 1
- 2
- 3
是窗口 (页面)加载事件,当文档内容完全加载完成会触发该事件(包括图像、脚本文件、CSS文件等), 就调用的处理函数。
注意:
- 有了 就可以把 JS 代码写到页面元素的上方,因为 onload 是等页面内容全部加载完毕,再去执行处理函数。
- 传统注册事件方式 只能写一次,如果有多个,会以最后一个 为准。
- 如果使用 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
是调整窗口大小加载事件, 当触发时就调用的处理函数。
注意:
-
只要窗口大小发生像素变化,就会触发这个事件。
-
我们经常利用这个事件完成响应式布局。
当前屏幕的宽度
//宽度超过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() 方法用于设置一个定时器,该定时器在定时器到期后执行调用函数。
注意:
- window 可以省略。
- 这个调用函数可以直接写函数,或者写函数名或者采取**字符串‘函数名()’**三种形式。第三种不推荐
- 延迟的毫秒数省略默认是 0,如果写,必须是毫秒。
- 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符。
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() 建立的定时器。
注意:
- window 可以省略。
- 里面的参数就是定时器的标识符 。
6.5.3 setInterval() 闹钟定时器
(回调函数, [间隔的毫秒数]);
- 1
setInterval() 方法重复调用一个函数,每隔这个时间,就去调用一次回调函数。
注意:
- window 可以省略。
- 这个调用函数可以直接写函数,或者写函数名或者采取字符串 ‘函数名()’ 三种形式。
- 间隔的毫秒数省略默认是 0,如果写,必须是毫秒,表示每隔多少毫秒就自动调用这个函数。
- 因为定时器可能有很多,所以我们经常给定时器赋值一个标识符。
- 第一次执行也是间隔毫秒数之后执行,之后每隔毫秒数就执行一次。
案例:倒计时
<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指向
-
全局作用域或者普通函数中this指向全局对象window(注意定时器里面的this指向window)
-
方法调用中谁调用this指向谁
-
构造函数中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分钟,去切菜,炒菜。
他们的本质区别: 这条流水线上各个流程的执行顺序不同。
6.7.3 JS执行机制(事件循环)
-
先执行执行栈中的同步任务。
-
异步任务(回调函数)放入任务队列中。
-
一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任
务结束等待状态,进入执行栈,开始执行。
由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环( 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
6.8.3 location 对象的属性
重点记住: 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 对象的方法
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。
history对象一般在实际开发中比较少用,但是会在一些 OA 办公系统中见到。
7 PC端网页特效
7.1 元素偏移量offset系列
7.1.1 offset概述
offset 翻译过来就是偏移量, 我们使用 offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等。
- 获得元素距离带有定位父元素的位置
- 获得元素自身的大小(宽度高度)
- 注意:返回的数值都不带单位
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系列的相关属性可以动态的得到该元素的边框大小、元素大小等。
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 系列的相关属性可以动态的得到该元素的大小、滚动距离等。
scrollWidth不包含边框,包含padding和内容区
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 页面被卷去的头部兼容性解决方案
需要注意的是,页面被卷去的头部,有兼容性问题,因此被卷去的头部通常有如下几种写法:
- 声明了 DTD,使用
- 未声明 DTD,使用
- 新方法 和 ,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/)]
他们主要用法:
系列 经常用于获得元素位置 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个移动距离
- 利用定时器不断重复这个操作
- 加一个结束定时器的条件
- 注意此元素需要添加定位,才能使用
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 缓动效果原理
缓动动画就是让元素运动速度有所变化,最常见的是让速度慢慢停下来
思路:
- 让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。
- 核心算法: (目标值 - 现在的位置 ) / 10 做为每次移动的距离 步长
- 停止的条件是: 让当前盒子位置等于目标位置就停止定时器
- 注意步长值需要取整
//仍有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
返回顶部
- 带有动画的返回顶部
- 此时可以继续使用我们封装的动画函数
- 只需要把所有的left 相关的值改为 跟 页面垂直滚动距离相关就可以了
- 页面滚动了多少,可以通过
得到
- 最后是页面滚动,使用
(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
筋斗云案例
- 利用动画函数做动画效果
- 原先筋斗云的起始位置是0
- 鼠标经过某个小li,把当前小li的offsetLeft 位置做为目标值即可
- 鼠标离开某个小li,就把目标值设为 0
- 如果点击了某个小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 移动端拖动元素
-
touchstart
、touchmove
、touchend
可以实现拖动元素 -
但是拖动元素需要当前手指的坐标值 我们可以使用
targetTouches[0]
里面的pageX
和pageY
-
移动端拖动的原理: 手指移动中,计算出手指移动的距离。然后用盒子原来的位置 + 手指移动的距离
-
手指移动的距离: 手指滑动中的位置 减去 手指刚开始触摸的位置
拖动元素三步曲:
(1) 触摸元素
touchstart
: 获取手指初始坐标,同时获得盒子原来的位置
(2) 移动手指touchmove
: 计算手指的滑动距离,并且移动盒子
(3) 离开手指touchend
:
注意: 手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动 ();
8.2 移动端常见特效
8.2.1 案例: 移动轮播图
移动端轮播图功能和基本PC端一致。
- 可以自动播放图片
- 手指可以拖动播放轮播图
8.2.2 案例分析:
- 自动播放功能
- 开启定时器
- 移动端移动,可以使用translate 移动
- 想要图片优雅的移动,请添加过渡效果
-
自动播放功能-无缝滚动
-
注意,我们判断条件是要等到图片滚动完毕再去判断,就是过渡完成后判断
-
此时需要添加检测过渡完成事件 transitionend
-
判断条件:如果索引号等于 3 说明走到最后一张图片,此时 索引号要复原为 0
-
此时图片,去掉过渡效果,然后移动
-
如果索引号小于0, 说明是倒着走, 索引号等于2
-
此时图片,去掉过渡效果,然后移动
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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) 页面。
解决方案:
- 禁用缩放。 浏览器禁用默认的双击缩放行为并且去掉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 延迟。
9 移动端常用开发插件
9.1 什么是插件
动端要求的是快速开发,所以我们经常会借助于一些插件来帮我完成操作,那么什么是插件呢?
JS 插件是 js 文件,它遵循一定规范编写,方便程序展示效果,拥有特定功能且方便调用。如轮播图和瀑布流插件。
特点:它一般是为了解决某个问题而专门存在,其功能单一,并且比较小。
我们以前写的 也算一个最简单的插件
fastclick 插件解决 300ms 延迟。 使用延时
GitHub官网地址: /ftlabs/fastclick
9.2 插件的使用
- 引入 js 插件文件。
- 按照规定语法使用。
if ('addEventListener' in document) {
document.addEventListener('DOMContentLoaded', function() {
FastClick.attach(document.body);
}, false);
}
- 1
- 2
- 3
- 4
- 5
9.3 Swiper 插件的使用
中文官网地址: /
- 引入插件相关文件。
- 按照规定语法使用
9.4 其他移动端常见插件
superslide: http:///
iscroll: /cubiq/iscroll
9.5 移动端视频插件
H5 给我们提供了 video 标签,但是浏览器的支持情况不同。
不同的视频格式文件,我们可以通过source解决。
但是外观样式,还有暂停,播放,全屏等功能我们只能自己写代码解决。
这个时候我们可以使用插件方式来制作。
我们可以通过 JS 修改元素的大小、颜色、位置等样式。
9.6 插件的使用总结
- 确认插件实现的功能
- 去官网查看使用说明
- 下载插件
- 打开demo实例文件,查看需要引入的相关文件,并且引入
- 复制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