JavaScript基础

目录

[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. 转数字类型方法

常用方法:

  1. parseInt(" 11 ") 取整
  2. parseInt("22.32 ")取浮点

其他方法:

  1. 强制类型转换:Number(" 122 ")
  2. 算数隐式转换:('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) 字符串其他方法

案例应用:根据字符串查找 出现次数最多的字符,次数
思路:

  1. 创建个新的字符数组对象,键值对: 字符 次数
  2. 遍历键值对对象,记录 值最大的键值对
  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. 作用域

  1. javascript 作用域:就是代码名字在某个范围起作用和效果,目的是提高程序的可靠性减少名称冲突 ;
  2. 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) 预解析 : 先将代码中,varfunction 提升到作用域的最前面 ;
(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("跳高");
  }
}

创建构造函数

  1. 构造函数-首字母大写
  2. 不需要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 在执行时会做四件事:

  1. 在内存中创建新的空对象
  2. this指向这个方法
  3. 执行构造函数里的方法,给新对象里添加属性和方法
  4. 返回这个新对象(所以构造函数里不需要新的对象)

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

常用的数学函数:

  1. 绝对值
    console.log(Math.abs(-123));

  2. 三个取整方法

  • 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. 猜数字游戏(演示随机数)
// 猜数字游戏

// 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

实现倒计时效果:天、时、分、秒

思路:

  1. 剩余时间 = 将来的时间戳 - 现在的时间戳
  2. 将得到的时间秒数,转化为 天、时、分、秒 即可
 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

关注点:

  1. WEB API 与 JavaScript 语法阶段的关联性
  2. API :
  3. 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 获取方式

注意:

  1. 由于文档页面从上往下加载,<script> 标签我们写在文档标签的下面
  2. get 方式的命名是驼峰命名法
  3. 参数 id 大小写敏感
  4. 返回的是元素的对象object
  5. 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) 百度换肤案例:

思路:

  1. 获取事件源: body 对象、3 个图片对象的集合
  2. 遍历图片集合,循环绑定事件
  3. CSS 创建不同背景属性的类名,留着备用
  4. 点击不同的图片后,给 body 加载不同的 className
(2) 表格全选效果:

要求:

  1. JS 鼠标悬浮每行 hr 变色,onmouseoveronmouseout

  2. 全选和全取消效果,子按钮的选择状态跟着全选按钮状态, checked=true/false

  3. 当子按钮挨个全部选中,全选按钮被影响为选中状态 ;

第一部分思路:

  • 获得全选框 和 子复选框的对象集合
  • 如果全选框【点击选中】,就循环所有子选项,修改为选中状态
  • 如果全选框【点击取消】,循环修改全部子项,为 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 自定义属性
  1. 兼容性获取 element.getAttribute('属性')

  2. 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. 兄弟节点操作(非重点)

兄弟节点

  1. node.nextSibling 获取下个兄弟节点
  2. node.perviousSibling 获取上个兄弟节点

问题:

  1. 返回:#text 返回对象集合中包含 文本节点 和 元素节点, 无法直接使用

优点: 兼容问题: H5 提供,没有兼容问题 IE9 下也支持

常用替代
返回结果是 兄弟元素节点,没有其他类型 nodeType,优点很方便

  1. 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 种方式

  1. document.write() 很少使用
    如果页面加载完毕,会导致页面重绘(清空之前的元素)

  2. innerHTML

  3. 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. 创建

  1. document.innerHTML
  2. document.createElement
  3. document.write (不常用)

2. 增

  1. appChild
  2. insertBefore

3. 删

  1. removeChild(child)

4. 改

主要是修改 dom 元素的元素属性,dom 元素 内容、属性、表单的值等

  1. 修改元素属性:src、herf、title
  2. 修改普通元素内容:innerHTML、innerText
  3. 修改表单属性 : value、type、checked、disable…
  4. 修改元素样式 : style、className…

5. 查

主要是查询 dom 的元素

  1. DOM 提供的 API 方法:getEleById、getEleByTagName、(方法太古老不建议使用)
  2. H5 提供的 : getElementByClass、querselector、querselectorAll (提倡)
  3. 使用节点获取兄弟父子元素:parentNode、 children、perviousElementSibling、nextElementSibling
    (提倡)

