目录
[TOC]
一、JS 基础知识
1. JS 的组成
- ECMASCRIPT:基础语法
- DOM(文档对象模型):使用 DOM 可以对页面上的元素进行操作
- BOM(浏览器对象模型): 独立于内容,我们可以对浏览器窗口进行操作;
2. js 的位置
- 行内式:
<input type="text" value="点击我试试" onclick="alert('我是秋香')" >
- 内嵌式:
<script>alert("测试")</script>
- 引用式:
<script src="lauch.js"></script>
3. js 输出语句
- promt("") : 提醒用户在窗口输入信息
- console.log : 输出文字
- alert("") : 提醒文字
4. 变量数据类型
数据类型:
- 简单数据类型:Number、String、、Boolean、Undefined、Null
- 复杂数据类型(引用数据类型):object、其他引用对象
5. 堆和栈
- 堆内存: 复杂数据类型放在堆内存,由程序员控制 释放、垃圾回收机制 ;
- 栈内存:存放简单数据类型,如参数值、局部变量等;
值类型:
- 包括 基础数据类型 和 简单数据类型,存储变量时是存储值的本身
- String 、Number 、Boolean 、Undefined 、Null
引用类型:
- 又称为:复杂数据类型,在存储时存储的仅仅是地址,所以也叫引用数据类型 ;
- 通过关键字(系统 this、或者对象)引用,Object、Array、Date…
6. 变量数据类型
1). Number
Number.Max_VALUE()
: 最大值Number.MIN_VALUE()
: 最小值
2). String
String = 任何数据类型 + String
- 任何数据类型和 String 相连,都会得到字符串 ;
3). Boolen
- true:1 、 false:0
4). 检测数据类型
语句:
alert(typeof age)
:得到 String
7. 3 种转字符串方法
- 数字转字符串: toString() : Number.toString
- 强制类型转换: String (Number) :
- 隐式转换: num + String
8. 转数字类型方法
常用方法:
parseInt(" 11 ")
取整parseInt("22.32 ")
取浮点
其他方法:
- 强制类型转换:
Number(" 122 ")
- 算数隐式转换:
('11' - 0 )
9. 标识符关键字保留字
- 标识符:变量、属性、函数、参数名
- 关键字: js 已经存在使用的词,不能再充当变量名和方法名
- 保留字: js 后续可能会启用的词,比如:
let
10. ++运算符
c++ : 先运算再加 1 ;
++c : 先加再运算 ;
var c =1 ;
<!-- 先运算在加1 -->
c++ ;
<!-- 先加一再与运算 -->
++c ;
11. 逻辑中断机制
123 || false || 12 => 从左判断,如果条件不成立,立即中断结束
12. 代码执行结构
三种执行结构
1) . 顺序执行
- 顺序执行,默认从左到右执行,权重高的 运算符 优先执行 ;
2) . 分支结构
参数 chose , 必须和 case 完全相等才能执行, 注意数据类型也要一致!
var chose = 0;
switch (chose) {
case 0:
alert("这是0");
break;
case 1:
alert("这是0");
break;
case 2:
alert("这是0");
break;
default:
alert("没有合适的选项");
}
3) . 循环结构
常见是 for 循环,通过初始条件、结束条件和递增条件来循环执行语句块:
do… while
它和 while 循环的区别是,不是每次循环开始的时候判断是否成立,而是先循环再判断是否成立
- while(true):循环
- while (false) :终止循环
var n = 0;
do {
n = n + 1;
} while (n < 100);
13. 三元表达式
语法:当 a>10 成立,执行 1 语句,反之执行 2 语句
a > 10 ? res=1 ; res= 2
14. 标识符命名规范
① 变量、函数命名必须有意义
② 变量一般用名词、函数一般用动词
③ 遵循驼峰命名
二、字符串
字符串使用 ``
包含多行字符串
常用:双引号包裹
Unicode 字符
ASCII 字符可以以\x##形式的十六进制表示,例如:
'\x41'; // 完全等同于 'A'
1. 基本包装类型
为了方便操作,JavaScript 提供了三种特殊的引用类型:Stirng、Number、Boolean、
基本包装类型将【简单数据类型】包装为 【复杂数据类型】,使其具备属性和方法 ;
- 一般情况下,只有【复杂数据类型】才有 属性和 方法
- 在 Js 中, 简单数据类型竟然也有 length 属性
- 因为: 简单数据类型被包装成复杂数据类型:
基本包装类型:
- 把简单数据类型包装成 复杂数据类型
可见形式:
var str = "zhangjian";
内部原理:
//创建 简单数据类型对象
var temp = new String("zhangjian");
// 地址赋值
str = temp;
// 销毁中间对象
temp = null ;
2. 模板字符串(ES6)
- ES6 新增使用:
${ }
包裹变量,简单快速
console.log(`你好, ${name}, 你今年${age}岁了!`);
3. 字符串的不可变性
字符串类似 Array,可以使用下标取出对应的单个字符
字符串不可变性,不要轻易大规模拼接,执行没结束,之前的字符串在内存中不会释放,导致阻塞
var names = "小狗逼你好呀";
console.log(names[1]); // 狗
字符串不可变
即便是对字符串里具体索引下的字符进行赋值,也不会报错但也不会改变
var str = "你是小狗逼";
str[0] = "我";
console.log(str);
输出:你是小狗逼
结论:字符串是不可变的,即便针对索引赋值,不报错也不变 ,编辑字符串需要特定的方法 ;
4. toUpperCase()
toUpperCase()
把字符串全部变成大写,返回大写
- 返回大写,但是原始数据大小写不变化
var str = "ABC";
str.toLocaleLowerCase();
console.log(str);
5. toLowerCase()
toUpperCase()
把字符串全部变成小写,返回小写
- 返回小写,但是原始数据大小写不化
var str = "ABC";
console.log(str.toLowerCase());
console.log(str);
6.字符串的查找
1) 指定内容查找
返回内容对应的索引,索引号是首个字符的索引
- Str.indexOf( "字符串 ", 开始查找位置 )
- str.lastIndexOf( "字符串 ", 开始查找位置)
2) 指定索引查找
返回字符/字符串(返回首个查到的位置)
- str[ index ]
- str.subString(start ,end )
- str. charAt(index)
- str.charCodeAt(index)
3) 字符串其他方法
案例应用:根据字符串查找 出现次数最多的字符,次数
思路:
- 创建个新的字符数组对象,键值对: 字符 次数
- 遍历键值对对象,记录 值最大的键值对
- 输出最多次的键值对
var str = "sdasdfdasdfsadfsggggggggg";
var str1 = {};
//1.遍历统计 出现次数
for (const key in str) {
let chars = str.charAt(key);
if (str1[chars]) {
//已经存在
str1[chars]++;
} else {
//尚未存在,直接添加键值对
str1[chars] = 1;
}
}
//2. 遍历统计 值最大的
let max = 0;
let keyIndex = 0;
for (const key in str1) {
if (str1[key] > max) {
max = str1[key];
keyIndex = key;
}
}
console.log(str1);
console.log("出现最多次:" + keyIndex + "出现次数:" + str1[keyIndex]);
7. 字符串转数组
str.split(' , ')
:将字符串切割为数组返回
8. 字符串替换
Str.replce( 'a' ,'b' ) ;
将字符串出现的第一个 a 替换成 b ,如果需要全替换使用正则 或者 循环 或者 ES6 方法 ;
注意: 遵循字符串不可更改的原则,repleace 返回新的字符串 ,不是更改原有的字符串
var str = "aaaaaosssssssssoffffffffoccccco";
//将字符串内的 o 替换成 _
while (str.indexOf("o") !== -1) {
//替换 前段后覆盖
var str = str.replace("o", "___");
}
console.log(str);
// aaaaa**sssssssssffffffffccccc**
三、数组
1. 数组属性
js 的数组 Array
可以包含任意的数据类型,并且通过 索引访问元素
取得 Array
的长度,通过 length
属性
var arr=[ 1,2,3,"str",null,true ] ;
2. 创建数组两种方式
①. 数组直接赋值
var arr= [1,2,"str"] ;
②. new Array
var arr = new Array(1,2,3,..);
注意:当new Array(2)
只写入一个参数,此参数默认为新数组长度,不是元素
3. 检测传入数据类型
当检测 Array 实例时, Array.isArray 优于 instanceof,因为 Array.isArray 能检测 iframes
方式一: instanceof
语句:arr instanceof Array
// true false
console.log(arr instanceof Array);
方式二: Array.isArray(arr)____内置对象方法
语句: Array.isArray(arr)
// true flase
console.log("是否为数组:" + Array.isArray(arr));
传入参数前,保证安全性,需要对传入类型进行检测!
var arr = [];
if (arr instanceof Array) {
console.log("传入是数组");
} else {
console.log("传入不是数组");
}
4. 增删数据
6. 查找数组
array.indexOf()
7. 数组 倒置 和 排序
数组倒置
arr.reverse()
数组排序
默认无参的数组排序,只能排一位数字元素
- a - b:正序排列
- b - a: 逆序排列
arr.sort(function (a, b) { return a - b; });
8. 数组转字符串
方法 | 简述 | 使用 |
---|---|---|
arr.toString() | 不修改原数组,返回一个新的字符串(join 参与操作加, ) | arr.toString() |
arr.join() | 不修改原数组,返回一个新的字符串 | arr.join(’,’) |
9. 数组拼接、删除、截取
方法 | 简述 | 返回 |
---|---|---|
arr.concat(arr1 ,…) | 不修改原数组,返回一个新的拼接数组 | 返回新的数组 |
arr.slice(starIndex ,endIndex ) | 截取开始 index~结束 index | 不修改原数组,返回一个新的字符串 |
arr.splice(starIndex ,del_num ,InsertData ) | arr.splice(1, 2, “x”) 删除开始位置,删除个数,插入元素 | 修改原数组 |
10. 练习:数组去重
思路: 1.传入数组参数 2.循环 数组,取出每个数组元素 3.创建新的数组挨个接收元素 3.取出的数组元素,在新的数组查找,返回-1,表明当前不存在,可以存入 4.打印新的数组
function res(arr) {
let newarr = [];
for (var x = 0; x < arr.length; x++) {
//取出对应的元素
if (newarr.indexOf(arr[x]) == -1) {
//如果不存在,存入
newarr.push(arr[x]);
}
}
console.log(newarr);
}
四、函数
1.函数意义
- 提高代码复用性,将一个或者多个功能的代码模块封装起来,提供简便可重复使用的 API 接口
2.实参形参传递
- 实参: 传递真实得数据给被调用函数
- 形参: 将实参的地址传递,形参指针指向实体参数,形参的改变不会影响实参
3.实参形参不匹配
- 当实参个数少于形参个数,其他的形参就变成
Undefined
- 当实参多于形参,其他多于参数不会被接收 ;
参数个数 | 说明 |
---|---|
实参个数多于形参 | 只取到形参个数 |
形参多于实参 | 接到实参值的正常,没接到实参的 undefined,结果为 NaN |
4.argument 获取实参
当实参个数超过形参,使用关键字argument
无需声明,接收实参,
...argument
函数自带伪数组,支持length
方法,数组的形式取出
function methods(...arguments) {
var n = 0;
for (const key in arguments) {
console.log(arguments[n]);
n++;
}
}
5. break、continue、return 区别
- break: 结束当前循环(for、while)
- continue : 跳出本次循环,执行下一次循环(for 、 while)
- return : 不仅可以结束循环,还可以返回 值,结束当前 函数体内代码执行 ;
6. 函数声明的方式
- 关键字定义:
function add(){ ......... }
- 匿名方式:
var fun= function(){.......}
7. 作用域
- javascript 作用域:就是代码名字在某个范围起作用和效果,目的是提高程序的可靠性减少名称冲突 ;
- js 作用域(ES6)之前:全局作用域、局部作用域 ;
全局作用域
- 整个 javascript 标签,或者整个 js 文件
局部作用域
- 就是仅在函数内部起作用的代码 ;
全局变量
- 在全局作用域下的变量,在全局下可以使用
注意:在函数内部如果没有声明,直接赋值的变量,也是全局变量
局部变量
- 在函数内部声明的变量
- 只能在函数内部被使用
全局和局部的区别
- 全局变量比较占内存,只有浏览器窗口关闭时才释放;
- 布局变量函数执行完毕就立即销毁 ;
作用域链
内部函数 访问外部函数的变量,采用的是链式查找的原则
- 当存在内部函数 变量名和外部相同,遵循 就近原则逐级向上查找 ;
- 以为最近的变量为准 ;
// 作用域链
var num=1;
//外部函数
function method1(){
var num=2; // ↑ 向上查找 num
//内部函数
function methods2(){
var num=3; // ↑ 向上查找 num
console.log(num); // num ?
}
}
结果:num =3
8. 案例:闰年二月
- 可以被 4 整除且不能被 100 整除的年份,是闰年
- 闰年二月 29 天,平年 28 天
days(prompt(`输入当前年份:`));
// 判断2月多少天
function days(year) {
var runyear = RunYear(year);
if (runyear == true) {
console.log(`${year}:闰年29天`);
} else {
console.log(`${year}:平年28天`);
}
}
// 判断闰年
function RunYear(year) {
//闰年不是整百年 且 可以被4整除
if (year % 100 !== 0) {
var res = year % 4 == 0 ? true : false;
return res;
} else {
return false;
}
}
五、预解析
JavaScript 代码是浏览器,是浏览器中的 JavaScript 解析器来执行的。
1.JavaScript 解析器
运行代码分为两步: 预解析代码 和 执行代码
(1) 预解析 : 先将代码中,var
和 function
提升到作用域的最前面 ;
(2) 代码执行 :按照代码的书写顺序依次执行 ;
2.预解析分为
- 变量提升:就是把所有的变量声明提升到作用域最前面----------- 但是不赋值
- 函数提升: 就是把所有的函数声明提升到作用域最前面---------- 但是不调用
3.注意
- 尽量将函数的调用放在函数声明的后面,防止报错
function methods(){
return true;
}
methods();
六、对象
- 万物皆对象,在 JavaScript 中,对象是一组无序的【相关属性】和【方法】的集合,所有的事物都是对象,例如字符串、数组、函数等.
1.对象的组成
- 属性: 是事物的特征,在对象中属性来表示【特征】(常用名词)
- 方法: 事物的行为,在对象中用方法来表示【行为】(常用动词)
2.对象创建方式
JavaScript 中现阶段我们使用三种方式创建对象(Object)
- 使用字面量创建对象
- 利用 newObject 创建对象
- 利用构造函数创建对象
3.变量、属性、函数、方法、总结
- 变量: 单独声明、单独存在
- 属性: 对象里面的变量称为属性
- 函数: 单独存在,使用函数名就可以使用
- 方法: 存在对象里,使用对象调用
4.字面量创建对象{}
var obj = {
user_name: "zhangjian",
age: 12,
address: "Anhui Hefei",
paly: function () {
console.log("运动");
},
};
调用方式:对象名加属性名的方式
- 方式 1 :
obj.age
/obj.paly();
- 方式 2 :
obj['age']
使用 =
,在对象体外单独设置属性和方法:
var obj = new Object();
obj.name = "Nalitor";
obj.age = 12;
obj.skill = function () {
console.log("螺旋丸");
};
obj.skill();
console.log(obj.name);
5.构造函数创建对象
其他方法一次只能创建一个对象,为了提高代码复用性,我们可以使用构造封装的方法,
- 构造函数:就是将对象里一些相同的属性和方法抽象出来封装到函数里
var zhangjian={
uname:'zhangjian',
age:12,
skill:function(){
console.log("跳高");
}
}
创建构造函数
- 构造函数-首字母大写
- 不需要
return
// 创建构造函数
function Star(uname, age, skill) {
this.uname = uname;
this.age = age;
this.skill = skill;
}
// 传入参数
var ldh = new Star("刘德华", 18, function () {
console.log("我会唱歌");
});
console.log(ldh.uname);
ldh.skill();
6. 构造函数和对象
- 构造函数:抽象了一类事物,封装到函数里,泛指一类事物 ;
- 对象:构造函数的实例化,填充了具体的看属性和方法,也称为对象实例化 ;
7. new 关键字
new 在执行时会做四件事:
- 在内存中创建新的空对象
- 让
this
指向这个方法 - 执行构造函数里的方法,给新对象里添加属性和方法
- 返回这个新对象(所以构造函数里不需要新的对象)
8. for…in 遍历对象
一般用于遍历对象,方法没有index
// 创建构造函数
function Star(uname, age, skill) {
this.uname = uname;
this.age = age;
this.skill = skill;
}
// 传入参数
var ldh = new Star("刘德华", 18, function () {
console.log("我会唱歌");
});
for (var key in ldh) {
console.log(ldh[key]);
}
结果:
uname
age
skill
七、内置对象
JavaScript 对象分为三种:
- 内置对象
- 自定义对象
- 浏览器对象
1.内置对象(工具类)
JS 语言自带的对象,这些对象提供给开发者使用,并且提供的都是常有且必要的功能(属性和方法)
常见内置对象:
- Math
- Date
- Array
- String
....
2.Math
常用的数学函数:
-
绝对值
console.log(Math.abs(-123));
-
三个取整方法
- Math.ceil() 向上取整
- Math.floor() 向下取整
- Math.round() 四舍五入
得到两个数之间的随机整数:
- 算法参考MDN
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min; //不含最大值,含最小值
}
- 猜数字游戏(演示随机数)
// 猜数字游戏
// 1.产生随机数
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min; //不含最大值,含最小值
}
var rand = getRandomInt(0, 10);
// 循环让用户输入,注意:不要死循环,输入正确需要终止循环
while (true) {
var guessNum = prompt("猜一猜数字");
if (guessNum > rand) {
alert("猜大啦~");
} else if (guessNum < rand) {
alert("猜小啦~");
} else {
alert("真聪明~猜对了");
break;
}
}
// 封装自己Math对象
var myMath = function () {
PI = 3.141592634324;
Max = function () {
var max = arguments[0];
for (let i = 0; x < arguments.length; i++) {
if (max < arguments[i]) {
max = arguments[i];
}
}
return max;
};
Min = function () {
var min = arguments[0];
for (let i = 0; x < arguments.length; i++) {
if (min > arguments[i]) {
min = arguments[i];
}
}
return min;
};
};
console.log(new myMath().Max(1, 12, 23, 4323, 423, 2323));
3.Date 日期对象
是一个构造函数,必须使用 new
来调用日历对象,必须先实例化
1. 无参用法
var date = new Date( ) ;
结果:2021-03-17T18:32:18.480Z
2. 有参用法
如果带参,括号里传入时间作为参数,就返回传入时间
格式:
- 字符串方式:
'2021-3-18'
new Date('2021-3-18')
(1)日期格式化
我们要设置2021-3-18 2:2:22
日期格式如何设置?
注意:day 周几、月份 都是从 0 开始计算
- 周 0 是周日
- 传回的月份比实际月份小 1,
Date.getMonth + 1
var date = new Date();
var year = date.getFullYear();
var month = date.getMonth();
var dates = date.getDate();
var day = date.getDay();
var hour = date.getHours();
var minut = date.getMinutes();
var second = date.getSeconds();
//汉化处理周
var arrMonth = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
// 格式化日期
var time = ` ${year}年${month + 1}月${dates}日 ${ arrMonth[day]} ${hour}时${minut}分${second}秒 `;
console.log(time);
输出: 2021 年 3 月 18 日 周四 3 时 26 分 18 秒
(2) 时间戳毫秒数
计算机起始日期 1970 年 1 月 1 日 ,是毫秒开始计数的起始时间
获得 Date 总毫秒数,累计毫秒有一下三种方式:
var date = new Date();
// 方法1:
console.log(date.valueOf());
console.log(date.getTime());
// 方法2:最常用
var date1 = +new Date();
console.log(date1);
//方法3:H5新增方法
console.log(Date.now());
输出:
1616009916725
1616009916725
1616009916729
1616009916730
实现倒计时效果:天、时、分、秒
思路:
- 剩余时间 = 将来的时间戳 - 现在的时间戳
- 将得到的时间秒数,转化为 天、时、分、秒 即可
d = parseInt(总秒 / 60 / 60 / 24 ) ; 计算天
h = parseInt(总秒 / 60 / 60 % 24 ) ; 计算小时
m = parseInt(总秒 / 60 % 60 ) ; 计算分钟
s = parseInt(总秒 % 60 ) ; 计算秒
代码:
function countDown(time) {
var now = +new Date(); //获得当前时间戳
var set_time = new Date(time); //用户设置时间总毫秒数
var times = (set_time - now) / 1000; //得剩余毫秒数
console.log(times);
d = parseInt(times / 60 / 60 / 24); //天
h = parseInt((times / 60 / 60) % 24); //时
m = parseInt((times / 60) % 60); //分
s = parseInt(times % 60); //秒
return d + "天" + h + "时" + m + "分" + s + "秒";
}
//传入设定时间
console.log(countDown("2021-3-18 10:57:00"));
输出:0 天 6 时 57 分 7 秒
八、WEB API
关注点:
- WEB API 与 JavaScript 语法阶段的关联性
- API :
- WEB API:
1.JS 的组成
2.JS 基础阶段和 WEB API 阶段
第一阶段:JS 基础阶段
- ECMAScript 是标准规定和基础语法
- 掌握基础语法,还需要 DOM/BOM 操作页面元素实现交互效果
第二阶段:WEB API 阶段
- web api 属于 W3C 组织标准
- WEB API 主要由于:BOM / DOM 组成
- WEB API 是 JS 独有的部分
- 主要为了实现页面交互的效果
3.WEB API
WEB API 是浏览器提供的针对操作 :页面元素
和 浏览器功能
的 API(DOM 和 BOM)
用于实现浏览器和文档的交互效果
4.DOM
文档对象模型(Document Object Model),W3C 组织推荐处理可扩展标记语言的标准编程接口
- 通过操作 DOM 我们可以改变页面元素的,内容、结构、样式
- 文档 : 一个页面就是一个文档 doument ,DOM 中使用 doument 表示
- 元素 : 页面中所有的标签都是元素 , DOM 中使用 element 表示
- 节点 : 页面文档中,所有的内容都是节点(标签、属性、文本、注释),DOM 中使用 node 表示
5.获取 DOM 对象方式
DOM 实际开发中主要是用来操作元素,如何获取页面元素,有以下方式:
- ID 获取
- 标签名获取
- HTML5 新增方法获取
- 特殊元素获取
注意
不管是哪种获取方式,除了 ID 目标明确的,其他的泛获取方式,返回的都是集合对象都是Collection[]
伪数组方式
(1) ID 获取方式
注意:
- 由于文档页面从上往下加载,
<script>
标签我们写在文档标签的下面 - get 方式的命名是驼峰命名法
- 参数 id 大小写敏感
- 返回的是元素的对象
object
console.dir(box);
可以查看到对象完整的属性和方法 ;
var box = document.getElementById("box");
console.log(box);
console.log(typeof box);
console.dir(box);
(2) Tag 获取方式
- 使用 tagName 方式获取到的是一个集合,以伪数组的形式返回
- 使用方式:
object[n]
var lis = document.getElementsByTagName("li");
console.log(lis);
HTMLCollection(10) [li, li, li, li, li, li, li, li, li, li]
如何获取父元素下的子元素对象:
如果需要精确到范围下的标签,就需要使用父元素对象做限定
方式:
父元素Obj.getElementsByTagName("标签名")
常用链式写法:
var li = document.getElementById("father").getElementsByTagName("li");
注意:父元素必须是单个明确对象,父元素如果也使用标签获取,注意把父元素从集合中取出来 ;
(3) H5 新增获取方法
① 类名获取
使用类名 class 方式获取对象,但是注意兼容问题,IE9 以上才兼容
- 返回的对象存储在 伪数组 集合中
Collection[n]
var h5Obj = document.getElementsByClassName("box");
console.log(h5Obj);
输出:
HTMLCollection [div#box.box, box: div#box.box]
0: div#box.box
length: 1
box: div#box.box
__proto__: HTMLCollection
① querySelector
- 获得指定标签中的第一个对象
- 可以使用:标签名、id、类名 来获取元素对象
- 切记里面的选择器需要加符号
var queryTag = document.querySelector("div");
var queryClass = document.querySelector(".box");
var queryId = document.querySelector("#box");
console.log(queryId);
① querySelectorAll
- 获得指定选择器的集合,伪数组形式返回
var queryALL = document.querySelectorAll(" .xiaoli ");
console.dir(queryALL);
返回:
NodeList(10)
(4) html 和 body 对象
body 获取对象
var bodyEle = document.body;
console.dir(bodyEle);
html 获取对象
var htmlEle = document.documentElement;
console.dir(htmlEle);
6. 事件三要素
简单理解: 触发 ----→ 响应机制
三要素:
- 事件源:被触发对象
- 事件类型:触发的方式
- 处理程序:触发后处理的程序
步骤:
- 第一步:获取事件源(DOM 对象)
- 第二步:绑定事件(注册事件)
- 第三步:添加事件处理程序
例:
document.querySelector("#box").onclick = function () {
alert("你点击我了");
};
- 事件源:box 盒子对象
- 事件类型:点击 click
- 处理程序:文字弹窗提醒
7. 常见鼠标事件
8. 操作元素
(1) 改变元素内容
两种写入方式:
element.innerText
: 从起始位置到终止位置的内容,它去除 html 标签,同时 空格 和 换行也会去掉 ;element.innerHtml
: 从起始到终止内容,包括 html 标签,同时保留 空格 和 换行 ;
只有 innerHtml 识别代码:
btn.onclick = function () {
box.innerHTML = "<p>添加个 <strong>P标签</strong> </p>";
};
两种方式是可以读写的:
- innerHTML 完整输出内部代码和文字,且保留空格空行
- innerText 只识别文字 , 空行空格删除
console.log(btn.innerHTML);
console.log(btn.innerText);
(2) 修改元素属性
- innerText、innerHTML 修改元素内容
- src 、 href
- id 、alt、title
…
(3) 表单属性操作
利用 DOM 可以操作,以下元素的表单属性
- type
- value : 表单特殊,内置文字使用该属性,不能使用
innerText
- checked
- selected
- disabled
- className
9. 样式属性操作
通过 JS 修改元素的 大小、颜色、位置等
-
方式一:
样式较少时候使用
element.style
行内样式操作 -
方式二:
样式较多,且复杂时候,使用
element.className
添加类名加载新样式
注意:
- 如果需要修改的样式较多复杂,使用类名的方式加载样式 ;
- class 是保留字,所有使用 className ;
- className 加载类名会覆盖原先类名,需要注意
10. 操作 js 精灵图
//1.获取 对象集合
var bgImgs = document.querySelectorAll("li");
let y = 0;
//2. 按照预定规律,计算 改变 position 位置,
for (const key in bgImgs) {
bgImgs[key].style.backgroundPosition = `10px ${y}px`;
y -= 157;
}
11. 排他思想实例
例如常见的按钮组,点击某个按钮更改其样式,其他按钮状态需要提前归零
需要用到循环的排他思想
步骤
- 1.循环绑定所有元素的事件
- 2.在循环体内,先清除其他所有同类的样式
- 3.再给当前选中的元素,添加样式
- 4.注意:顺序不能颠倒,先排除掉其他子元素样式,再给自己添加
(1) 百度换肤案例:
思路:
- 获取事件源: body 对象、3 个图片对象的集合
- 遍历图片集合,循环绑定事件
- CSS 创建不同背景属性的类名,留着备用
- 点击不同的图片后,给 body 加载不同的
className
(2) 表格全选效果:
要求:
-
JS 鼠标悬浮每行 hr 变色,
onmouseover
和onmouseout
-
全选和全取消效果,子按钮的选择状态跟着全选按钮状态,
checked=true/false
-
当子按钮挨个全部选中,全选按钮被影响为选中状态 ;
第一部分思路:
- 获得全选框 和 子复选框的对象集合
- 如果全选框【点击选中】,就循环所有子选项,修改为选中状态
- 如果全选框【点击取消】,循环修改全部子项,为 false
var all = document.querySelector(".allBtn");
var tds = document.querySelectorAll(".che");
all.onclick = function () {
if (all.checked) {
//如果已经全选,就将其他元素回归未选
for (const key in tds) {
tds[key].checked = all.checked;
}
} else {
//如果全选按钮没有选择,就把其他元素全选
for (const key in tds) {
tds[key].checked = all.checked;
console.log(all.checked);
}
}
};
第二部分思路:
实现:子复选框都被选中时,全选框选中状态
- 设置 flag 控制全选框状态
- 每次点击就循环一次,确认子项中是否存在没选中的
- 如果循环中存在子项没选中的,
flag=false ;
- 循环过后,代码底部的 flag 的值,决定全选框的
checked
状态
for (const key in tds) {
tds[key].onclick = function () {
//1.flag 控制全选按钮是否选中
var flag = true;
//2.循环确认是否都选中了,如果不是:flag=false
for (const k in tds) {
if (tds[k].checked === false) {
flag = false;
break; //只要有一个没选中,剩下的不需要循环了
}
}
//3.如果循环结果子项全选中了,flag = true
all.checked = flag;
console.log(flag);
};
}
12. 自定义属性操作
getAttribute(‘属性’)
(1) 自定义属性获值
1.获取属性值:
element.属性
: 获取属性值(针对 内置属性)element.getAttribute(' 属性 ') ;
(针对 自定义属性)
2.区别:
element.属性
:获取的是元素本身自带属性element.getAttribute(' 属性 ') ;
: 主要获取自定义属性,程序员自定属性
(2) 自定义属性设值
设置属性
element.属性 = 值
(针对 内置属性)element.setAttribute('属性','值')
(针对 自定义属性)
(3) 移除自定义属性
主要针对自定义属性的移除
elemrnt.removeAttribute('属性')
13. table 栏切换
自定义属性操作
案例分析:
-
Tab 选项卡有两个模块,上半部分
tab_hd
,下半部分tab_bd
-
上面的模块选项卡,点击一个背景色就变成红色,其他的不变色(排他思想)修改类;
-
下面模块会跟着选项卡变化。所以下面模块就写到点击事件里面响应 ;
-
规律:下面的显示内容和上面的选项卡一一对应,相匹配
-
核心思路:给每个选项卡
li
添加自定义属性indexNum
,属性值从 0 开始编号 -
哪个选项卡被点击了,就取出它的编号(0~5),到 items 内容集合伪数组匹配对应的 item
-
定位到 内容后,修改其属性
dispay:block
,让它显示,注意别忘排他
HTML 部分:
<div class="box">
<div class="table-hd">
<ul>
<li class="current">商品介绍</li>
<li>规格包装</li>
<li>售后保障</li>
<li>商品评价</li>
<li>手机社区</li>
</ul>
</div>
<div class="table-bd ">
<div class="item" style="display: block">
商品介绍、商品介绍、商品介绍、商品介绍、商品介绍、商品介绍、商品介绍、商品介绍
</div>
<div class="item">
规格包装、规格包装、规格包装、规格包装、规格包装、规格包装
</div>
<div class="item">
售后保障、售后保障、售后保障、售后保障、售后保障、售后保障
</div>
<div class="item">商品评价、商品评价、商品评价、商品评价、商品评价</div>
<div class="item">手机社区、手机社区、手机社区、手机社区、手机社区</div>
</div>
</div>
JS 部分:
//1.获取所有 li的对象
var lis = document.querySelectorAll("li");
var items = document.querySelectorAll(".item");
//2.循环给所有 li 加点击事件
for (const key in lis) {
lis[key].onclick = function () {
// 【第一步】:选项卡li 的样式加载
for (const k in lis) { //(1)先给干掉其他元素:移除类名
lis[k].className = "";
}
// (2)再给选中元素:添加类名
this.setAttribute("class", "current");
// 【第二步】:
//(1) 循环给每个 li 加自定义属性编号
for (let i = 0; i < lis.length; i++) {
lis[i].setAttribute("index", i);
}
// (2)获取 当前被点击 li 对应自定义编号
var li_Index = this.getAttribute("index");
// (3) 使用li编号,匹配 内容对象集合中的 具体对象
// 修改属性前,先干掉其他元素属性(排他思想)
for (let i = 0; i < items.length; i++) {
items[i].style.display = "none";
}
items[li_Index].style.display = "block";
};
}
14. H5 自定义属性
自定义属性目的:
- 是为了暂存并且使用数据,有些数据可以保存到页面不用存到数据库 ;
(1) 自定属性规范
由于自定义属性必须使用 getAttribute("属性")
获取,不能使用内置方法直接获取
为了醒目区分避免歧义混淆,判断 元素是 【内置属性】 还是 【自定义属性】按照规范命名
命名方式
行内命名:
data-名称
data-index..
或者使用 JS 设置:
element.setAttribute('data-index',2)
(2) 获取 H5 自定义属性
-
兼容性获取
element.getAttribute('属性')
-
H5 新增
element.dataset.index
或者element.dataset['index']
兼容很差:IE11 才开始兼容
(3)dataset:
dom.dataset ,它是一个容纳所有 data-
开头的自定义属性的集合
类型:DOMStringMap
h5 新增的集合类型
<div class="box" data-list-name="10">哈哈哈</div>
var get = box.dataset.listName;
注意:如果自定义属性里有多个单词使用 - 连接,使用 dataset 取值方法的时候,使用驼峰命名法
16. 父子节点操作
节点操作的两种方式:
两种方式都会使用,但是节点操作会更简单些
(1) 节点概述
一般情况下,一个节点至少拥有
- nodeType(节点类型)
- nodeName(节点名称)
- nodeValue(节点值)
元素节点:nodeType=1
属性节点:nodeType=2
文本节点:nodeType=3 (文本节点包括,文字、空格、换行)
实际开发中使用最多的是元素节点
(2) 节点层级
父子兄 关系
1. 利用儿子获取父亲对象
var father = son.parentNode;
2. 利用父亲获取所有儿子对象(返回集合)
var father = son.childNodes;
注意:返回的节点集合,包括 文本、元素、属性 节点,需要过滤;
返回:
子元素代替方案:father.children
实际开发常用:
var lis = document.querySelector("ul").children;
此方法兼容性较好,是 childNodes
的过滤版本,(非标准方法)
(3) 获取子节点
子节点:
parentNode.firstElementChild
parentNode.lastElementChild
返回第一个/最后一个 子元素节点,找不到就返回 null
注意: 这两个方法有兼容问题 ;IE9 以上才支持
17. 节点操作下拉菜单
- 先获取父元素的对象,利用父对象
father.children
获取子元素对象集合 - 遍历子元素集合,添加鼠标经过事件
onmouseout
- 注意排他思想,添加状态前,先去除其他同级 元素状态
<ul class="nav">
<li class="items">
<a href="#">移动应用</a>
<ul>
<li>移动应用</li>
<li>网站接入</li>
<li>无限游戏</li>
</ul>
</li>
<li class="items">
<a href="#">移动应用</a>
<ul>
<li>移动应用</li>
<li>网站接入</li>
<li>无限游戏</li>
</ul>
</li>
</ul>
var items = document.querySelectorAll(".items");
// 获取了所有 li 的对象集合
for (const key in items) {
// 循环注册 集合中对象的 鼠标景事件
items[key].onmouseover = function () {
// 第一步: 先干掉其他 ul 的状态
for (let i = 0; i < items.length; i++) {
items[i].children[1].style.display = "none";
}
//第二步: 再给当前目标 下的 ul 添加显示
this.children[1].style.display = "block";
// 第三步: 添加 鼠标离开 ul 隐藏
this.onmouseout = function () {
this.children[1].style.display = "none";
};
};
}
18. 兄弟节点操作(非重点)
兄弟节点
node.nextSibling
获取下个兄弟节点node.perviousSibling
获取上个兄弟节点
问题:
- 返回:
#text
返回对象集合中包含 文本节点 和 元素节点, 无法直接使用
优点: 兼容问题: H5 提供,没有兼容问题 IE9 下也支持
常用替代
返回结果是 兄弟元素节点,没有其他类型 nodeType,优点很方便
- nextElementSibling
下一个兄弟:var bor2 = box.nextElementSibling;
上一个兄弟:var bor2 =box.previousElementSibling;
问题:
他妈的还是 IE9 以上才兼容!!!草拟吗微软
19. 创建节点
(1) 动态创建元素节点
document.createElement(‘tagName’)
使用 document.createElement() 方法创建由于 tagName 指定的 HTML 元素,这些元素原先不存在,这根据我们需求动态生成的,所以我们称为 动态创建元素节点
(2) 添加元素节点
添加到父元素内的尾部 ,类似于 CSS 的伪元素 返回值是新元素的节点
后面添加:返回值是新元素的节点
father.appendChild(child);
//动态创建节点
// ul 下创建新的 li 元素
var ul = document.querySelector("ul");
//创建节点
var li = document.createElement("li");
//添加节点
ul.appendChild(li);
前面添加:
// 新建元素节点
var newEle = document.createElement("span");
// 父元素前面添加
ul.insertBefore(newEle, ul.children[0]);
18. 删除节点
node.removeChild(child)
father.removeChild(father.children[0]);
删除父元素的第一个子元素,返回删除的节点
19. 复制节点
node.cloneNode('true/false')
node.cloneNode() 方法返回一个节点的副本,称为克隆节点
注意:
- 如果方法内的参数为 空 或者 fasle , 就是浅拷贝,只是克隆节点本身,不复制里的子节点
- 深拷贝需要携带 true 作为参数,会携带内子节点或者内容复制 ;
20. 创建元素 3 种方式
-
document.write() 很少使用
如果页面加载完毕,会导致页面重绘(清空之前的元素) -
innerHTML
-
document.createElement
21. 实现评论功能
要求:
- 实现添加评论、删除评论功能
- 灵活控制节点获取、删除、子找父、父找子
- 注意排他思想
<div class="mainbox">
<div class="comment">
<textarea
name=""
id=""
cols="10"
rows="10"
placeholder="输入评论..."
></textarea>
<button type="submit">
发表 <br />
评论
</button>
</div>
<ul class="showcomm"></ul>
</div>
// 1.获取父元素的对象
var mainbox = document.querySelector(".mainbox");
//2.获取父元素下 texarea 对象
var text = mainbox.children[0].children[0];
//3. 提交按钮的对象
var btn = mainbox.children[0].children[1];
//4. 获取评论框的对象
var comment = mainbox.children[1];
//5. 添加提交按钮的点击事件,点击按钮 获取 写入框的内容
btn.onclick = function () {
// 非空判断
if (text.value == "") {
alert("请输入内容!");
} else {
console.log(text.value);
//6. 将获取到的内容,添加到 下方评论框
var newcomm = document.createElement("li");
// 7. 将生成的新标签添加在,父元素在前面
comment.insertBefore(newcomm, comment.children[0]);
//添加删除 链接
var del = "<a herf='javascript:;'>删除</a>";
//8. 将从 输入框拿到的内容,添加在新标签内
comment.children[0].innerHTML = text.value + del;
//9. 清空输入框的内容
text.value = "";
//加载删除功能
dele();
}
};
// 点击按钮删除 评论
var dele = function () {
//1. 获取按钮的对象,按钮添加点击事件
var lis = comment.children;
//2. 给li 下面的 a 标签 ,循环添加点击事件
for (let i = 0; i < lis.length; i++) {
var delBtn = lis[i].children[0];
delBtn.onclick = function () {
//找到其父亲 li,提交给 评论框对象 执行删除
this.parentNode;
comment.removeChild(this.parentNode);
};
}
};
20. 表格动态数据
1.先 JS 模拟数据,代替数据库,使用数组装 n 个对象 2.所有数据放在 tbody 里面 3. 因为有很多行,所以我们循环创建多个行(多少人多少行)
var user = [
{
uName: "鸣人",
course: "螺旋丸",
score: 100,
},
{
uName: "佐助",
course: "千鸟",
score: 90,
},
{
uName: "长门",
course: "轮目",
score: 10,
},
{
uName: "卡卡西",
course: "时空忍术",
score: 20,
},
];
// tbody创建行
var tbody = document.querySelector("tbody");
for (let i = 0; i < user.length; i++) {
// (1)先创建行:几个人几个行,插入到 tbody
var tr = document.createElement("tr"); //返回的新节点的对象
tbody.appendChild(tr);
//(2) 在行内插入单元格 td
for (const key in user[i]) {
let td = document.createElement("td");
tr.appendChild(td);
// (3) 给 td 录入值
td.innerHTML = user[i][key];
}
// (4) 创建有删除 链接的单元格
var delHTML = "<a href='javascript:;'>删除</a>";
var deleTd = document.createElement("td");
deleTd.innerHTML = delHTML;
tr.appendChild(deleTd);
//(5) 点删除,就删除链接的父节点
deleTd.onclick = function () {
tbody.removeChild(this.parentNode);
};
}
<table>
<thead>
<tr>
<th width="100">姓名</th>
<th width="160">科目</th>
<th width="80">成绩</th>
</tr>
</thead>
<tbody>
<!-- <tr>
<td>魏璎珞</td>
<td>java</td>
<td>100</td>
<td><a href="#">删除</a></td>
</tr> -->
</tbody>
</table>
九、DOM 操作(总结)
关于 dom 操作主要针对元素的操作,主要有
- 创建
- 增
- 删
- 改
- 查
- 属性操作
- 事件操作
1. 创建
- document.innerHTML
- document.createElement
- document.write (不常用)
2. 增
- appChild
- insertBefore
3. 删
- removeChild(child)
4. 改
主要是修改 dom 元素的元素属性,dom 元素 内容、属性、表单的值等
- 修改元素属性:src、herf、title
- 修改普通元素内容:innerHTML、innerText
- 修改表单属性 : value、type、checked、disable…
- 修改元素样式 : style、className…
5. 查
主要是查询 dom 的元素
- DOM 提供的 API 方法:getEleById、getEleByTagName、(方法太古老不建议使用)
- H5 提供的 : getElementByClass、querselector、querselectorAll (提倡)
- 使用节点获取兄弟父子元素:parentNode、 children、perviousElementSibling、nextElementSibling
(提倡)
6. 属性操作
主要用于自定义属性
- setAttribute : 设置 dom 的属性值
- getAttribute : 获取 dom 自定义属性值
- removeAttribute : 移除自定义属性
7. 鼠标事件
给目标元素添加触发事件 和 处理程序
- 常见的鼠标事件
九、DOM 高级(核心)
重点关注
- 注册事件(绑定事件)
- 删除事件(解绑事件)
- DOM 事件流
- 事件对象 (重点)
- 阻止事件冒泡
- 事件委托(代理、委派)
- 常用鼠标事件
- 常用键盘事件
1. 注册事件概述
注册事件方式有两种 :传统注册方式 和 监听注册方式
2. 事件监听方式
addEventListener 事件监听
eventTarget.addEventListener(type,function{},[,useCapture])
接收三个参数:
- type : 事件类型,如 click、mouseover,不同传统方式,不要加 on
- listener : 事件处理函数,事件触发,调用函数
- useCapture : 可选参数,是个布尔值,默认 false
3. 事件监听兼容
注意:兼容原则,先照顾大多数浏览器,再处理特殊浏览器
4. 事件解绑
传统方式解绑: onclick = null
boxs[1].onclick = function () {
alert("我是盒子2");
this.onclick = null;
};
事件监听方式解绑: 注意 此处传入函数名不加括号
//盒子1:绑定事件
boxs[0].addEventListener("click", fn);
// 删除事件
function fn() {
alert("ok");
boxs[0].removeEventListener("click", fn);
}
5. DOM 事件流
事件流 是描述页面接收事件的顺序
事件发生时会在元素节点间按照特定的顺序 传播,这个过程称为为事件流 ;
DOM 事件流 3 阶段
- 捕获阶段 ↓
- 当前目标阶段
- 冒泡阶段 ↑
解释:
- 事件最开始从页面节点的最顶点开始,向着事件发生最具体元素(目标点)下沉这个过程叫捕获
- 下个阶段从具体的节点 ,将事件逐层上传,到最高节点,称为冒泡(最先触发最底层事件)
6. 注意
- JS 代码只执行,冒泡 和 捕获 的一个阶段
- onclick 和 attchEvent 方式,只能得到冒泡阶段
- removeEventListener(“click”, fn,[useCapture]);
第三个参数,true 表示执行捕获阶段,默认 flase 执行冒泡阶段 - 实际开发很少使用捕获,更关注是冒泡
- 事件冒泡有时候会带来麻烦,需要阻止,有时也会带来巧妙地效果
6. 事件对象
解释:
event 代表事件的状态,比如 按键盘、鼠标位置、鼠标按钮的状态等
(1) 简单理解:
事件发生后,跟事件相关的一系列数据的集合都存在 event 里,这就是事件对象,它里面有很多属性方法
box.addEventListener("click", function (event) {
console.log(event);
});
(2) IE78 兼容问题:
因为 IE678 不认识 event 有兼容问题:
- 标准浏览器默认给事件函数传递事件对象,作为参数,设置为 e 就可以使用
- IE678 非标准,不会默认给事件函数传参,需要做兼容处理,使用 window.event 查找
解决:
e = e||window.event ;
(目前很少考虑兼容 IE678,只做参考)
(3) e.target 和 this
- e.target 返回的是触发事件的对象 (【不固定】谁触发指向谁)
- this 返回的一定是绑定事件元素的对象,(【固定】谁绑定指向谁)
box.addEventListener("click", function (event) {
console.log(event);
console.log(this);
});
(4) 事件对象常见属性方法
标准写法:(常用)
//阻止 访问 跳转
var a = document.querySelector("a");
a.addEventListener("click", fn);
function fn(e) {
e.preventDefault(); //dom标准写法
}
兼容性写法:
//兼容性写法
a.onclick = function (e) {
//(1)普通浏览器
//e.preventDefault();
// (2)低版本浏览器 ie678
// e.returnValue();
// (3) 也可以使用 reutrn false 没有兼容问题,但是后续代码不执行了
return false;
};
(5) 阻止冒泡影响
父子元素叠加可能会带来冒泡影响,例如都是 click ,点击儿子可能父亲也会响应
这就需要 使用 事件对象,给父子都添加阻止冒泡方法
e.stopPropagation()
: 阻止冒泡标准
兼容 IE 写法: 注意兼容问题:天杀 IE678 不兼容,
e.cancelBubble();
(7) 事件委托
冒泡的本身带来坏处,导致混乱响应,但是灵活使用也会带来好处。我们利用冒泡称为事件委托
事件委派
事件委托也称为事件委派,在 jquery 称为事件委派
事件委托原理
不是每个子节点都会单独设置监听器,将事件监听器设置在其父节点上,利用冒泡原理影响每个子节点。
以下案例:
-
给 ul 注册点击事件
-
然后利用事件对象 event 的 target 来找到当前点击的 li
-
事件会冒泡到 ul 上,ul 上有注册事件,就会触发事件监听器
事件委托左右:
我们只操作一次 DOM ,简化了代码书写,提高了程序性能
(8) mouseenter 和 mouseover 区别
mouseenter 鼠标事件
-
当鼠标移动到元素上就会触发 mouseenter
-
类似于 mousemove 事件,他俩差别是 :
-
mouseover 为父盒子经过会触发 move 事件,但是经过子盒子也会触发
-
但是 mouseenter 只是经过自己会触发,子盒子不会
-
因为 mouseenter 不会冒泡
-
跟着 mouseenter 搭配的 mouseleave 也不会冒泡
冒泡 | 不冒泡 |
---|---|
mouseover | mouseenter |
mouseout | mouseleave |
(9) 常用的鼠标事件
- 禁止鼠标右键菜单
contextmenu 主要是控制 何时显示上下文菜单,商业应用禁止 复制
document.addEventListener("contextmenu", function (e) {
e.preventDefault();
});
- 禁止鼠标 文字选中
document.addEventListener("selectstart", function (e) {
e.preventDefault();
});
7.鼠标事件对象
event 代表事件的状态,内置状态的集合,集合包含一些列的属性和方法
现阶段常用的是
- 鼠标事件对象 MouseEvent
- 键盘事件对象 KeyboardEvent
(1) 图片跟随鼠标
- 获取鼠标在页面相对位置 pageY、pageX
- 图片父元素 和 图片定位,且获取 对象
- 鼠标移动触发 mousemove 动态修改图片定位
document.addEventListener("mousemove", fn);
var imgs = document.querySelector(".mouse");
//1.获取水表 位置
function fn(e) {
let x = e.pageX;
let y = e.pageY;
imgs.style.left = `${x}px`;
imgs.style.top = `${y}px`;
console.log("x轴:" + x + "____" + "y轴:" + y);
}
8.常用的键盘事件
事件可以鼠标触发,还可以键盘触发
注意:
- 如果使用 addEvenListener 不要加 on
- onkeypress 和 down 区别,不识别 功能键
- 三个的执行顺序: down - press -up
(1) 监听键盘按下键
keyCode: 返回该键的 ASCII 值
ASCII 参考表
注意字母大小写:
- keyup 和 keydown 是不区分字母大小写的
- keypress 区分大小写
document.addEventListener("keydown", function (e) {
console.log(e);
});
输出:
key: "F12"
keyCode: 123
(2) 按键激活输入框
- 添加键盘事件
- 获取输入框对象
- 获得返回按键码进行监听 s =83
- 确认是按的 s ,调用输入框的 focus()方法激活光标
// 1.添加键盘事件
document.addEventListener("keyup", fn);
//2.获取输入框对象
var sear = document.querySelector(".searbox").children[0];
function fn(e) {
console.log(e.keyCode);
if (e.keyCode === 83) {
sear.focus();
}
}
9.快递表单放大效果
<div class="ems">
<div class="number">
<span>公司名称</span>
<input type="text" name="" id="">
<div class="tipbox">
<div class="triangle"></div>
</div>
</div>
</div>
var inp = document.querySelector(".number").children[1];
var tipbox = document.querySelector(".number").children[2];
inp.addEventListener("keyup", fn);
function fn() {
if (inp.value !== "") {
console.log(inp.value);
// 得到的数据 写入放大框
tipbox.innerText = this.value;
// 让 提示框展示出来
tipbox.style.display = "block";
} else {
tipbox.style.display = "none";
}
}
// 失去焦点 隐藏输入框
inp.addEventListener("blur", fn2);
function fn2() {
tipbox.style.display = "none";
}
// 得到焦点 ,如果内容非空,显示提示框
inp.addEventListener("focus", fn3);
function fn3() {
if (tipbox.innerText !== "") {
tipbox.style.display = "block";
}
}
九、BOM 浏览器对象
1. BOM 概述
BOM(Browser Object Model)浏览器对象模型,它提供了独立内容而与浏览器窗口进行交互的对象,其核心对象 window
BOM 由一系列相关的对象构成,并且 每个对象提供了很多方法 和 属性
BOM 缺乏标准,JavaScript 语法组织是 ECMA ,
DOM 标准化组织是 W3C
2. BOM 构成
DOM 和 BOM 比,BOM 更大
window 对象是浏览器的顶级对象,它具备双层角色
- 他是 JS 访问浏览器窗口的接口 ;
- 他是一个全局对象,定义在全局作用域中的变量,函数都会 window 对象的属性和方法
- 在调用的时候可以省略 window , 前面学习的对话框都属于 window 对象方法,比如 alert、promot
注意:window 下的特殊属性 window.name
3. 窗口加载事件
window.onload
传统方式:
window.onload()=function(){}
推荐新方式:
window.addEventListener("load",function(){})
window.load 是页面加载事件,当页面完全加载完毕会才会触发(包括图像、脚本、CSS 文件等)
加载完调用函数 ;
注意:
- 使用了 window.load 内的 js,就可以放在页面任意地方,因为其他内容加载完毕才会执行它
- window.load 传统方式只能注册一次,后续的同事件,会覆盖之前的
- 如果使用新方法注册,就不会覆盖,同时有效
4. DOMContentLoaded
它是针对 dom 作为参考对象的延迟加载的方法,等 DOM 加载完毕(不包含图片、css)
才会执行里面的 js,速度比 window.load 更快些
document.DOMContentLoaded
5. 调整窗口大小事件
- window.innerWidth : 获取当前浏览器窗口宽度/高度
- window.onresize : 事件可以监听 浏览器窗口尺寸变化
注意:
使用 尺寸监听事件的传统写法和监听写法,注意有无 on- 开头
// 添加 窗口大小事件
window.addEventListener("resize", fn);
var box = document.querySelector(".box");
function fn() {
var wid = window.innerWidth;
console.log(`当前宽度:${wid}`);
//浏览器窗口宽度小于 800 隐藏盒子
if (wid < 800) {
box.style.display = "none";
} else {
box.style.display = "block";
}
}
6. 定时器
window.setTimeout(方法,延迟 ms);
- 只会执行一次,第二个参数是延迟可以省略单位毫秒
- 该方法是全局方法,window 前缀可以省略
- 使用为了区分,一般会定义个方法名(标识符),后续清除定时器使用标识符
window.setTimeout(function () {
alert("只执行一次");
}, 1000);
4S 关闭广告:
var imgs = document.querySelector("img");
var set4000 = window.setTimeout(fn, 4000);
//回调函数:关闭广告
function fn() {
imgs.style.display = "none";
}
7. 定时器清除
清除上面定时器
// 清除定时器
document.querySelector("button").onclick = function () {
clearTimeout(set4000);
};
8. 间隔定时器
setInterval() 循环定时器
定时器实现,倒计时面板
<div class="box">
<h1>京东秒杀</h1>
<ul>
<li>03</li>
<li>22</li>
<li>42</li>
</ul>
</div>
var ul = document.querySelector("ul");
var hour = ul.children[0];
var min = ul.children[1];
var second = ul.children[2];
// 修改标签内时间
var endTime = "2021-3-23 8:21:00"; // 自定义截止时间
setInterval(function () {
countDown(endTime);
}, 1000);
// 创建给定时间戳,计算剩余时间
function countDown(time) {
var now = +new Date(); //获得当前时间戳
var setTime = new Date(time); //用户设置时间总毫秒数
var times = (setTime - now) / 1000; //得剩余毫秒数
var overTime = []; //返回数据盛放
d = parseInt(times / 60 / 60 / 24); //天
h = parseInt((times / 60 / 60) % 24); //时
m = parseInt((times / 60) % 60); //分
s = parseInt(times % 60); //秒
overTime.push(d); // 推入数组
overTime.push(h);
overTime.push(m);
overTime.push(s);
console.log(d + "天" + h + "时" + m + "分" + s + "秒");
// 修改标签内时间 时、分、秒
hour.innerText = overTime[1];
min.innerText = overTime[2];
second.innerText = overTime[3];
}
停止定时器
var btn = document.querySelector(".btn");
// 点击停止 定时器
btn.addEventListener("click", function () {
clearInterval(setInter);
});
9. 短信限制读秒
- 点击按钮后限制再次点击,进入读秒
- 同时按钮中文字有变化,使用定时器倒计时
- 注意:button 里的内容使用 inntext 修改
- 定义一个变量里面是定时器,不断递减
步骤:
- 在外设置定时器标识符,是为了第二步 删除定时器留的外部 访问变量;这是技巧
- button 比较特殊,不同于其他表单控件,使用 innerText 修改内容
//1.获取 节点
var btn = document.querySelector("button");
let count = 3;
var setInter = null;
btn.addEventListener("click", function () {
// 2. 点击修改内容
this.innerText = count;
this.style.backgroundColor = "#dadada";
this.disabled = "disabled";
console.log(this.disabled);
//3.设置定时器读秒
setInter = setInterval(fn, 1000);
});
function fn() {
count--;
btn.innerText = count;
//4.如果读秒结束 删除定时器 且 按钮恢复可用
if (count == 0) {
clearInterval(setInter);
btn.style.backgroundColor = "royalblue";
btn.innerText = "发送";
btn.disabled = false;
count = 3;
}
}
10. this 指向问题
this 指向在函数定义的时候是确定不了的,只有对象调用函数执行的时候才能确定指向,
一般情况 this 指向调用者
- 全局作用域 或者 普通函数的 this 指向全局对象 window(定时器里也是指向 window)
console.log(this); //指向全局
window.fn(); //指向全局
function fn() {
console.log(this);
}
- 对象调用 方法时 this 指向 调用者(对象)
var Obj = {
skill: function () {
console.log(this);
},
};
Obj.skill();
- 按钮对象的事件点击:this 点击后指向 按钮 (节点对象)
var btn = document.querySelector("button");
btn.onclick = function () {
console.log(this);
};
11. 单线程多线程
JS 语言特性就是单线程,因为 JavaScript 早期设计定位就是处理页面中用户的交互,以及操作
DOM 的目的。
局限: 比如我们对某个 DOM 的添加、修改、删除操作不能同时进行,需要排队顺序执行;
1. 同步异步
为了解决单线程排队的问题,利用 CPU 的多核能力,HTML5 提出,web worker 标准,
运行 JS 脚本创建多线程,JS 出现 同步 和 异步
同步:
前一个任务执行完毕,后一个开始执行,任务执行顺序与任务排列数据是一致的;
异步:
- 先执行程序 2,在执行 程序 1
- 因为 程序 1 需要等待,异步机制,直接先执行了 程序 2
setTimeout(function () {
console.log("程序1");
}, 1000);
console.log("程序2");
2. 同异步执行机制
(1)同步任务
同步任务都是在主线程上执行的,形成一个执行栈
(2)异步任务
JS 的异步是通过回调函数实现的,一般而言,异步任务有以下三种内容:
1、普通事件,如 click 、resize
2、 资源加载,如 load 、error 等
3、 定时器,包括 setTineout、setInterval 等
异步任务 相关 回调函数添加到任务队列中(任务队列也称为消息队列)
(3) 执行步骤:
-
先执行栈中的同步任务
-
异步任务(回调函数)提交给【异步处理进程】等待条件满足
-
异步任务条件满足后,被【异步处理程序】放入 任务队列继续等待
-
等到执行栈中的同步任务执行完毕后,主动查询 任务队列,并取出任务执行
-
取到 执行栈内的异步任务,到主线程,会立即开始执行 ;
总结 : 先执行同步任务,异步回调等待进入执行栈执行
(4) 事件循环机制
(event loop):
由于主线程不断重复从任务栈获得任务,执行任务,再获得任务再执行,这种循环叫事件循环机制
3. location 对象
1. URL:
URL 称为统一资源定位符,唯一,指出文件位置 和 浏览器处理它
2. URL 格式组成:
protocol : 协议
格式: protocol : // host [:port] / path / [? query ] # fragment
实例: http: // www. baidu .com / index.html ?name=zhangsan & age =19 # link
组成 | 说明 |
---|---|
protocol | 通信协议: 常用 http、https、ftp、maito |
host | 主机域名: www.baidu.com |
port | 端口号: 可选,省略时,使用默认端口 80 |
path | 路径 : 由一个或多个 / 组成,定位服务器目录或文件位置 |
query | 参数: 键值对形式存在,用 & 连接 |
fragment | 片段: # 后面内容,常见用于锚点 |
3. location 属性
4. 跳转获取参数
从 index.html 页面跳转到 第二页面,携带参数,在第二个页面展示
- 第一个页面输入表单,设置 表单提交
- name: username 是参数的键 ,表单内容 是值,点击按钮提交到第二页面
- 第二个页面获取参数并处理参数,先剔除
?
,再 split 分离为数组 - 得到的用户名,innerText 输出在页面上
<form action="login.html">
用户名:<input type="text" name="username" />
<button type="submit" value="点我登录">点击跳转</button>
</form>
// 1.获取参数,去除 ?
var param = location.search;
//2. 截取有效部分,从第一个参数截到末尾 ?username=zhangjian+
param = param.substr(1);
console.log(param);
//3. 使用 = 分割 split :["username", "zhangjian"]
var arr = param.split("=");
//4. 修改文字内容
var p = document.querySelector("p");
p.innerText = `早上好!${arr[1]}`;
console.log(p);
5. location 常用方法
6. history 前进后退
- history.forward();
- history.back();
- history.go(-1/1);
box.children[0].addEventListener("click", function () {
history.forward();
})
十二、PC 端网页特效导读
1. offset 概述
offset 翻译过来就是偏移量,我们使用 offset 相关的属性可以动态的得到元素的偏移量
- 获得元素距离在有定位的父元素内的位置
- 获得元素自身大小,宽度高度
- 注意:获得返回结果没有单位 px
- parntNode 和 offsetParent 不同,parntNode 不需要定位
2. offset 和 style 区别
3. 鼠标在盒子里的定位
淘宝的商铺放大镜需要用到鼠标定位原理,鼠标无法直接获取 相对盒子里的定位,需要简单计算
- 先获取盒子相对浏览器的定位
- 再获取鼠标相对浏览器的定位
- 差值相减得到鼠标相对盒子的定位
- mousemove 鼠标移动就会触发
var spans = document.querySelectorAll("span");
var box = document.querySelector(".box");
// 鼠标在盒子内的定位 输出
box.addEventListener("mousemove", function fn1(e) {
var X = e.pageX - box.offsetLeft;
var Y = e.pageY - box.offsetTop;
spans[0].innerText = X;
spans[1].innerText = Y;
});
4. 元素偏移量 offset 系列
(1) 模态框拖拽要求
弹出框,我们称为模态框。
- 点击弹出层会弹出模态框,并且会显示灰色的半透明遮罩层
- 点击关闭按钮,可以关闭模态框 ,同时关闭 灰色半透明遮挡层
- 鼠标放在模态框最上面一层,可以按住鼠标拖拽模态框在页面上移动
- 鼠标松开可以停止模态框
(2) 案例分析:
① 点击弹出层,登录模态框 和 遮挡层 会显示出来 display :block
② 点击关闭 按钮,模态框 和 遮挡层 display:none 异常
③ 页面拖拽原理 : 鼠标按下 并且移动 ,之后 松开鼠标
④ 按下 mousedown、 移动 mousemove、松开 mouseup 、
按下过程 :
- 得到 鼠标
e.pageX/Y
坐标 和 盒子offsetLeft/Top
,相减得到,鼠标在盒子内的偏移量 x、y ; - 鼠标不松手拖动过程中,这个相对位置的坐标是不变的
拖动过程 :
- mousemove 拖动不断触发,鼠标相对浏览器窗口坐标不断变化
- 鼠标坐标发生变化,需要不断求 最新的坐标 :面板定位 =鼠标定位 - 盒子内定位
- 实时监听,将面板定位左边 设置 到 面板 position 属性中
鼠标松手 :
- 在 鼠标 mousemove 移动事件内监听 设置鼠标弹起监听,mouseup
- 如果鼠标弹起,立即 removeEventListener
// 获取操作节点
var content = document.querySelector(".login-input-cont");
loginBg.style.height = innerHeight + "px";
var movetap = document.querySelector(".movetap");
// (1)鼠标按下后,触发事件
movetap.addEventListener("mousedown", function down(e) {
// (2)获取- 鼠标相对窗口的定位
var mouseX = e.pageX;
var mouseY = e.pageY;
// (3) 计算鼠标在盒子内的坐标(固定不变)
var x = mouseX - content.offsetLeft;
var y = mouseY - content.offsetTop;
// (4)鼠标拖动,鼠标定位发生改变,实时计算
movetap.addEventListener("mousemove", function move(e) {
var X = e.pageX - x; // 盒子相对窗口的坐标
var Y = e.pageY - y;
// (5) 修改 注册面板 在浏览器窗口位置
content.style.left = X + "px";
content.style.top = Y + "px";
// (6) 监听鼠标松开 : 移除 move 监听
movetap.addEventListener("mouseup", function () {
movetap.removeEventListener("mousemove", move);
});
});
});
5. 案例-京东放大镜
实现京东商品图放大镜效果,大量使用到 元素offset
属性 和 鼠标 e.pageX
属性
<div class="box">
<img src="../../img/商品放大镜.jpg" alt="" />
<div class="zoom"></div>
<div class="big"><img src="../../img/商品放大镜.jpg" alt="" /></div>
</div>
//获得鼠标在盒子内的定位
var box = document.querySelector(".box");
var zoom = document.querySelector(".zoom");
var big = box.children[2];
var img = big.children[0];
//(1) 鼠标经过显示,离开隐藏: 黄色盒子 和 放大盒子
box.addEventListener("mouseover", function () {
big.style.display = "block";
zoom.style.display = "block";
this.addEventListener("mouseout", function () {
big.style.display = "none";
zoom.style.display = "none";
});
});
// (2) 监听计算鼠标在盒子内得 坐标
box.addEventListener("mousemove", move);
function move(e) {
var zoomWdi = zoom.offsetWidth / 2;
var zoomHei = zoom.offsetHeight / 2;
var x = e.pageX - this.offsetLeft - zoomWdi;
var y = e.pageY - this.offsetTop - zoomHei;
//(4)限制 黄盒 X、Y轴移动范围
if (x <= 0) {
x = 0;
} else if (x > box.offsetWidth - zoom.offsetWidth) {
// 父盒宽 - 子盒宽
x = box.offsetWidth - zoom.offsetWidth;
}
if (y <= 0) {
y = 0;
} else if (y > box.offsetHeight - zoom.offsetHeight) {
y = box.offsetHeight - zoom.offsetHeight;
}
//(3) 黄色盒子移动相应的位移,鼠标居中
zoom.style.left = x + "px";
zoom.style.top = y + "px";
// (4) 放大盒子 跟随 黄色盒子 绝对定位移动 ,需要计算比例
// 大图最大大移动距离 = (遮挡层移动距离 * 遮挡层最大移动距离) / 图片最大移动距离
var X = (big.offsetWidth / box.offsetWidth) * x;
var Y = (big.offsetWidth / box.offsetWidth) * y;
X = X + 100;
img.style.left = -X + "px";
img.style.top = -Y + "px";
}
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.box {
position: relative;
margin: 200px auto;
cursor: move;
width: 400px;
height: 400px;
border: 1px solid #282c3488;
}
.big {
display: none;
overflow: hidden;
position: absolute;
right: -550px;
top: 0;
width: 500px;
height: 500px;
border: 1px solid red;
background-color: #666;
}
.box img {
width: 100%;
}
.big img {
position: absolute;
width: 1000px;
}
.zoom {
display: none;
position: absolute;
top: 0;
left: 0;
width: 200px;
height: 200px;
background-color: rgba(255, 255, 0, 0.377);
}
</style>
6. client 系列
client 翻译过来就是 客户端,我们使用 client 获取元素 可视区相关信息,
使用 client 系列相关属性,可以动态 获得元素的大小 和 边框大小,和 offset 有区别
7. 立即执行函数
优点 : 创建了单独的作用域,避免多个 js 文件里,命名冲突,常用
//方式1: (function(){})([参数]) ;
//方式1: (匿名函数)([参数]) ;
(function (a, b) {
console.log("方式1:匿名" + a + b);
})(1, 2);
//方式2: (function(){}())
(function (a, b) {
console.log("方式2:自调" + a + b);
}(3, 4));
(1)JS 模块封装
JS 模块封装 就使用这个方法了,避免其他脚本命名冲突
- 注意,传入 DOM 和 BOM 顶级对象
(function (window,document) {
fn1(){};
fn2(){};
fn3(){};
.........
............
})(window,document)
8. 元素滚动 scroll 系列
scroll 是滚动的意思,包含了内容滚动,使用该属性 可以动态得到,元素大小、滚动距离等
不同于 client ,内容超出父元素 也在计算在内
(1) 页面被卷去的头部
如果浏览器高度(宽度)不够显示整个页面,会自动出现滚动条,当滚动条向下滚动时,
- 页面上被隐藏的高度 称为 :scrollTop 被卷去的头部
- 页面内容整体的高度/宽度 :srollHeight /scrollWidth
- 滚动条在滚动时,会触发滚动事件 onscroll
(2) 仿:淘宝固定右侧侧边栏
- 原先侧边栏是绝对定位
- 当页面滚动到一定的位置,侧边栏改为 固定定位
- 页面继续滚动,会让 返回顶部 按钮显示出来
思路:
① 需要用到页面滚动事件,因为滚动的是页面不是某个元素,所以 document
② 页面滚动到某个位置,需要判断被卷上去的值
③ 这里不同于普通元素,这是父元素是页面,参照着页面 document
④ 使用 window.pageYOffset , 普通元素使用 elem.scrollTop
<div class="w">
<div class="hearder">hearder</div>
<div class="banner">banner</div>
<div class="main content">main</div>
<div class="hearder">hearder</div>
<div class="banner">banner</div>
<div class="hearder">hearder</div>
<div class="banner">banner</div>
<div class="sider">
<div class="other">
猜你 <br />
喜欢
</div>
<div class="other">
反馈 <br />
我们
</div>
<div class="other">
限时 <br />
优惠
</div>
<div class="goback">
<a href="index.html"
>↑返回 <br />
顶部</a
>
</div>
</div>
</div>
var sider = document.querySelector(".sider");
var w = document.querySelector(".w");
var main = document.querySelector(".content");
var siderWidth = sider.offsetWidth;
console.log(siderWidth);
// (1)页面滚动监听事件
document.addEventListener("scroll", function () {
// (2) document 获得 已头部卷去高度
var scroHei = window.pageYOffset;
// (3) 页面卷上去达到 500像素,更换 fixed,同时要设置相对浏览器的top left
var fixRight = (innerWidth - main.offsetWidth) / 2 - siderWidth - 7;
if (scroHei > 500) {
sider.style.position = "fixed";
sider.style.top = 0 + "px";
sider.style.right = fixRight + "px";
} else {
sider.style.position = "absolute";
sider.style.top = 500 + "px";
sider.style.right = -55 + "px";
}
});
(3) 页面卷去头部兼容问题
9. 三大系列小结
三个系列都能返回元素 大小、位置等等
三大系列对比 | 左右 |
---|---|
element.offsetWidth | 返回 :【包含边框】、padding、内容区域宽度,不带 px |
element .clientWidth | 返回 【不含边框】: padding、内容区域 宽度 ,不带 px |
element . scrollWidth | 返回 :【自身实际宽度】、【不含边框】 |
主要使用场景:
- offset 系列经常使用获取元素位置 : offsetTop、offsetLeft
- client 经常用于获取 元素的大小 : clientWidth、clientHeight
- scroll 经常用来获取 滚动距离的
- 注意: 页面滚动要使用 window.pageYOffset 获得
十三、 动画实现原理
核心原理:
通过定时器 setInertval
1.动画函数简单封装
函数简单封装,使用方法调用传入 对象 和 其他参数即可
- 每次调用都要在内存声明新的标识符盛放定时器,大量的调用会内存浪费
- 根据传入对象 ,在参数对象内声明独有的属性,盛放自己的定时器,解放了内存,增强了独立性
问题:
- 注意如果事件监听触发,重复触发会叠加很多定时器,动画失常,执行前清理历史同名定时器
- 添加定时器前先判断清除同名丁思琪
var box1 = document.querySelector(".box1");
var count = 0;
// 点击开始
box1.addEventListener("click", function () {
animate(box1, 300);
});
// 动画函数
function animate(obj, target) {
//防止多次点击,清除历史定时器
clearInterval(obj.timer);
obj.timer = setInterval(function () {
if (obj.offsetLeft > target) {
console.log("停下删除定时器");
clearInterval(obj.timer);
} else {
obj.style.left = `${count++}px`;
console.log("继续");
}
}, 10);
}
2.缓动动画公式
缓动动画公式就是让元素运动速度有所变化,常用的是 线快后慢再停
思路:
- 让每次盒子每次移动的距离慢慢变小,速度就会逐渐下降
- 核心算法 :(目标位置 - 现在位置) / 10 结果越来越小,作为每次移动的距离
- 停止条件:让当前盒子的位置,等于目标位置,就清理定时器
问题:
- JS 里常见会出现像素计算等,会出现 JS 不擅长的浮点运算,我们需要手动取整,避免误差
- 一般我们会选择 正值上取整 ceil; 负值 向下取整* 这样就不会出现像素不足的问题了精确
return Math.ceil((endX - nowX) / 10);
var box1 = document.querySelector(".box1");
// 点击开始
box1.addEventListener("click", function () {
animate(box1, 500);
});
// 动画函数
function animate(obj, target) {
//防止多次点击,清除历史定时器
clearInterval(obj.timer);
obj.timer = setInterval(function () {
if (obj.offsetLeft > target) {
console.log("停下删除定时器");
clearInterval(obj.timer);
} else {
// 位置= 现在位置 + 下次移动单位
obj.count = obj.offsetLeft + liner(obj, target);
obj.style.left = `${obj.count}px`;
}
}, 20);
}
// 实现线性计算的方法
//实现 缓动特效,把每次移动的单位长度变小 (终点位置 - 当前位置) /10
// 由于 差值不断在线性变小,后一次的移动距离也在线性变小
function liner(obj, target) {
let endX = target;
let nowX = obj.offsetLeft;
return (endX - nowX) / 10;
}
2.缓动动画回调函数
动画回调函数原理:函数作为调用 另外一个函数的一个参数传递,被传入函数执行完毕后,再执行传入进去的这个函数,这个过程叫做 回调
- 回调函数以参数形式传入
- 动画回调函数一般写在 最后一步,清除定时器的后面,压在最后执行
var box1 = document.querySelector(".box1");
var btn500 = document.querySelector(".btn500");
var btn800 = document.querySelector(".btn800");
// 点击开始
btn500.addEventListener("click", function () {
animate(box1, 500, function () {
box1.style.backgroundColor = "red";
alert("执行结束");
});
});
obj.timer = setInterval(function () {
if (obj.offsetLeft === target) {
clearInterval(obj.timer);
if (callback) {
callback();
}
} else {
// 位置= 现在位置 + 下次移动单位
obj.count = obj.offsetLeft + liner(obj, target);
obj.style.left = `${obj.count}px`;
}
}, 20);
}
3.实现侧边栏滑动
因为以后会经常用到这个函数,可以单独封装一个线性动画的执行函数,需要的时候引入这个函数
<div class="sliderbar">
<span>→</span>
<div class="con">反应问题</div>
</div>
// 实现鼠标放上去 就伸出来
var con = document.querySelector(".con");
var span = document.querySelector("span");
span.addEventListener("mouseover", function () {
// 调用动画函数
animate(con, 100, function () {
span.innerText = "←"; // 修改箭头方向,定时器执行完毕执行
});
span.addEventListener("mouseout", function () {
animate(con, -10, function () {
span.innerText = "→"; // 修改箭头方向,定时器执行完毕执行
});
});
});
// target : 元素目标位移位置的定位
function animate(obj, target, callback) {
console.log("1");
//1.清除之前的定时器
clearInterval(obj.timer);
obj.timer = setInterval(function () {
if (obj.offsetLeft === target) {
clearInterval(obj.timer);
// 如果回调函数存在,就执行
if (callback) {
callback();
}
} else {
let step = obj.offsetLeft + liner(obj, target);
obj.style.left = step + "px";
}
}, 10);
}
// 线性提供移动单位 liner
function liner(obj, target) {
let res = (target - obj.offsetLeft) / 10;
if (res > 0) {
return Math.ceil(res);
} else {
return Math.floor(res);
}
}
4.轮播图
使用特效实现轮播图效果
原理图:
功能:
- 鼠标经过轮播图,切页按钮才显示
- 点击右侧按钮一次,切图一张,依次列推
- 图片播放时 底下的导航小圈圈也跟着变成红色
- 点击小圆圈,就会播放相应的图片
- 鼠标不经过图片,也会自动播放图片
案例分析:
① 因为 JS 较多,分离开发,单独引入 js 文件
② 此时需要加 load 事件,等到 content 加载完毕 才加载动效
③ 鼠标经过 图片显示 按钮,离开隐藏
(1) 动态生成导航圈
① 点击小圆圈滚动图片
② 此时用到自己封装的 animate.js 动画封装脚本,在页面 js 前面引入,因为被使用者需要在前加载
③ 动画移动前提是被移动的元素和其父元素都要有定位!
④ 注意 是 ul 移动,不是小 li, ul 和 ul 的父盒子都要都定位,
⑤ 滚动的核心算法 : 点击小圆,就让 图片滚动小圆圈索引号 乘以 图片的宽度作为 ul 移动距离
(2) 左右导航按钮实现
① 点击右侧按钮一次,就让图片滚动一张的 偏移量
② 声明一个变量 num,点击一次自增 1 ,让这个变量乘以 图片宽度,偏移相应的距离
③ 图片无缝滚动原理:
④ 把 ul 里的第一个 li 复制一份,放到 ul 最后面
⑤ 当图片滚动到克隆的最后一张图的时候,让 ul 立即 不做动画过渡的直接跳回第一张:left:0
⑥ 同时 num 设置为 1 ,动画方法继续调用,num = 1 ,重新开始滚动
⑦ num 设置为 1 是为了 动画立即往下一个单位移动
// 左侧导航按钮翻页
let num = 0; // 点击一次num自增1 ,偏移量 = num * boxWidth
btnLeft.addEventListener("click", function () {
num++;
if (num === 5) {
ul.style.left = 0;
console.log("最后一张");
num = 1;
}
animate(ul, -boxWidth * num);
console.log(num);
});
});
(4) 左右导航按钮实现
(5) 手动调用事件
为了方便方法的实现,element.click() 可以模拟对控件的点击
- 定时器内实现自动播放功能,效果类似手动点击 :btnLeft.click();
- 我们再让鼠标经过定时器清除,停止自动播放
- 鼠标离开,定时器开始
// 手动调用点击事件 emlemnt.click();
var timer = setInterval(function () {
btnLeft.click();
}, 1000);
box.addEventListener("mouseenter", function () {
clearInterval(timer);
});
box.addEventListener("mouseleave", function () {
timer = setInterval(function () {
btnLeft.click();
}, 1000);
});
(6) 节流阀
为了防止按钮连续点击过快
目的:
控制上一个函数动画执行完毕,再执行下一次函数动画,让事件无法快速连续触发
核心思路:
利用回调函数,添加一个变量 flag = ture,来锁住函数 和 解锁函数
开始设置: var flag = ture
!(flag){ flag=false , do somting… } 关闭水阀
利用回调函数,动画执行完毕才会执行的特性,执行结束后 flag =true 再打开水阀
btnLeft.addEventListener("click", function () {
if (flag) {
// 1. 节流阀内部,先关上阀门
flag = false;
// 等待动画函数执行结束,执行回调, 打开阀门
animate(Obj, param, function callback () {
flag = true;
});
}
});