6. 属性操作

主要用于自定义属性

  1. setAttribute : 设置 dom 的属性值
  2. getAttribute : 获取 dom 自定义属性值
  3. removeAttribute : 移除自定义属性

7. 鼠标事件

给目标元素添加触发事件 和 处理程序

  1. 常见的鼠标事件


九、DOM 高级(核心)

重点关注

  1. 注册事件(绑定事件)
  2. 删除事件(解绑事件)
  3. DOM 事件流
  4. 事件对象 (重点)
  5. 阻止事件冒泡
  6. 事件委托(代理、委派)
  7. 常用鼠标事件
  8. 常用键盘事件

1. 注册事件概述

注册事件方式有两种 :传统注册方式 和 监听注册方式

2. 事件监听方式

addEventListener 事件监听
eventTarget.addEventListener(type,function{},[,useCapture])

接收三个参数:

  1. type : 事件类型,如 click、mouseover,不同传统方式,不要加 on
  2. listener : 事件处理函数,事件触发,调用函数
  3. 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 阶段

  1. 捕获阶段 ↓
  2. 当前目标阶段
  3. 冒泡阶段 ↑

解释:

  • 事件最开始从页面节点的最顶点开始,向着事件发生最具体元素(目标点)下沉这个过程叫捕获
  • 下个阶段从具体的节点 ,将事件逐层上传,到最高节点,称为冒泡(最先触发最底层事件)

6. 注意

  1. JS 代码只执行,冒泡 和 捕获 的一个阶段
  2. onclick 和 attchEvent 方式,只能得到冒泡阶段
  3. removeEventListener(“click”, fn,[useCapture]);
    第三个参数,true 表示执行捕获阶段,默认 flase 执行冒泡阶段
  4. 实际开发很少使用捕获,更关注是冒泡
  5. 事件冒泡有时候会带来麻烦,需要阻止,有时也会带来巧妙地效果

6. 事件对象

解释:
event 代表事件的状态,比如 按键盘、鼠标位置、鼠标按钮的状态等

(1) 简单理解:

事件发生后,跟事件相关的一系列数据的集合都存在 event 里,这就是事件对象,它里面有很多属性方法

box.addEventListener("click", function (event) {
  console.log(event);
});

(2) IE78 兼容问题:

因为 IE678 不认识 event 有兼容问题:

  1. 标准浏览器默认给事件函数传递事件对象,作为参数,设置为 e 就可以使用
  2. 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 也不会冒泡

冒泡不冒泡
mouseovermouseenter
mouseoutmouseleave
(9) 常用的鼠标事件
  1. 禁止鼠标右键菜单
    contextmenu 主要是控制 何时显示上下文菜单,商业应用禁止 复制
document.addEventListener("contextmenu", function (e) {
e.preventDefault();
});
  1. 禁止鼠标 文字选中
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.常用的键盘事件

事件可以鼠标触发,还可以键盘触发

注意:

  1. 如果使用 addEvenListener 不要加 on
  2. onkeypress 和 down 区别,不识别 功能键
  3. 三个的执行顺序: down - press -up
(1) 监听键盘按下键

keyCode: 返回该键的 ASCII 值
ASCII 参考表

注意字母大小写:

  • keyup 和 keydown 是不区分字母大小写的
  • keypress 区分大小写
document.addEventListener("keydown", function (e) {
  console.log(e);
});

输出:

key: "F12"
keyCode: 123
(2) 按键激活输入框

  1. 添加键盘事件
  2. 获取输入框对象
  3. 获得返回按键码进行监听 s =83
  4. 确认是按的 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 文件等)
加载完调用函数 ;

注意:

  1. 使用了 window.load 内的 js,就可以放在页面任意地方,因为其他内容加载完毕才会执行它
  2. window.load 传统方式只能注册一次,后续的同事件,会覆盖之前的
  3. 如果使用新方法注册,就不会覆盖,同时有效

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 修改
  • 定义一个变量里面是定时器,不断递减

步骤:

  1. 在外设置定时器标识符,是为了第二步 删除定时器留的外部 访问变量;这是技巧
  2. 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 指向调用者

  1. 全局作用域 或者 普通函数的 this 指向全局对象 window(定时器里也是指向 window)
  console.log(this);  //指向全局

  window.fn();    //指向全局
  function fn() {
    console.log(this);
  }
  1. 对象调用 方法时 this 指向 调用者(对象)
var Obj = {
  skill: function () {
    console.log(this);
  },
};

Obj.skill();
  1. 按钮对象的事件点击: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) 执行步骤:
  1. 先执行栈中的同步任务

  2. 异步任务(回调函数)提交给【异步处理进程】等待条件满足

  3. 异步任务条件满足后,被【异步处理程序】放入 任务队列继续等待

  4. 等到执行栈中的同步任务执行完毕后,主动查询 任务队列,并取出任务执行

  5. 取到 执行栈内的异步任务,到主线程,会立即开始执行 ;

总结 : 先执行同步任务,异步回调等待进入执行栈执行

(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) 模态框拖拽要求

弹出框,我们称为模态框。

  1. 点击弹出层会弹出模态框,并且会显示灰色的半透明遮罩层
  2. 点击关闭按钮,可以关闭模态框 ,同时关闭 灰色半透明遮挡层
  3. 鼠标放在模态框最上面一层,可以按住鼠标拖拽模态框在页面上移动
  4. 鼠标松开可以停止模态框
(2) 案例分析:

① 点击弹出层,登录模态框 和 遮挡层 会显示出来 display :block

② 点击关闭 按钮,模态框 和 遮挡层 display:none 异常

③ 页面拖拽原理 : 鼠标按下 并且移动 ,之后 松开鼠标

④ 按下 mousedown、 移动 mousemove、松开 mouseup 、

按下过程

  1. 得到 鼠标e.pageX/Y 坐标 和 盒子 offsetLeft/Top ,相减得到,鼠标在盒子内的偏移量 x、y ;
  2. 鼠标不松手拖动过程中,这个相对位置的坐标是不变的

拖动过程

  1. mousemove 拖动不断触发,鼠标相对浏览器窗口坐标不断变化
  2. 鼠标坐标发生变化,需要不断求 最新的坐标 :面板定位 =鼠标定位 - 盒子内定位
  3. 实时监听,将面板定位左边 设置 到 面板 position 属性中

鼠标松手

  1. 在 鼠标 mousemove 移动事件内监听 设置鼠标弹起监听,mouseup
  2. 如果鼠标弹起,立即 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) 仿:淘宝固定右侧侧边栏
  1. 原先侧边栏是绝对定位
  2. 当页面滚动到一定的位置,侧边栏改为 固定定位
  3. 页面继续滚动,会让 返回顶部 按钮显示出来

思路:
① 需要用到页面滚动事件,因为滚动的是页面不是某个元素,所以 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返回 :【自身实际宽度】、【不含边框】

主要使用场景:

  1. offset 系列经常使用获取元素位置 : offsetTop、offsetLeft
  2. client 经常用于获取 元素的大小 : clientWidth、clientHeight
  3. scroll 经常用来获取 滚动距离的
  4. 注意: 页面滚动要使用 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.缓动动画公式

缓动动画公式就是让元素运动速度有所变化,常用的是 线快后慢再停
思路:

  1. 让每次盒子每次移动的距离慢慢变小,速度就会逐渐下降
  2. 核心算法 :(目标位置 - 现在位置) / 10 结果越来越小,作为每次移动的距离
  3. 停止条件:让当前盒子的位置,等于目标位置,就清理定时器

问题:

  • 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.轮播图

使用特效实现轮播图效果
原理图:

功能:

  1. 鼠标经过轮播图,切页按钮才显示
  2. 点击右侧按钮一次,切图一张,依次列推
  3. 图片播放时 底下的导航小圈圈也跟着变成红色
  4. 点击小圆圈,就会播放相应的图片
  5. 鼠标不经过图片,也会自动播放图片
案例分析:

① 因为 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;
      });

    }
  });
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值