首页 前端知识 JavaScript 学习笔记 超详细(b站pink老师)_pink老师最新版js笔记

JavaScript 学习笔记 超详细(b站pink老师)_pink老师最新版js笔记

2024-04-29 11:04:39 前端知识 前端哥 564 428 我要收藏

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Web前端全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024c (备注前端)
img

正文

****使用场景:****适合于有多个结果的时候,比如学习成绩可以分为: 优 良 中 差

先判断条件1,若满足条件1就执行代码1,其他不执行;若不满足则向下判断条件2,满足条件2执行代码2,其他不执行;若依然不满足继续往下判断,依次类推;若以上条件都不满足,执行else里的代码n

7.2.2 三元运算符

使用场景: 其实是比if双分支更简单的写法,可以使用 三元表达式,一般用来取值

符号:? 与 : 配合使用

语法:

数字补0案例

用户输入1个数,如果数字小于10,则前面进行补0, 比如09

  <script>
    // 1. 用户输入 
    let num = prompt('请您输入一个数字:')
    // 2. 判断输出- 小于10才补0
    num = num < 10 ? 0 + num : num
    alert(num)
  </script>

7.2.3 switch语句

语法:

释义:

找到跟小括号里数据全等的case值,并执行里面对应的代码,例:数据若跟值2全等,则执行代码2;若没有全等 === 的则执行default里的代码

注意事项:

  1. switch case语句一般用于等值判断,不适合于区间判断

  2. switch case一般需要配合break关键字使用,没有break会造成case穿透

简单计算器案例

<script>
    // 1.用户输入  2个数字 +  操作符号  + - *  / 
    let num1 = +prompt('请您输入第一个数字:')
    let num2 = +prompt('请您输入第二个数字:')
    let sp = prompt('请您输入 + - * / 其中一个:')
    // 2. 判断输出
    switch (sp) {
      case '+':
        alert(`两个数的加法操作是${num1 + num2}`)
        break
      case '-':
        alert(`两个数的减法操作是${num1 - num2}`)
        break
      case '*':
        alert(`两个数的乘法操作是${num1 * num2}`)
        break
      case '/':
        alert(`两个数的除法操作是${num1 / num2}`)
        break
      default:
        alert(`请输入+-*/`)
    } 
  </script>
7.3 循环结构

7.3.1 断点调试

作用:学习时可以帮助更好的理解代码运行,工作时可以更快找到bug

浏览器打开调试界面

1. 按F12打开开发者工具

2. 点到sources一栏

3. 选择代码文件,添加断点并刷新

断点:在某句代码上加的标记就叫断点,当程序执行到这句有标记的代码时会停下来

7.3.2 while 循环

while循环就是在满足条件期间,重复执行某些代码。

1. while 循环基本语法:

释义:

跟if语句很像,都要满足小括号里的条件为true才会进入循环体执行代码。while大括号里代码执行完毕后不会跳出,而是继续回到小括号里判断条件是否满足,若满足又执行大括号里的代码,然后再回到小括号判断条件,直到括号内条件不满足,即跳出。

2. while 循环三要素:

循环的本质就是以某个变量为起始值,然后不断产生变化量,慢慢靠近终止条件的过程。

所以,while循环需要具备三要素:

  1. 变量起始值

  2. 终止条件(没有终止条件,循环会一直执行,造成死循环)

  3. 变量变化量(用自增或者自减)

3 循环退出

循环结束:

● break:退出循环

● continue:结束本次循环,继续下次循环

区别:

● continue退出本次循环,一般用于排除或者跳过某一个选项的时候, 可以使用continue

● break退出整个循环,一般用于结果已经得到, 后续的循环不需要的时候可以使用

综合案例:简易ATM取款机案例

<script>
    // 1. 开始循环 输入框写到 循环里面
    // 3. 准备一个总的金额
    let money = 100
    while (true) {
      let re = +prompt(`
        请您选择操作:
        1.存钱
        2.取钱
        3.查看余额
        4.退出
        `)
      // 2. 如果用户输入的 4 则退出循环, break  写到if 里面,没有写到switch里面, 因为4需要break退出循环
      if (re === 4) {
        break
      }
      // 4. 根据输入做操作
      switch (re) {
        case 1:
          // 存钱
          let cun = +prompt('请输入存款金额')
          money = money + cun
          break
        case 2:
          // 存钱
          let qu = +prompt('请输入取款金额')
          money = money - qu
          break
        case 3:
          // 存钱
          alert(`您的银行卡余额是${money}`)
          break
      }
    }
  </script>

8.for循环

1. for循环语法

****作用:****重复执行代码

****好处:****把声明起始值、循环条件、变化值写到一起,让人一目了然,它是最常使用的循环形式

2. 退出循环

● continue退出本次循环,一般用于排除或者跳过某一个选项的时候,可以使用continue

● break退出整个for循环,一般用于结果已经得到, 后续的循环不需要的时候可以使用

了解:

  1. while(true) 来构造“无限”循环,需要使用break退出循环。

  2. for(;😉 也可以来构造“无限”循环,同样需要使用break退出循环。

for循环和while循环的区别:

● 当如果明确了循环的次数的时候推荐使用for循环

● 当不明确循环的次数的时候推荐使用while循环

****3. for 循环嵌套:****一个循环里再套一个循环,一般用在for循环里

案例:九九乘法表

  <script>
    // 1. 外层循环控制行数
    for (let i = 1; i <= 9; i++) {
      // 2. 里层循环控制列数
      for (let j = 1; j <= i; j++) {
        document.write(`<span>${j} X ${i} = ${i * j}</span>`)
      }
      // 换行
      document.write('<br>')
    }
  </script>

9.组****

9.1 数组是什么

数组:(Array)是一种可以按顺序保存数据的
数据类型

场景:如果有多个数据可以用数组保存起来,然后放到一个变量中,管理非常方便

9.2 数组的基本使用

1. 声明语法:

数组是按顺序保存,所以每个数据都有自己的编号

计算机中的编号从0开始,所以小明的编号为0,小刚编号为1,以此类推

在数组中,数据的编号也叫索引或下标

数组可以存储任意类型的数据

2. 取值语法:数组名[下标]

通过下标取数据,取出来是什么类型的,就根据这种类型特点来访问

3. 一些术语:

元素:数组中保存的每个数据都叫数组元素

下标:数组中数据的编号

长度:数组中数据的个数,通过数组的length属性获得

4. 遍历数组(重点)

用循环把数组中每个元素都访问到,一般会用for循环遍历

语法:

9.3 操作数组

数组本质是数据集合, 操作数据无非就是
增 删 改 查

语法:

操作数组-新增

arr.push()
方法将一个或多个元素添加到数组的末尾,并返回该数组的新长度
(重点)

语法:

arr.unshift(新增的内容)
方法将一个或多个元素添加到数组的
开头
,并返回该数组的新长度

语法:

操作数组-删除

arr. pop() 方法从数组中删除最后一个元素,并返回该元素的值

语法:arr.pop()

arr. shift()
方法从数组中删除第一个元素,并返回该元素的值

语法:arr.shift()

数组. splice() 方法 删除指定元素

语法:数组.splice(start, deleteCount, item1, item2, …, itemN)

从start开始删除deleteCount个数组元素,并从start处开始添加item(item可加可不加)

使用场景:

随机抽奖,中奖的用户就需要从数组里面删除,不允许重复抽奖

点击删除按钮,相关的数据会从商品数据中删除

语法:

解释:

start 起始位置:
指定修改的开始位置(从0计数)

deleteCount: 
表示要移除的数组元素的个数。可选的,如果省略则默认从指定的起始位置删

除到最后。

综合案例:根据数据生成柱形图

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        .box {
            display: flex;
            width: 700px;
            height: 300px;
            border-left: 1px solid pink;
            border-bottom: 1px solid pink;
            margin: 100px auto;
            justify-content: space-around;
            align-items: flex-end;
            text-align: center;
        }

        .box>div {
            display: flex;
            width: 50px;
            background-color: pink;
            flex-direction: column;
            justify-content: space-between;
        }

        .box div span {

            margin-top: -20px;
        }

        .box div h4 {
            margin-bottom: -35px;
            width: 70px;
            margin-left: -10px;
        }
    </style>
</head>

<body>
    <script>
        // 1. 四次弹框效果
        // 声明一个新的数组
        let arr = []
        for (let i = 1; i <= 4; i++) {
            // let num = prompt(`请输入第${i}季度的数据:`)
            // arr.push(num)
            arr.push(prompt(`请输入第${i}季度的数据:`))
            // push记得加小括号,不是等号赋值的形式
        }
        // console.log(arr)  ['123','135','345','234']
        // 盒子开头
        document.write(` <div class="box">`)

        // 盒子中间 利用循环的形式  跟数组有关系
        for (let i = 0; i < arr.length; i++) {
            document.write(`
              <div style="height: ${arr[i]}px;">
                <span>${arr[i]}</span>
                <h4>第${i + 1}季度</h4>
              </div>          
            `)
        }
        // 盒子结尾
        document.write(` </div>`)
    </script>
</body>

</html>
9.4 冒泡排序

冒泡排序是一种简单的排序算法。

它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访

数列的工作是重 复地进行直到没有再需要交换,也就是说该数列已经排序完成。

9.5 数组排序

数组. sort() 方法可以排序

let arr = [4, 2, 5, 1, 3]
// 1.升序排列写法
arr.sort(function (a, b) {
return a - b
})
console.log(arr) // [1, 2, 3, 4, 5]
// 降序排列写法
arr.sort(function (a, b) {
return b - a
})
console.log(arr) // [5, 4, 3, 2, 1]

10.函数

10.1 为什么需要函数

函数: function,是被设计为
执行特定任务
的代码

**说明:**函数可以把具有相同或相似逻辑的代码“包裹”起来,通过函数调用执行这些被“包裹”的代码逻辑,这么做的优势是有利于
精简代码方便复用
,提高开发效率

比如我们前面使用的 alert() 、 prompt() 和 console.log() 都是一些 js
函数
,只不过已经封装好了,我们直接使用的。

10.2 函数使用

函数的声明语法

函数名命名规范

和变量命名基本一致;尽量小驼峰式命名法;前缀应该为动词。命名建议:常用动词约定

函数的调用语法:函数名()

注意:声明(定义)的函数必须调用才会真正被执行,使用 () 调用函数

我们曾经使用的 alert() , parseInt() 这种名字后面跟小括号的本质都是函数的调用

函数体:

函数体是函数的构成部分,它负责将相同或相似代码“包裹”起来,直到函数调用时函数体内的代码才会被执行。函数的功能代码都要写在函数体当中。

函数的复用代码和循环重复代码有什么不同?

● 
循环代码写完即执行,不能很方便控制执行位置。

● 函数的复用代码可
随时调用,随时执行,可重复调用。

10.3 函数传参

若函数完成功能需要调用者传入数据,那就需要有参数的函数。
这样可以极大提高函数的灵活性。

声明语法:

参数列表:
传入数据列表;声明这个函数需要传入几个数据;多个数据用逗号隔开。

调用语法:函数名(传递的参数列表)

调用函数时,需要传入几个数据就写几个,用逗号隔开。

**形参:**声明函数时写在函数名右边小括号里的叫形参(形式上的参数)

**实参:**调用函数时写在函数名右边小括号里的叫实参(实际上的参数)

形参可以理解为是
在这个函数内声明的
变量
(比如 num1 = 10)实参可以理解为是给这个变量赋值

开发中尽量保持形参和实参个数一致

我们曾经使用过的 alert(‘打印’), parseInt(‘11’), Number(‘11’) 本质上都是函数调用的传参。

形参: 可以看做变量,但是如果一个变量不给值,
默认是 undefined

但是如果做用户不输入实参,刚才的案例则出现 undefined + undefined 结果是 NaN

可以改进下,用户不输入实参,可以给
形参默认值
,默认为 0, 这样程序更严谨,可以如下操作:

这个默认值只会在缺少实参参数传递时才会被执行,所以有参数会优先执行传递过来的实参, 否则默认为undefined

10.4 函数返回值


返回值
函数的概念: 当调用某个函数,这个函数会返回一个结果出来。

当函数需要返回数据出去时,用
return
关键字。这样
对执行结果的扩展性更高,可以让其他的程序使用这个结果。

语法:return 数据

在函数体中使用 return 关键字能将内部的执行结果交给函数外部使用。return 后面代码不会再被执行,会立即结束当前函数,所以
return 后面的数据不要换行写。
return函数可以没有 return
,这种情况
函数默认返回值为 undefined

10.5 函数细节补充

● 两个相同的函数后面的会覆盖前面的函数

● 在Javascript中 实参的个数和形参的个数可以不一致

如果形参过多 会自动填上undefined (了解即可)

如果实参过多 那么多余的实参会被忽略 (函数内部有一个arguments,里面装着所有的实参)

● 函数一旦碰到return就不会在往下执行了,函数的结束用return

break的结束和return结束有什么区别?

● 
break用来跳出整个语句块,结束当前循环的执行。另外我们要特别注意,
break语句总是跳出自己所在的那一层循环。当两个for循环嵌套时,如果break语句位于内层的for循环,它只会跳出内层的for循环,但不会跳出外层的for循环。

● 
return并不是专门用来结束循环的关键字,但它可以用来结束一个方法或循环,而且还能返回return中的值。当一个方法执行到return语句时,该方法就会被结束。与break和continue不同的是,
return是直接结束整个方法,不管这个return处在多少层的循环之内。

10.6 作用域

通常来说,一段程序代码中所用到的名字并不总是有效和可用的,而限定这个名字的
可用性的代码范围
就是这个名字的
作用域

作用域的使用提高了程序逻辑的局部性,增强了程序的可靠性,减少了名字冲突。

在JavaScript中,根据作用域的不同,变量可以分为:

特殊情况:

1.如果函数内部,变量没有声明,直接赋值
,也当
全局变量
看,但是强烈不推荐

2.但是有一种情况,函数内部的形参可以看做是局部变量。

变量的访问原则:

只要是代码,就至少有一个作用域。如果函数中还有函数,那么在这个作用域中就又可以诞生一个作用域。

访问原则:
在能够访问到的情况下 先局部, 局部没有在找全局

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

  <script>
    let a = 1
    function fn1() {
        let a = 2
        let b = '22'
        fn2()
        function fn2() {
          let a = 3
          fn3()
          function fn3() {
            let a = 4
            console.log(a) //a的值 ?
            console.log(b) //b的值 ?
          }
        }
    }
    fn1()
  </script>

a 的值为4,b的值为 ‘22’

10.7 匿名函数

匿名函数:
没有名字的函数, 无法直接使用。

**使用方式(有两种):**1.函数表达式   2.立即执行函数。

1. 函数表达式

将匿名函数赋值给一个变量,并且通过变量名称进行调用 我们将这个称为
函数表达式

语法:

调用:

其中函数的形参和实参使用跟具名函数一致。

函数表达式要求必须先声明,再调用。

运行结果为:程序报错

2. 立即执行函数

场景介绍: 避免全局变量之间的污染

语法:

注意: 多个立即执行函数要用 ; 隔开,要不然会报错

综合案例:转换时间案例

用户输入秒数,可以自动转换为时分秒

计算公式:计算时分秒

小时: h = parseInt(总秒数 / 60 / 60 % 24)

分钟: m = parseInt(总秒数 / 60 % 60 )

秒数: s = parseInt(总秒数 % 60)

  <script>
    // 1. 用户输入
    let second = +prompt('请输入秒数:')
    // 2.封装函数
    function getTime(t) {
      // 3. 转换 
      let h = parseInt(t / 60 / 60 % 24)
      let m = parseInt(t / 60 % 60)
      let s = parseInt(t % 60)
      h = h < 10 ? '0' + h : h
      m = m < 10 ? '0' + m : m
      s = s < 10 ? '0' + s : s
      return `转换完毕之后是${h}小时${m}分${s}秒`
    }
    let str = getTime(second)
    document.write(str)
  </script>
10.8 逻辑中断

开发中,还会见到以下的写法:

其实类似参数的默认值写法

1. 逻辑运算符里的短路

**短路:**只存在于 && 和 || 中,当满足一定条件会让右边代码不执行

**原因:**通过左边能得到整个式子的结果,因此没必要再判断右边

**运算结果:**无论 && 还是 || ,运算结果都是最后被执行的表达式值,一般用在变量赋值

表达式1 && 表达式2:表达式1为真,返回表达式2;表达式1为假,返回表达式1

表达式1  ||  表达式2:表达式1为真,返回表达式1;表达式1为假,返回表达式2

2. 转换为Boolean型

显示转换: Boolean(内容)

记忆

‘’ 、0、undefined、null、false、NaN 转换为布尔值后都是false, 其余则为 true

隐式转换:

有字符串的加法 “” + 1 ,结果是 “1”

减法 - (像大多数数学运算一样)只能用于数字,它会使空字符串 “” 转换为 0

null 经过数字转换之后会变为 0

undefined 经过数字转换之后会变为 NaN

10.对象

10.1 对象是什么

对象(object):JavaScript 里的一种数据类型。

可以理解为是一种
无序的数据集合
, 注意数组是有序的数据集合。
用来描述某个事物
,例如描述一个人,人有姓名、年龄、性别等信息、还有吃饭睡觉打代码等功能。
如果用多个变量保存则比较散,用对象比较统一。

10.2 对象使用

1. 对象声明语法

2. 对象由属性和方法组成

**属性:**信息或叫
特征
(名词)。 比如 手机尺寸、颜色、重量等…

**方法:**功能或叫
行为
(动词)。 比如 手机打电话、发短信、玩游戏…

3. 属性

数据描述性的信息称为属性,如人的姓名、身高、年龄、性别等,一般是名词性的。

● 属性都是成对出现的,包括
属性名


,它们之间使用英文
:
分隔

● 多个属性之间使用英文
,
分隔

● 属性就是依附在对象上的变量(外面是变量,对象内是属性)

● 属性名可以使用 “” 或 ‘’,
一般情况下省略
,除非名称遇到特殊符号如空格、中横线等

4.对象中的方法

数据行为性的信息称为方法,如跑步、唱歌等,一般是动词性的,其本质是函数。

● 方法是由方法名和函数两部分构成,它们之间使用 : 分隔

● 多个属性之间使用英文 , 分隔

● 方法是依附在对象中的函数

●  方法名可以使用 “” 或 ‘’,一般情况下省略,除非名称遇到特殊符号如空格、中横线等

10.2 对象使用

对象本质是无序的数据集合, 操作数据无非就是 增 删 改 查

查询对象:

  1. 对象名.属性

声明对象,并添加了若干属性后,可以使用 . 获得对象中属性对应的值,我称之为属性访问。

简单理解就是获得对象里面的属性值。

  1. 对象[‘属性’] ( ‘’ 或 “” 都可以)

对于多词属性或则 - 等属性,点操作就不能用了。

[ ] 语法里面的值如果不添加引号 默认会当成变量解析

总结: 没有必要的时候直接使用点语法, 在需要解析变量的时候使用 [] 语法

修改属性:
对象名.属性 = 新值

增加属性:
对象名.新属性 = 新值

删除属性:
delete 对象名.属性

方法调用:
声明对象,并添加了若干方法后,可以使用 . 调用对象中函数,称之为方法调用。

也可以添加形参和实参。

10.3 遍历对象

一般不用这种方式遍历数组、主要是用来遍历对象

for in语法中的 k 是一个变量, 在循环的过程中依次代表对象的属性名

由于 k 是变量, 所以必须使用 [ ] 语法解析

一定记住:

k
是获得对象的
属性名

对象名[k]
是获得
属性值

案例:渲染学生信息表

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        table {
            width: 600px;
            text-align: center;
        }

        table,
        th,
        td {
            border: 1px solid #ccc;
            border-collapse: collapse;
        }

        caption {
            font-size: 18px;
            margin-bottom: 10px;
            font-weight: 700;
        }

        tr {
            height: 40px;
            cursor: pointer;
        }

        table tr:nth-child(1) {
            background-color: #ddd;
        }

        table tr:not(:first-child):hover {
            background-color: #eee;
        }
    </style>
</head>

<body>
    <h2>学生信息</h2>
    <p>将数据渲染到页面中...</p>

    <table>
        <caption>学生列表</caption>
        <tr>
            <th>序号</th>
            <th>姓名</th>
            <th>年龄</th>
            <th>性别</th>
            <th>家乡</th>
        </tr>
        <!-- script写到这里 -->
        <script>
            // 1. 数据准备
            let students = [
                { name: '小明', age: 18, gender: '男', hometown: '河北省' },
                { name: '小红', age: 19, gender: '女', hometown: '河南省' },
                { name: '小刚', age: 17, gender: '男', hometown: '山西省' },
                { name: '小丽', age: 18, gender: '女', hometown: '山东省' },
                { name: '晓强', age: 16, gender: '女', hometown: '蓝翔技校' }
            ]
            // 2. 渲染页面
            for (let i = 0; i < students.length; i++) {
                document.write(`
                <tr>
                    <td>${i + 1}</td>
                    <td>${students[i].name}</td>
                    <td>${students[i].age}</td>
                    <td>${students[i].gender}</td>
                    <td>${students[i].hometown}</td>
                </tr>
                `)
            }
        </script>
    </table>

</body>


</html>
10.4 内置对象

1.内置对象是什么?

JavaScript 内部提供的对象,包含各种属性和方法给开发者调用。

思考:我们之前用过内置对象吗?

document.write() 、console.log()

2.内置对象-Math

介绍:
Math对象是JavaScript提供的一个“数学”对象

作用:
提供了一系列做数学运算的方法

Math对象包含的方法有:

● random:生成0-1之间的随机数(包含0不包括1)

● ceil:向上取整

● floor:向下取整

● max:找最大数

● min:找最小数

● pow:幂运算

● abs:绝对值

生成任意范围随机数

Math.random() 随机数函数, 返回一个0 - 1之间,并且包括0不包括1的随机小数 [0, 1)

如何生成0-10的随机数呢?     
Math
.
floor
(
Math
.
random
() * (
10
+
1
))

如何生成5-10的随机数?         
Math
.
floor
(
Math
.
random
() * (
5
+
1
)) + 5

如何生成N-M之间的随机数?  
Math
.
floor
(
Math
.
random
() * (
M - N
+
1
)) + N

案例:
猜数字游戏(限定次数)

  <script>
    // 1. 随机生成一个数字 1~10
    // 取到 N ~ M 的随机整数
    function getRandom(N, M) {
      return Math.floor(Math.random() * (M - N + 1)) + N
    }
    let random = getRandom(1, 10)
    // 2. 设定三次  三次没猜对就直接退出
    let flag = true  // 开关变量 
    for (let i = 1; i <= 3; i++) {
      let num = +prompt('请输入1~10之间的一个数字:')
      if (num > random) {
        alert('您猜大了,继续')
      } else if (num < random) {
        alert('您猜小了,继续')
      } else {
        flag = false
        alert('猜对了,真厉害')
        break
      }
    }
    // 写到for的外面来
    if (flag) {
      alert('次数已经用完')
    }
  </script>

案例:生成随机颜色

如果参数传递的是true或者无参数,则输出 一个随机十六进制的颜色

如果参数传递的是false,则输出 一个随机rgb的颜色

  <script>
    // 1. 自定义一个随机颜色函数
    function getRandomColor(flag = true) {
      if (flag) {
        // 3. 如果是true 则返回 #ffffff
        let str = '#'
        let arr = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
        // 利用for循环随机抽6次 累加到 str里面
        for (let i = 1; i <= 6; i++) {
          // 每次要随机从数组里面抽取一个  
          // random 是数组的索引号 是随机的
          let random = Math.floor(Math.random() * arr.length)
          str += arr[random]
        }
        return str

      } else {
        // 4. 否则是 false 则返回 rgb(255,255,255)
        let r = Math.floor(Math.random() * 256)  // 255
        let g = Math.floor(Math.random() * 256)  // 255
        let b = Math.floor(Math.random() * 256)  // 255
        return `rgb(${r},${g},${b})`
      }

    }
    // 2. 调用函数 getRandomColor(布尔值)
    console.log(getRandomColor(false))
    console.log(getRandomColor(true))
    console.log(getRandomColor())

  </script>

综合案例:学成在线页面渲染案例

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>学车在线首页</title>
    <link rel="stylesheet" href="./css/style.css">
    <style>

    </style>
</head>

<body>

    <!-- 4. box核心内容区域开始 -->
    <div class="box w">
        <div class="box-hd">
            <h3>精品推荐</h3>
            <a href="#">查看全部</a>
        </div>
        <div class="box-bd">
            <ul class="clearfix">
                <!-- <li>
                    <a href="#">
                        <img src="images/course01.png" alt="">
                        <h4>
                            Think PHP 5.0 博客系统实战项目演练
                        </h4>
                        <div class="info">
                            <span>高级</span> • <span>1125</span>人在学习
                        </div>
                    </a>
                </li> -->
                <script>
                    let data = [
                        {
                            src: 'images/course01.png',
                            title: 'Think PHP 5.0 博客系统实战项目演练',
                            num: 1125
                        },
                        {
                            src: 'images/course02.png',
                            title: 'Android 网络动态图片加载实战',
                            num: 357
                        },
                        {
                            src: 'images/course03.png',
                            title: 'Angular2大前端商城实战项目演练',
                            num: 22250
                        },
                        {
                            src: 'images/course04.png',
                            title: 'AndroidAPP实战项目演练',
                            num: 389
                        },
                        {
                            src: 'images/course05.png',
                            title: 'UGUI源码深度分析案例',
                            num: 124
                        },
                        {
                            src: 'images/course06.png',
                            title: 'Kami2首页界面切换效果实战演练',
                            num: 432
                        },
                        {
                            src: 'images/course07.png',
                            title: 'UNITY 从入门到精通实战案例',
                            num: 888
                        },
                        {
                            src: 'images/course08.png',
                            title: 'Cocos 深度学习你不会错过的实战',
                            num: 590
                        },
                        {
                            src: 'images/course04.png',
                            title: '自动添加的模块',
                            num: 1000
                        }
                    ]

                    for (let i = 0; i < data.length; i++) {
                        document.write(`
                        <li>
                            <a href="#">
                                <img src=${data[i].src} title="${data[i].title}">
                                <h4>
                                   ${data[i].title}
                                </h4>
                                <div class="info">
                                    <span>高级</span> • <span>${data[i].num}</span>人在学习
                                </div>
                            </a>
                        </li>
                      `)
                    }
                </script>
            </ul>
        </div>
    </div>

</body>

</html>

附:style.css

* {
    margin: 0;
    padding: 0;
}
.w {
    width: 1200px;
    margin: auto;
}
body {
    background-color: #f3f5f7;
}
li {
    list-style: none;
}
a {
    text-decoration: none;
}
.clearfix:before,.clearfix:after {
    content:"";
    display:table; 
  }
  .clearfix:after {
    clear:both;
  }
  .clearfix {
     *zoom:1;
  }   
 

.box {
    margin-top: 30px;
}
.box-hd {
    height: 45px;
}
.box-hd h3 {
    float: left;
    font-size: 20px;
    color: #494949;
}
.box-hd a {
    float: right;
    font-size: 12px;
    color: #a5a5a5;
    margin-top: 10px;
    margin-right: 30px;
}
/* 把li 的父亲ul 修改的足够宽一行能装开5个盒子就不会换行了 */
.box-bd ul {
    width: 1225px;
}
.box-bd ul li {
    position: relative;
    top: 0;
    float: left;
    width: 228px;
    height: 270px;
    background-color: #fff;
    margin-right: 15px;
    margin-bottom: 15px;
    transition: all .3s;
   
}
.box-bd ul li a {
    display: block;
}
.box-bd ul li:hover {
    top: -8px;
    box-shadow: 0 15px 30px rgb(0 0 0 / 10%);
}
.box-bd ul li img {
    width: 100%;
}
.box-bd ul li h4 {
    margin: 20px 20px 20px 25px;
    font-size: 14px;
    color: #050505;
    font-weight: 400;
}
.box-bd .info {
    margin: 0 20px 0 25px;
    font-size: 12px;
    color: #999;
}
.box-bd .info span {
    color: #ff7c2d;
}

拓展-术语解释

拓展- 基本数据类型和引用数据类型

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

● 值类型:简单数据类型/基本数据类型,在存储时变量中存储的是
值本身
,因此叫做值类型

string ,number,boolean,undefined,null

● 引用类型:复杂数据类型,在存储时变量中存储的仅仅是
地址(引用)
,因此叫做引用数据类型

通过 new 关键字创建的对象(系统对象、自定义对象),如 Object、Array、Date等

简单数据类型存放到栈里面,引用数据类型存放到堆里面。

简单数据类型传参,是把
值复制给形参,在方法内部对形参做任何改变,
不影响外部变量。

复杂数据类型传参,把变量在栈空间里保存的堆地址复制给了形参,形参和实参保存同一个堆地址,
操作的是同一个对象。

拓展- 变量声明

变量声明:
有了变量先给const,如果发现它后面是要被修改的,再改为let

const 声明的值不能更改,而且const声明变量的时候需要里面进行初始化。
但是
对于引用数据类型

const声明的变量,里面存的不是值,是地址

建议数组和对象使用 const 来声明。

二、Web APIs

1. Web API 基本认知

1.1 作用和分类

Web API是浏览器提供的一套操作浏览器功能和页面元素的API(BOM和DOM)。

作用: 就是使用 JS 去操作 html 和浏览器

分类:
DOM
(文档对象模型)、
BOM
(浏览器对象模型)

1.2 什么是DOM

DOM(Document Object Model——
文档对象模型
)是用来呈现以及与任意 HTML 或 XML文档交互的API。即DOM是浏览器提供的一套专门用来
操作网页内容
的功能。

**DOM作用:**开发网页内容特效和实现用户交互

1.3 DOM树

将 HTML 文档以树状结构直观的表现出来,称之为文档树或 DOM 树。

作用:
文档树直观的体现了标签与标签之间的关系

文档:一个页面就是一个文档,DOM中使用document表示

元素:页面中的所有标签都是元素,DOM中使用element表示

节点:网页中的所有内容都是节点(标签、属性、文本、注释等),DOM中使用node表示

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

1.4 DOM对象(重要)

**● DOM 对象:**浏览器根据 html 标签生成的
JS对象

所有的标签属性都可以在这个对象上面找到,修改这个对象的属性会自动映射到标签身上。

● **DOM 的核心思想:**把网页内容当做
对象
来处理

● **document 对象:**是 DOM 提供的一个
对象,
网页所有内容都在document里面。

document 提供的属性和方法都是
用来访问和操作网页内
容的。
例:document.write()

2.获取DOM对象

查找元素DOM元素就是利用 JS 选择页面中标签元素

2.1 根据CSS选择器来获取DOM元素 (重点)

1. 选择匹配的第一个元素

语法:

document.querySelector('css选择器')

参数: 包含一个或多个有效的CSS选择器
字符串
选择器需要加符号,如类名 .box,id名 #nav

返回值: 返回CSS选择器匹配的
第一个元素
(HTMLElement对象),如果没有匹配则返回 null。

获取的元素可直接修改。

<body>
  <div class="box">123</div>
  <div class="box">abc</div>
  <ul class="nav">
    <li>测试1</li>
    <li>测试2</li>
    <li>测试3</li>
  </ul>
  <script>
    // 获取匹配的第一个元素
    // const box = document.querySelector('div')  // 123
    const box = document.querySelector('.box')    //123
    console.log(box)
    // 获取 ul 第一个小 li
    // const li = document.querySelector('ul li')
    const li = document.querySelector('ul li:first-child')
    console.log(li)
  </script>
</body>

2. 选择匹配的多个元素

语法:

document.querySelectorAll('css选择器')

参数:  包含一个或多个有效的CSS选择器
字符串

返回值: CSS选择器匹配的 
NodeList 对象集合

获取的对象集合不可以直接修改, 只能通过遍历的方式依次给里面的元素做修改。

<body>
  <ul class="nav">
    <li>测试1</li>
    <li>测试2</li>
    <li>测试3</li>
  </ul>
  <script>
    const lis = document.querySelectorAll('.nav li')
    console.log(lis)
  </script>
</body>

querySelectAll() 得到的是一个
伪数组

有长度有索引号,但是没有 pop() push() 等数组方法

想要得到里面的每一个对象,则需要遍历(for)的方式获得。

    const lis = document.querySelectorAll('.nav li')
    for (let i = 0; i < lis.length; i++) {
      console.log(lis[i]) // 每一个小li对象
    }

注意事项:

哪怕只有一个元素,通过querySelectAll() 获取过来的也是一个
伪数组
,里面只有一个元素而已。

若要修改可通过
变量名[0]
进行修改。

<body>
  <p id="nav">导航栏</p>
  <script>
    const p = document.querySelectorAll('#nav')
    p[0].style.color = 'red'
  </script>
</body>

querySelector() 和 querySelectorAll() 的区别:


querySelector()只能选择一个元素, 可以直接操作。


querySelectorAll() 可以选择多个元素,得到的是伪数组,需要遍历得到每一个元素。

2.2 其他获取DOM元素方法(了解)

获取特殊元素

  1. 获取body元素:doucumnet.body // 返回body元素对象

  2. 获取html元素:document.documentElement // 返回html元素对象

3. 操作元素内容

DOM对象都是根据标签生成的,所以操作标签本质上就是操作DOM对象。

如果想要修改标签元素的里面的
内容
,则可以使用两种方式:

1. 元素innerText 属性

将文本内容添加/更新到任意标签位置。
显示纯文本,
不解析标签

<body>
  <div class="box">我是文字的内容</div>
  <script>
    // 1. 获取元素
    const box = document.querySelector('.box')
    // 2. 修改文字内容  对象.innerText 属性
    box.innerText = '我是一个盒子'  // 修改文字内容
    // box.innerText = '<strong>我是一个盒子</strong>'  // 不解析标签
  </script>
</body>

2. 元素.innerHTML 属性

将文本内容添加/更新到任意标签位置。
会解析标签
,多标签建议使用模板字符。

<body>
  <div class="box">我是文字的内容</div>
  <script>
    // 获取元素
    const box = document.querySelector('.box')
    // innerHTML 解析标签
    // box.innerHTML = '我要更换'
    box.innerHTML = '<strong>我要更换</strong>'
  </script>
</body>

如果还在纠结到底用谁,可以选择innerHTML

案例:
年会抽奖

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>年会抽奖</title>
  <style>
    .wrapper {
      width: 840px;
      height: 420px;
      background: url(./images/bg01.jpg) no-repeat center / cover;
      padding: 100px 250px;
      box-sizing: border-box;
    }
  </style>
</head>

<body>
  <div class="wrapper">
    <strong>传智教育年会抽奖</strong>
    <h1>一等奖:<span id="one">???</span></h1>
    <h3>二等奖:<span id="two">???</span></h3>
    <h5>三等奖:<span id="three">???</span></h5>
  </div>
  <script>
    // 1.声明数组
    const personArr = ['周杰伦', '刘德华', '周星驰', 'Pink老师', '张学友']
    // 2. 先做一等奖
    // 2.1 随机数 数组的下标
    const random = Math.floor(Math.random() * personArr.length)
    // console.log(personArr[random])
    // 2.2 获取one 元素 
    const one = document.querySelector('#one')
    // 2.3 把名字给 one
    one.innerHTML = personArr[random]
    // 2.4 删除数组这个名字
    personArr.splice(random, 1)
    // console.log(personArr)


    // 3. 二等奖
    // 2.1 随机数 数组的下标
    const random2 = Math.floor(Math.random() * personArr.length)
    // console.log(personArr[random])
    // 2.2 获取one 元素 
    const two = document.querySelector('#two')
    // 2.3 把名字给 one
    two.innerHTML = personArr[random2]
    // 2.4 删除数组这个名字
    personArr.splice(random2, 1)

    // 4. 三等奖
    // 2.1 随机数 数组的下标
    const random3 = Math.floor(Math.random() * personArr.length)
    // console.log(personArr[random])
    // 2.2 获取one 元素 
    const three = document.querySelector('#three')
    // 2.3 把名字给 one
    three.innerHTML = personArr[random3]
    // 2.4 删除数组这个名字
    personArr.splice(random3, 1)
  </script>
</body>

</html>

4.操作元素属性

4.1 操作元素常用属性

还可以通过 JS 设置/修改标签元素属性,比如通过 src更换 图片

最常见的属性比如: href、title、src 等

语法:
对象.属性 = 值

<body>
  <img src="./images/1.webp" alt="">
  <script>
    // 1. 获取图片元素
    const img = document.querySelector('img')
    // 2. 修改图片对象的属性   对象.属性 = 值
    img.src = './images/2.webp'
    img.title = 'pink老师的艺术照'
  </script>
</body>
4.2 操作元素样式属性

还可以通过 JS 设置/修改标签元素的样式属性。

比如通过 轮播图小圆点自动更换颜色样式;点击按钮可以滚动图片,移动的图片的位置 left 等等

1.通过 style 属性操作CSS

语法:
对象.style.样式属性 = 值

注意:

  1. 修改样式通过
    style
    属性引出

如果属性有

连接符,需要转换为
小驼峰
命名法

  1. 赋值的时候,需要的时候不要忘记加
    css单位

4.JS修改style样式操作,产生的是行内样式,CSS权重比较高

2. 操作类名(className) 操作CSS

如果修改的样式比较多,直接通过style属性修改比较繁琐,我们可以通过借助于css类名的形式。可以同时修改多个样式。

语法:
元素.className = ‘css类名’

注意:

由于 class 是关键字, 所以使用 
className 
去代替

className 是使用新值

旧值, 如果需要添加一个类,需要保留之前的类名,
直接使用 className 赋值会覆盖以前的类名。

3.
通过 classList 操作类控制CSS

为了解决className 容易覆盖以前的类名,我们可以通过classList方式追加和删除类名。

语法:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <style>
    .box {
      width: 200px;
      height: 200px;
      color: #333;
    }

    .active {
      color: red;
      background-color: pink;
    }
  </style>
</head>

<body>
  <div class="box active">文字</div>
  <script>
    // 通过classList添加
    // 1. 获取元素
    const box = document.querySelector('.box')
    // 2. 修改样式
    // 2.1 追加类 add() 类名不加点,并且是字符串
    // box.classList.add('active')
    // 2.2 删除类  remove() 类名不加点,并且是字符串
    // box.classList.remove('box')
    // 2.3 切换类  toggle()  有还是没有啊, 有就删掉,没有就加上
    box.classList.toggle('active')
  </script>
</body>

</html>
案例:轮播图随机版
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>轮播图点击切换</title>
  <style>
    * {
      box-sizing: border-box;
    }

    .slider {
      width: 560px;
      height: 400px;
      overflow: hidden;
    }

    .slider-wrapper {
      width: 100%;
      height: 320px;
    }

    .slider-wrapper img {
      width: 100%;
      height: 100%;
      display: block;
    }

    .slider-footer {
      height: 80px;
      background-color: rgb(100, 67, 68);
      padding: 12px 12px 0 12px;
      position: relative;
    }

    .slider-footer .toggle {
      position: absolute;
      right: 0;
      top: 12px;
      display: flex;
    }

    .slider-footer .toggle button {
      margin-right: 12px;
      width: 28px;
      height: 28px;
      appearance: none;
      border: none;
      background: rgba(255, 255, 255, 0.1);
      color: #fff;
      border-radius: 4px;
      cursor: pointer;
    }

    .slider-footer .toggle button:hover {
      background: rgba(255, 255, 255, 0.2);
    }

    .slider-footer p {
      margin: 0;
      color: #fff;
      font-size: 18px;
      margin-bottom: 10px;
    }

    .slider-indicator {
      margin: 0;
      padding: 0;
      list-style: none;
      display: flex;
      align-items: center;
    }

    .slider-indicator li {
      width: 8px;
      height: 8px;
      margin: 4px;
      border-radius: 50%;
      background: #fff;
      opacity: 0.4;
      cursor: pointer;
    }

    .slider-indicator li.active {
      width: 12px;
      height: 12px;
      opacity: 1;
    }
  </style>
</head>

<body>
  <div class="slider">
    <div class="slider-wrapper">
      <img src="./images/slider01.jpg" alt="" />
    </div>
    <div class="slider-footer">
      <p>对人类来说会不会太超前了?</p>
      <ul class="slider-indicator">
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ul>
      <div class="toggle">
        <button class="prev">&lt;</button>
        <button class="next">&gt;</button>
      </div>
    </div>
  </div>
  <script>
    // 1. 初始数据
    const sliderData = [
      { url: './images/slider01.jpg', title: '对人类来说会不会太超前了?', color: 'rgb(100, 67, 68)' },
      { url: './images/slider02.jpg', title: '开启剑与雪的黑暗传说!', color: 'rgb(43, 35, 26)' },
      { url: './images/slider03.jpg', title: '真正的jo厨出现了!', color: 'rgb(36, 31, 33)' },
      { url: './images/slider04.jpg', title: '李玉刚:让世界通过B站看到东方大国文化', color: 'rgb(139, 98, 66)' },
      { url: './images/slider05.jpg', title: '快来分享你的寒假日常吧~', color: 'rgb(67, 90, 92)' },
      { url: './images/slider06.jpg', title: '哔哩哔哩小年YEAH', color: 'rgb(166, 131, 143)' },
      { url: './images/slider07.jpg', title: '一站式解决你的电脑配置问题!!!', color: 'rgb(53, 29, 25)' },
      { url: './images/slider08.jpg', title: '谁不想和小猫咪贴贴呢!', color: 'rgb(99, 72, 114)' },
    ]

    // 1. 需要一个随机数 
    const random = parseInt(Math.random() * sliderData.length)
    // console.log(sliderData[random])
    // 2. 把对应的数据渲染到标签里面
    // 2.1 获取图片
    const img = document.querySelector('.slider-wrapper img')
    // 2.2. 修改图片路径  =  对象.url
    img.src = sliderData[random].url
    // 3. 把p里面的文字内容更换
    // 3.1 获取p
    const p = document.querySelector('.slider-footer p')
    // 3.2修改p
    p.innerHTML = sliderData[random].title
    // 4. 修改背景颜色
    const footer = document.querySelector('.slider-footer')
    footer.style.backgroundColor = sliderData[random].color
    // 5. 小圆点
    const li = document.querySelector(`.slider-indicator li:nth-child(${random + 1})`)
    // 让当前这个小li 添加 active这个类
    li.classList.add('active')
  </script>
</body>

</html>
4.3 操作表单元素属性

表单很多情况也需要修改属性,比如点击眼睛,可以看到密码,本质是把表单类型转换为文本框。

获取: DOM对象.属性名

设置: DOM对象.属性名 = 新值

表单属性中添加就有效果,移除就没有效果,一律使用布尔值表示。如果为true代表添加了该属性, 如果是false 代表移除了该属性。比如: disabled、checked、selected

<body>
  <button>点击</button>
  <script>
    // 1.获取
    const button = document.querySelector('button')
    // console.log(button.disabled)  // 默认false 不禁用
    button.disabled = true   // 禁用按钮
  </script>
</body>
4.4 自定义属性

标准属性:
标签天生自带的属性,比如class、id、title等, 可以直接使用点语法操作。比如: disabled、checked、 selected

自定义属性: 在html5中推出来了专门的data-自定义属性,在标签上一律
以data-开头
,在DOM对象上一律以
dataset对象
方式获取。

5.定时器-间歇函数

网页中经常会需要一种功能:每隔一段时间需要自动执行一段代码,不需要我们手动去触发。

例如:网页中的倒计时。要实现这种需求,需要定时器函数。

定时器函数可以开启和关闭定时器,可以根据时间自动重复执行某些代码。

1. 开启定时器

setInterval(函数, 间隔时间)

**作用:**每隔一段时间调用这个函数

注意:

  1. 函数名字
    不需要加括号

2. 定时器返回的是一个id数字

  1. 间隔时间单位是毫秒(1s = 1000ms)
  <script>
    // setInterval(函数, 间隔时间)
    // 1.直接写匿名函数
    // setInterval(function () {
    //   console.log('一秒执行一次')
    // }, 1000)
    // 2.调用函数,只写函数名,不要加小括号
    function fn() {
      console.log('一秒执行一次')
    }
    let n = setInterval(fn, 1000)
    // setInterval('fn()', 1000)
  </script>

2. 关闭定时器

let 变量名 = setInterval(函数, 间隔时间)
clearInterval(变量名)

一般不会刚创建就停止,而是满足一定条件再停止。

案例:
阅读注册协议

<body>
    <textarea name="" id="" cols="30" rows="10">
        用户注册协议
        欢迎注册成为京东用户!在您注册过程中,您需要完成我们的注册流程并通过点击同意的形式在线签署以下协议,请您务必仔细阅读、充分理解协议中的条款内容后再点击同意(尤其是以粗体或下划线标识的条款,因为这些条款可能会明确您应履行的义务或对您的权利有所限制)。
        【请您注意】如果您不同意以下协议全部或任何条款约定,请您停止注册。您停止注册后将仅可以浏览我们的商品信息但无法享受我们的产品或服务。如您按照注册流程提示填写信息,阅读并点击同意上述协议且完成全部注册流程后,即表示您已充分阅读、理解并接受协议的全部内容,并表明您同意我们可以依据协议内容来处理您的个人信息,并同意我们将您的订单信息共享给为完成此订单所必须的第三方合作方(详情查看
    </textarea>
    <br>
    <button class="btn" disabled>我已经阅读用户协议(5)</button>
    <script>
        // 1. 获取元素
        const btn = document.querySelector('.btn')
        // console.log(btn.innerHTML)  butto按钮特殊用innerHTML
        // 2. 倒计时
        let i = 5
        // 2.1 开启定时器
        let n = setInterval(function () {
            i--
            btn.innerHTML = `我已经阅读用户协议(${i})`
            if (i === 0) {
                clearInterval(n)  // 关闭定时器
                // 定时器停了,我就可以开按钮
                btn.disabled = false
                btn.innerHTML = '同意'
            }
        }, 1000)
    </script>
</body>

综合案例:轮播图定时器版

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>轮播图点击切换</title>
  <style>
    * {
      box-sizing: border-box;
    }

    .slider {
      width: 560px;
      height: 400px;
      overflow: hidden;
    }

    .slider-wrapper {
      width: 100%;
      height: 320px;
    }

    .slider-wrapper img {
      width: 100%;
      height: 100%;
      display: block;
    }

    .slider-footer {
      height: 80px;
      background-color: rgb(100, 67, 68);
      padding: 12px 12px 0 12px;
      position: relative;
    }

    .slider-footer .toggle {
      position: absolute;
      right: 0;
      top: 12px;
      display: flex;
    }

    .slider-footer .toggle button {
      margin-right: 12px;
      width: 28px;
      height: 28px;
      appearance: none;
      border: none;
      background: rgba(255, 255, 255, 0.1);
      color: #fff;
      border-radius: 4px;
      cursor: pointer;
    }

    .slider-footer .toggle button:hover {
      background: rgba(255, 255, 255, 0.2);
    }

    .slider-footer p {
      margin: 0;
      color: #fff;
      font-size: 18px;
      margin-bottom: 10px;
    }

    .slider-indicator {
      margin: 0;
      padding: 0;
      list-style: none;
      display: flex;
      align-items: center;
    }

    .slider-indicator li {
      width: 8px;
      height: 8px;
      margin: 4px;
      border-radius: 50%;
      background: #fff;
      opacity: 0.4;
      cursor: pointer;
    }

    .slider-indicator li.active {
      width: 12px;
      height: 12px;
      opacity: 1;
    }
  </style>
</head>

<body>
  <div class="slider">
    <div class="slider-wrapper">
      <img src="./images/slider01.jpg" alt="" />
    </div>
    <div class="slider-footer">
      <p>对人类来说会不会太超前了?</p>
      <ul class="slider-indicator">
        <li class="active"></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ul>
      <div class="toggle">
        <button class="prev">&lt;</button>
        <button class="next">&gt;</button>
      </div>
    </div>
  </div>
  <script>
    // 1. 初始数据
    const sliderData = [
      { url: './images/slider01.jpg', title: '对人类来说会不会太超前了?', color: 'rgb(100, 67, 68)' },
      { url: './images/slider02.jpg', title: '开启剑与雪的黑暗传说!', color: 'rgb(43, 35, 26)' },
      { url: './images/slider03.jpg', title: '真正的jo厨出现了!', color: 'rgb(36, 31, 33)' },
      { url: './images/slider04.jpg', title: '李玉刚:让世界通过B站看到东方大国文化', color: 'rgb(139, 98, 66)' },
      { url: './images/slider05.jpg', title: '快来分享你的寒假日常吧~', color: 'rgb(67, 90, 92)' },
      { url: './images/slider06.jpg', title: '哔哩哔哩小年YEAH', color: 'rgb(166, 131, 143)' },
      { url: './images/slider07.jpg', title: '一站式解决你的电脑配置问题!!!', color: 'rgb(53, 29, 25)' },
      { url: './images/slider08.jpg', title: '谁不想和小猫咪贴贴呢!', color: 'rgb(99, 72, 114)' },
    ]
    // 1. 获取元素 
    const img = document.querySelector('.slider-wrapper img')
    const p = document.querySelector('.slider-footer p')
    let i = 0  // 信号量 控制图片的张数
    // 2. 开启定时器
    setInterval(function () {
      i++
      // 无缝衔接位置  一共八张图片,到了最后一张就是 8, 数组的长度就是 8
      if (i >= sliderData.length) {
        i = 0
      }
      // 更换图片路径  
      img.src = sliderData[i].url
      // 把字写到 p里面
      p.innerHTML = sliderData[i].title
      // 小圆点
      // 先删除以前的active
      document.querySelector('.slider-indicator .active').classList.remove('active')
      // 只让当前li添加active
      document.querySelector(`.slider-indicator li:nth-child(${i + 1})`).classList.add('active')
    }, 1000)

  </script>
</body>

</html>

6.事件监听(绑定)

6.1 事件监听

什么是事件?

事件是在编程时系统内发生的
动作
或者发生的事情。比如用户在网页上
单击
一个按钮

什么是事件监听?

就是让程序检测是否有事件产生,一旦有事件触发,就立即调用一个函数做出响应,也称为
绑定事件或者注册事件,
比如鼠标经过显示下拉菜单,比如点击可以播放轮播图等等。

语法:

元素对象.addEventListener('事件类型', 要执行的函数)

事件监听三要素:

事件源: 事件被触发的对象
。dom元素被事件触发了,要获取dom元素

事件类型:
用什么方式触发,比如鼠标单击 click、鼠标经过 mouseover 等

事件调用的函数:
要做什么事

注意:

  1. 事件类型要
    加引号

  2. 函数是点击之后再去执行,每次点击都会执行一次

案例:随机点名

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        h2 {
            text-align: center;
        }

        .box {
            width: 600px;
            margin: 50px auto;
            display: flex;
            font-size: 25px;
            line-height: 40px;
        }

        .qs {

            width: 450px;
            height: 40px;
            color: red;

        }

        .btns {
            text-align: center;
        }

        .btns button {
            width: 120px;
            height: 35px;
            margin: 0 50px;
        }
    </style>
</head>

<body>
    <h2>随机点名</h2>
    <div class="box">
        <span>名字是:</span>
        <div class="qs">这里显示姓名</div>
    </div>
    <div class="btns">
        <button class="start">开始</button>
        <button class="end">结束</button>
    </div>

    <script>
        // 数据数组
        const arr = ['马超', '黄忠', '赵云', '关羽', '张飞']
        // 定时器的全局变量
        let timerId = 0
        // 随机号要全局变量
        let random = 0
        // 业务1.开始按钮模块
        const qs = document.querySelector('.qs')
        // 1.1 获取开始按钮对象
        const start = document.querySelector('.start')
        // 1.2 添加点击事件
        start.addEventListener('click', function () {
            timerId = setInterval(function () {
                // 随机数
                random = parseInt(Math.random() * arr.length)
                // console.log(arr[random])
                qs.innerHTML = arr[random]
            }, 35)
            // 如果数组里面只有一个值了,还需要抽取吗?  不需要  让两个按钮禁用就可以
            if (arr.length === 1) {
                // start.disabled = true
                // end.disabled = true
                start.disabled = end.disabled = true
            }
        })

        // 2. 关闭按钮模块
        const end = document.querySelector('.end')
        end.addEventListener('click', function () {
            clearInterval(timerId)
            // 结束了,可以删除掉当前抽取的那个数组元素
            arr.splice(random, 1)
            console.log(arr)
        })
    </script>
</body>

</html>
6.2 事件监听版本

事件源.on事件 = function() { }

事件源.addEventListener(事件, 事件处理函数)

区别:

on方式会被覆盖,
addEventListener
方式可绑定多次,拥有事件更多特性,推荐使用

7.事件类型

案例:轮播图完整版
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>轮播图点击切换</title>
  <style>
    * {
      box-sizing: border-box;
    }

    .slider {
      width: 560px;
      height: 400px;
      overflow: hidden;
    }

    .slider-wrapper {
      width: 100%;
      height: 320px;
    }

    .slider-wrapper img {
      width: 100%;
      height: 100%;
      display: block;
    }

    .slider-footer {
      height: 80px;
      background-color: rgb(100, 67, 68);
      padding: 12px 12px 0 12px;
      position: relative;
    }

    .slider-footer .toggle {
      position: absolute;
      right: 0;
      top: 12px;
      display: flex;
    }

    .slider-footer .toggle button {
      margin-right: 12px;
      width: 28px;
      height: 28px;
      appearance: none;
      border: none;
      background: rgba(255, 255, 255, 0.1);
      color: #fff;
      border-radius: 4px;
      cursor: pointer;
    }

    .slider-footer .toggle button:hover {
      background: rgba(255, 255, 255, 0.2);
    }

    .slider-footer p {
      margin: 0;
      color: #fff;
      font-size: 18px;
      margin-bottom: 10px;
    }

    .slider-indicator {
      margin: 0;
      padding: 0;
      list-style: none;
      display: flex;
      align-items: center;
    }

    .slider-indicator li {
      width: 8px;
      height: 8px;
      margin: 4px;
      border-radius: 50%;
      background: #fff;
      opacity: 0.4;
      cursor: pointer;
    }

    .slider-indicator li.active {
      width: 12px;
      height: 12px;
      opacity: 1;
    }
  </style>
</head>

<body>
  <div class="slider">
    <div class="slider-wrapper">
      <img src="./images/slider01.jpg" alt="" />
    </div>
    <div class="slider-footer">
      <p>对人类来说会不会太超前了?</p>
      <ul class="slider-indicator">
        <li class="active"></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
        <li></li>
      </ul>
      <div class="toggle">
        <button class="prev">&lt;</button>
        <button class="next">&gt;</button>
      </div>
    </div>
  </div>
  <script>
    // 1. 初始数据
    const data = [
      { url: './images/slider01.jpg', title: '对人类来说会不会太超前了?', color: 'rgb(100, 67, 68)' },
      { url: './images/slider02.jpg', title: '开启剑与雪的黑暗传说!', color: 'rgb(43, 35, 26)' },
      { url: './images/slider03.jpg', title: '真正的jo厨出现了!', color: 'rgb(36, 31, 33)' },
      { url: './images/slider04.jpg', title: '李玉刚:让世界通过B站看到东方大国文化', color: 'rgb(139, 98, 66)' },
      { url: './images/slider05.jpg', title: '快来分享你的寒假日常吧~', color: 'rgb(67, 90, 92)' },
      { url: './images/slider06.jpg', title: '哔哩哔哩小年YEAH', color: 'rgb(166, 131, 143)' },
      { url: './images/slider07.jpg', title: '一站式解决你的电脑配置问题!!!', color: 'rgb(53, 29, 25)' },
      { url: './images/slider08.jpg', title: '谁不想和小猫咪贴贴呢!', color: 'rgb(99, 72, 114)' },
    ]
    // 获取元素
    const img = document.querySelector('.slider-wrapper img')
    const p = document.querySelector('.slider-footer p')
    const footer = document.querySelector('.slider-footer')
    // 1. 右按钮业务
    // 1.1 获取右侧按钮 
    const next = document.querySelector('.next')
    let i = 0  // 信号量 控制播放图片张数
    // 1.2 注册点击事件
    next.addEventListener('click', function () {
      i++
      i = i >= data.length ? 0 : i
      // 调用函数
      toggle()
    })

    // 2. 左侧按钮业务
    // 2.1 获取左侧按钮 
    const prev = document.querySelector('.prev')
    // 2.2 注册点击事件
    prev.addEventListener('click', function () {
      i--
      i = i < 0 ? data.length - 1 : i
      // 调用函数
      toggle()
    })

    // 声明一个渲染的函数作为复用
    function toggle() {
      // 1.4 渲染对应的数据
      img.src = data[i].url
      p.innerHTML = data[i].title
      footer.style.backgroundColor = data[i].color
      // 1.5 更换小圆点    先移除原来的类名, 当前li再添加 这个 类名
      document.querySelector('.slider-indicator .active').classList.remove('active')
      document.querySelector(`.slider-indicator li:nth-child(${i + 1})`).classList.add('active')
    }

    // 3. 自动播放模块
    let timerId = setInterval(function () {
      // 利用js自动调用点击事件  click()  一定加小括号调用函数
      next.click()
    }, 1000)

    // 4. 鼠标经过大盒子,停止定时器
    const slider = document.querySelector('.slider')
    // 注册事件
    slider.addEventListener('mouseenter', function () {
      // 停止定时器
      clearInterval(timerId)
    })

    // 5. 鼠标离开大盒子,开启定时器
    // 注册事件
    slider.addEventListener('mouseleave', function () {
      // 停止定时器
      if (timerId) clearInterval(timerId)
      // 开启定时器
      timerId = setInterval(function () {
        // 利用js自动调用点击事件  click()  一定加小括号调用函数
        next.click()
      }, 1000)
    })
  </script>
</body>

</html>

8.事件对象

8.1 获取事件对象

事件对象是什么?

事件对象也是个对象,这个对象里有事件触发时的相关信息
。例如:鼠标点击事件中,事件对象就存了鼠标点在哪个位置等信息。

使用场景:

可以判断用户按下哪个键,比如按下回车键可以发布新闻

可以判断鼠标点击了哪个元素,从而做相应的操作

语法:

在事件绑定的回调函数的第一个参数就是事件对象
,一般命名为event、ev、e

8.2 事件对象常用属性

部分常用属性:

type:获取当前的事件类型

clientX/clientY:获取光标相对于浏览器可见窗口左上角的位置

offsetX/offsetY:获取光标相对于当前DOM元素左上角的位置

key:用户按下的键盘键的值,现在不提倡使用keyCode

拓展:字符串.trim()  可去除字符串左右的空格

案例:
评论回车发布

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>评论回车发布</title>
  <style>
    .wrapper {
      min-width: 400px;
      max-width: 800px;
      display: flex;
      justify-content: flex-end;
    }

    .avatar {
      width: 48px;
      height: 48px;
      border-radius: 50%;
      overflow: hidden;
      background: url(./images/avatar.jpg) no-repeat center / cover;
      margin-right: 20px;
    }

    .wrapper textarea {
      outline: none;
      border-color: transparent;
      resize: none;
      background: #f5f5f5;
      border-radius: 4px;
      flex: 1;
      padding: 10px;
      transition: all 0.5s;
      height: 30px;
    }

    .wrapper textarea:focus {
      border-color: #e4e4e4;
      background: #fff;
      height: 50px;
    }

    .wrapper button {
      background: #00aeec;
      color: #fff;
      border: none;
      border-radius: 4px;
      margin-left: 10px;
      width: 70px;
      cursor: pointer;
    }

    .wrapper .total {
      margin-right: 80px;
      color: #999;
      margin-top: 5px;
      opacity: 0;
      transition: all 0.5s;
    }

    .list {
      min-width: 400px;
      max-width: 800px;
      display: flex;
    }

    .list .item {
      width: 100%;
      display: flex;
    }

    .list .item .info {
      flex: 1;
      border-bottom: 1px dashed #e4e4e4;
      padding-bottom: 10px;
    }

    .list .item p {
      margin: 0;
    }

    .list .item .name {
      color: #FB7299;
      font-size: 14px;
      font-weight: bold;
    }

    .list .item .text {
      color: #333;
      padding: 10px 0;
    }

    .list .item .time {
      color: #999;
      font-size: 12px;
    }
  </style>
</head>

<body>
  <div class="wrapper">
    <i class="avatar"></i>
    <textarea id="tx" placeholder="发一条友善的评论" rows="2" maxlength="200"></textarea>
    <button>发布</button>
  </div>
  <div class="wrapper">
    <span class="total">0/200字</span>
  </div>
  <div class="list">
    <div class="item" style="display: none;">
      <i class="avatar"></i>
      <div class="info">
        <p class="name">zhuie</p>
        <p class="text">大家都辛苦啦,感谢各位大大的努力,能圆满完成真是太好了[笑哭][支持]</p>
        <p class="time">2023-05-17 20:33:21</p>
      </div>
    </div>
  </div>
  <script>
    const tx = document.querySelector('#tx')
    const total = document.querySelector('.total')
    const item = document.querySelector('.item')
    const text = document.querySelector('.text')
    // 1. 当我们文本域获得了焦点,就让 total 显示出来
    tx.addEventListener('focus', function () {
      total.style.opacity = 1
    })
    // 2. 当我们文本域失去了焦点,就让 total 隐藏出来
    tx.addEventListener('blur', function () {
      total.style.opacity = 0
    })
    // 3. 检测用户输入
    tx.addEventListener('input', function () {
      // console.log(tx.value.length)  得到输入的长度
      total.innerHTML = `${tx.value.length}/200字`
    })

    // 4. 按下回车发布评论
    tx.addEventListener('keyup', function (e) {
      // 只有按下的是回车键,才会触发
      if (e.key === 'Enter') {
        // 如果用户输入的不为空就显示和打印
        if (tx.value.trim()) {
          item.style.display = 'block'
          // console.log(tx.value)  // 用户输入的内容
          text.innerHTML = tx.value
        }
        // 等我们按下回车,结束,清空文本域
        tx.value = ''
        // 按下回车之后,就要把 字符统计 复原
        total.innerHTML = '0/200字'
      }

    })
  </script>
</body>

</html>

9.环境对象

环境对象:
指的是函数内部特殊的
变量 this

它代表着当前函数运行时所处的环境

作用:
弄清楚this的指向,可以让我们代码更简洁

函数的调用方式不同,this 指代的对象也不同

【谁调用, this 就是谁】
是判断 this 指向的粗略规则

直接调用函数,其实相当于是 window.函数,所以 this 指代 window

10.回调函数

如果将函数 A 做为参数传递给函数 B 时,我们称函数 A 为
回调函数

简单理解: 当一个函数当做参数来传递给另外一个函数的时候,这个函数就是
回调函数

常见的使用场景:

回调函数本质还是函数,只不过把它当成参数使用。使用匿名函数做为回调函数比较常见。

综合案例:Tab栏切换

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>tab栏切换</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    .tab {
      width: 590px;
      height: 340px;
      margin: 20px;
      border: 1px solid #e4e4e4;
    }

    .tab-nav {
      width: 100%;
      height: 60px;
      line-height: 60px;
      display: flex;
      justify-content: space-between;
    }

    .tab-nav h3 {
      font-size: 24px;
      font-weight: normal;
      margin-left: 20px;
    }

    .tab-nav ul {
      list-style: none;
      display: flex;
      justify-content: flex-end;
    }

    .tab-nav ul li {
      margin: 0 20px;
      font-size: 14px;
    }

    .tab-nav ul li a {
      text-decoration: none;
      border-bottom: 2px solid transparent;
      color: #333;
    }

    .tab-nav ul li a.active {
      border-color: #e1251b;
      color: #e1251b;
    }

    .tab-content {
      padding: 0 16px;
    }

    .tab-content .item {
      display: none;
    }

    .tab-content .item.active {
      display: block;
    }
  </style>
</head>

<body>
  <div class="tab">
    <div class="tab-nav">
      <h3>每日特价</h3>
      <ul>
        <li><a class="active" href="javascript:;">精选</a></li>
        <li><a href="javascript:;">美食</a></li>
        <li><a href="javascript:;">百货</a></li>
        <li><a href="javascript:;">个护</a></li>
        <li><a href="javascript:;">预告</a></li>
      </ul>
    </div>
    <div class="tab-content">
      <div class="item active"><img src="./images/tab00.png" alt="" /></div>
      <div class="item"><img src="./images/tab01.png" alt="" /></div>
      <div class="item"><img src="./images/tab02.png" alt="" /></div>
      <div class="item"><img src="./images/tab03.png" alt="" /></div>
      <div class="item"><img src="./images/tab04.png" alt="" /></div>
    </div>
  </div>
  <script>
    // 1. a 模块制作 要给 5个链接绑定鼠标经过事件
    // 1.1 获取 a 元素 
    const as = document.querySelectorAll('.tab-nav a')
    for (let i = 0; i < as.length; i++) {
      // 要给 5个链接绑定鼠标经过事件
      as[i].addEventListener('mouseenter', function () {  // 鼠标经过
        // 排他思想  
        // 干掉别人 移除类active
        document.querySelector('.tab-nav .active').classList.remove('active')
        // 我登基 我添加类 active  this 当前的那个 a 
        this.classList.add('active')

        // 下面5个大盒子 一一对应  .item 
        // 干掉别人
        document.querySelector('.tab-content .active').classList.remove('active')
        // 对应序号的那个 item 显示 添加 active 类
        document.querySelector(`.tab-content .item:nth-child(${i + 1})`).classList.add('active')

      })
    }
  </script>
</body>

</html>

拓展:css伪类选择器checked(选择被勾选的复选框)

重点案例:全选文本框
<!DOCTYPE html>

<html>

<head lang="en">
  <meta charset="UTF-8">
  <title></title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    table {
      border-collapse: collapse;
      border-spacing: 0;
      border: 1px solid #c0c0c0;
      width: 500px;
      margin: 100px auto;
      text-align: center;
    }

    th {
      background-color: #09c;
      font: bold 16px "微软雅黑";
      color: #fff;
      height: 24px;
    }

    td {
      border: 1px solid #d0d0d0;
      color: #404060;
      padding: 10px;
    }

    .allCheck {
      width: 80px;
    }
  </style>
</head>

<body>
  <table>
    <tr>
      <th class="allCheck">
        <input type="checkbox" name="" id="checkAll"> <span class="all">全选</span>
      </th>
      <th>商品</th>
      <th>商家</th>
      <th>价格</th>
    </tr>
    <tr>
      <td>
        <input type="checkbox" name="check" class="ck">
      </td>
      <td>小米手机</td>
      <td>小米</td>
      <td>¥1999</td>
    </tr>
    <tr>
      <td>
        <input type="checkbox" name="check" class="ck">
      </td>
      <td>小米净水器</td>
      <td>小米</td>
      <td>¥4999</td>
    </tr>
    <tr>
      <td>
        <input type="checkbox" name="check" class="ck">
      </td>
      <td>小米电视</td>
      <td>小米</td>
      <td>¥5999</td>
    </tr>
  </table>
  <script>
    // 1. 获取大复选框
    const checkAll = document.querySelector('#checkAll')
    // 2. 获取所有的小复选框
    const cks = document.querySelectorAll('.ck')
    // 3. 点击大复选框  注册事件
    checkAll.addEventListener('click', function () {
      // 得到当前大复选框的选中状态
      // console.log(checkAll.checked)  // 得到 是 true 或者是 false
      // 4. 遍历所有的小复选框 让小复选框的checked  =  大复选框的 checked
      for (let i = 0; i < cks.length; i++) {
        cks[i].checked = this.checked
      }
    })
    // 5. 小复选框控制大复选框
    for (let i = 0; i < cks.length; i++) {
      // 5.1 给所有的小复选框添加点击事件
      cks[i].addEventListener('click', function () {
        // 判断选中的小复选框个数 是不是等于  总的小复选框个数
        // console.log(document.querySelectorAll('.ck:checked').length)
        // console.log(document.querySelectorAll('.ck:checked').length === cks.length)
        checkAll.checked = document.querySelectorAll('.ck:checked').length === cks.length
      })
    }
  </script>
</body>

</html>

11.事件流

11.1 事件流和两个阶段说明

事件流
指的是事件完整执行过程中的流动路径。

说明:假设页面里有个div,当触发事件时,会经历两个阶段,分别是捕获阶段、冒泡阶段

简单来说:捕获阶段是从父到子,冒泡阶段是从子到父

实际工作都是使用事件冒泡为主。

11.2 事件捕获

事件捕获概念: 从DOM的根元素开始去执行对应的事件 (从外到里)

代码:
DOM.addEventListener(事件类型, 事件处理函数, 是否使用捕获机制)

说明:

addEventListener第三个参数传入
true
代表是
捕获阶段
触发(很少使用)

若传入
false
代表
冒泡阶段
触发,默认就是false

若是用 L0 事件监听,则只有冒泡阶段,没有捕获

11.3 事件冒泡

事件冒泡概念: 当一个元素的事件被触发时,同样的事件将会在该元素的所有祖先元素中依次被触发。这一过程被称为事件冒泡。

**简单理解:**当一个元素触发事件后,会依次向上调用所有父级元素的
同名事件
。比如click事件

事件冒泡是默认存在的。L2事件监听第三个参数是 false,或者默认都是冒泡。

11.4 阻止冒泡

问题:
因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素

需求:
若想把事件就限制在当前元素内,就需要阻止事件冒泡

前提:
阻止事件冒泡需要拿到事件对象

语法:
事件对象.stopPropagation()

注意:
此方法可以阻断事件流动传播,不光在冒泡阶段有效,捕获阶段也有效

<body>
  <div class="father">
    <div class="son"></div>
  </div>
  <script>
    const fa = document.querySelector('.father')
    const son = document.querySelector('.son')
    document.addEventListener('click', function () {
      alert('我是爷爷')
    })
    fa.addEventListener('click', function () {
      alert('我是爸爸')
    })
    son.addEventListener('click', function (e) {
      alert('我是儿子')
      // 阻止流动传播  事件对象.stopPropagation()
      e.stopPropagation()
    })

  </script>
</body>

我们某些情况下需要
阻止
默认行为
的发生,比如 阻止
链接的跳转,表单域跳转

语法:
e.preventDefault()

<body>
  <form action="http://www.itcast.cn">
    <input type="submit" value="免费注册">
  </form>
  <script>
    const form = document.querySelector('form')
    form.addEventListener('submit', function (e) {
      // 阻止默认行为  提交
      e.preventDefault()
    })
  </script>
</body>
11.5 解绑事件

on事件方式,直接使用null覆盖偶就可以实现事件的解绑

语法:

<body>
  <button>点击</button>
  <script>
    const btn = document.querySelector('button')
    btn.onclick = function () {
      alert('点击了')
      // L0 事件移除解绑
      btn.onclick = null
    }
  </script>
</body>

addEventListener方式必须使用:

removeEventListener(事件类型, 事件处理函数, [获取捕获或者冒泡阶段])

<body>
  <button>点击</button>
  <script>
    const btn = document.querySelector('button')
    function fn() {
      alert('点击了')
    }
    btn.addEventListener('click', fn)
    // L2 事件移除解绑
    btn.removeEventListener('click', fn)
  </script>
</body>

注意:匿名函数无法被解绑

拓展:鼠标经过事件的区别、两种注册事件的区别

鼠标经过事件:

mouseover 和 mouseout 会有冒泡效果

mouseenter 和 mouseleave 没有冒泡效果 (推荐)

两种注册事件:

传统on注册(L0)

同一个对象,后面注册的事件会覆盖前面注册(同一个事件)

直接使用null覆盖偶就可以实现事件的解绑

都是冒泡阶段执行的

事件监听注册(L2)

语法: addEventListener(事件类型, 事件处理函数, 是否使用捕获)

后面注册的事件不会覆盖前面注册的事件(同一个事件)

可以通过第三个参数去确定是在冒泡或者捕获阶段执行

必须使用removeEventListener(事件类型, 事件处理函数, 获取捕获或者冒泡阶段)

匿名函数无法被解绑

12.事件委托

事件委托是利用事件流的特征解决一些开发需求的知识技巧。

**优点:**减少注册次数,可以提高程序性能。

**原理:**事件委托其实是利用
事件冒泡
的特点。

父元素注册事件
,当我们触发子元素的时候,会冒泡到父元素身上,从而触发父元素的事件。

实现:
事件对象.target. tagName 可以获得真正触发事件的元素
u

如:ul.addEventListener(‘click’ , function(){})
执行父级点击事件

案例:tab栏切换改造

思路:

①:给 a的父级 注册点击事件,采取事件委托方式

②:如果点击的是A , 则进行排他思想,删除添加类,注意判断的方式 利用 e.target.tagName

③:因为没有索引号了,所以这里我们可以自定义属性data-id,给5个链接添加序号

④:下面大盒子获取索引号的方式 e.target.dataset.id 号, 然后进行排他思想

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>tab栏切换</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }

    .tab {
      width: 590px;
      height: 340px;
      margin: 20px;
      border: 1px solid #e4e4e4;
    }

    .tab-nav {
      width: 100%;
      height: 60px;
      line-height: 60px;
      display: flex;
      justify-content: space-between;
    }

    .tab-nav h3 {
      font-size: 24px;
      font-weight: normal;
      margin-left: 20px;
    }

    .tab-nav ul {
      list-style: none;
      display: flex;
      justify-content: flex-end;
    }

    .tab-nav ul li {
      margin: 0 20px;
      font-size: 14px;
    }

    .tab-nav ul li a {
      text-decoration: none;
      border-bottom: 2px solid transparent;
      color: #333;
    }

    .tab-nav ul li a.active {
      border-color: #e1251b;
      color: #e1251b;
    }

    .tab-content {
      padding: 0 16px;
    }

    .tab-content .item {
      display: none;
    }

    .tab-content .item.active {
      display: block;
    }
  </style>
</head>

<body>
  <div class="tab">
    <div class="tab-nav">
      <h3>每日特价</h3>
      <ul>
        <li><a class="active" href="javascript:;" data-id="0">精选</a></li>
        <li><a href="javascript:;" data-id="1">美食</a></li>
        <li><a href="javascript:;" data-id="2">百货</a></li>
        <li><a href="javascript:;" data-id="3">个护</a></li>
        <li><a href="javascript:;" data-id="4">预告</a></li>
      </ul>
    </div>
    <div class="tab-content">
      <div class="item active"><img src="./images/tab00.png" alt="" /></div>
      <div class="item"><img src="./images/tab01.png" alt="" /></div>
      <div class="item"><img src="./images/tab02.png" alt="" /></div>
      <div class="item"><img src="./images/tab03.png" alt="" /></div>
      <div class="item"><img src="./images/tab04.png" alt="" /></div>
    </div>
  </div>
  <script>
    // 采取事件委托的形式 tab栏切换
    // 1. 获取 ul 父元素 因为 ul只有一个
    const ul = document.querySelector('.tab-nav ul')
    // 获取 5个 item 
    const items = document.querySelectorAll('.tab-content .item')
    // 2. 添加事件
    ul.addEventListener('click', function (e) {
      // console.log(e.target)  // e.target是我们点击的对象
      // 我们只有点击了 a 才会 进行 添加类和删除类操作 
      // console.log(e.target.tagName)  // e.target.tagName 点击那个对象的 标签名
      if (e.target.tagName === 'A') {
        // console.log('我选的是a')
        // 排他思想 ,先移除原来的active  
        document.querySelector('.tab-nav .active').classList.remove('active')
        //当前元素添加 active  是 e.target
        // this 指向ul 不能用this 
        e.target.classList.add('active')

        // 下面大盒子模块
        // console.log(e.target.dataset.id)
        // 获取的是字符串,需转换为数字
        const i = +e.target.dataset.id
        // 排他思想 ,先移除原来的active 
        document.querySelector('.tab-content .active').classList.remove('active')
        // 对应的大盒子 添加 active 
        // document.querySelector(`.tab-content .item:nth-child(${i + 1})`).classList.add('active')
        items[i].classList.add('active')
      }
    })
  </script>
</body>

</html>

13.其他事件

13.1 页面加载事件

1.外部资源(如图片、外联CSS和JavaScript等)加载完毕时触发的事件

**原因:**有些时候需要等页面资源全部处理完了做一些事情,老代码喜欢把 script 写在 head 中,这时候直接找 dom 元素找不到。

事件名:
load

监听页面所有资源加载完毕:给 window 添加 load 事件

注意:不光可以监听整个页面资源加载完毕,也可以针对某个资源绑定load事件

2.当初始的HTML文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像等完全加载

事件名:
DOMContentLoaded

监听页面DOM加载完毕:给 document 添加 DOMContentLoaded 事件

13.2 页面滚动事件

滚动条在滚动的时候持续触发的事件。很多网页需要检测用户把页面滚动到某个区域后做一些处理, 比如固定导航栏,比如返回顶部。

事件名:scroll

监听整个页面滚动:

一般给 window 或 document 添加 scroll 事件,监听某个元素的内部滚动直接给某个元素加即可。

13.2 页面滚动事件-获取位置

scrollLeft

scrollTop
(属性):

获取被卷去的大小,获取元素内容往左、往上滚出去看不到的距离,这两个值是可
读写
的。

尽量在scroll事件里面获取被卷去的距离。

开发中,我们经常检测页面滚动的距离,比如页面滚动100像素,就可以显示一个元素,或者固定一个元素。

<script>
    const div = document.querySelector('div')
    // 页面滚动事件
    window.addEventListener('scroll', function () {
      // 获取html元素写法  
      // document.documentElement  
      const n = document.documentElement.scrollTop
      if (n >= 100) {
        div.style.display = 'block'
      } else {
        div.style.display = 'none'
      }
    })
  </script>

注意:document.documentElement    HTML文档返回对象为HTML元素

13.2 页面滚动事件-滚动到指定的坐标

scrollTo() 方法可把内容滚动到指定的坐标

**语法:**元素.scrollTo(x, y)

案例:返回顶部

    <script>
        // 点击返回页面顶部
        const backTop = document.querySelector('#backTop')
        backTop.addEventListener('click', function () {
            // 可读写
            // document.documentElement.scrollTop = 0
            window.scrollTo(0, 0)
        })
    </script>
13.3 页面尺寸事件

会在窗口尺寸改变的时候触发事件:resize

13.3 页面尺寸事件-获取元素宽高

获取元素的可见部分宽高(不包含边框,margin,滚动条等): 
clientWidth和clientHeight

案例:Rem基准值

13.4 元素尺寸与位置

就是通过js的方式,得到
元素在页面中的位置


获取宽高:
offsetWidth和offsetHeight
(内容 + padding + border)

获取元素的自身宽高、包含元素自身设置的宽高、padding、border。
获取的是数值,方便计算。

注意: 获取的是可视宽高, 如果盒子是隐藏的,获取的结果是0


获取位置:

1.offsetLeft和offsetTop

获取元素距离自己
定位父级元素
的左、上距离,
如果都没有则以
文档左上角
为准。

注意:offsetLeft和offsetTop 是只读属性

案例:仿京东固定导航栏案例

<body>
    <div class="header">我是顶部导航栏</div>
    <div class="content">
        <div class="sk">秒杀模块</div>
    </div>
    <div class="backtop">
        <img src="./images/close2.png" alt="">
        <a href="javascript:;"></a>
    </div>
    <script>
        const sk = document.querySelector('.sk')
        const header = document.querySelector('.header')
        // 页面滚动事件
        window.addEventListener('scroll', function () {
            // 当页面滚动到 秒杀模块的时候,就改变 头部的 top值
            // 页面被卷去的头部 >=  秒杀模块的位置 offsetTop
            const n = document.documentElement.scrollTop
            header.style.top = n >= sk.offsetTop ? 0 : '-80px'
        })
    </script>
</body>

案例:实现bilibili 点击小滑块移动效果

  <script>
    // 1. 事件委托的方法 获取父元素 tabs-list
    const list = document.querySelector('.tabs-list')
    const line = document.querySelector('.line')
    // 2. 注册点击事件
    list.addEventListener('click', function (e) {
      // 只有点击了A 才有触发效果
      if (e.target.tagName === 'A') {
        // 得到当前点击元素的位置
        // console.log(e.target.offsetLeft)
        // 把我们点击的a链接盒子的位置  然后移动
        line.style.transform = `translateX(${e.target.offsetLeft}px)`
      }
    })
  </script>

element.getBoundingClientRect()

方法返回元素的大小及其相对于视口的位置

总结
综合案例:电梯导航
  <script>
    // 第一大模块,页面滑动可以显示和隐藏
    (function () {
      // 获取元素
      const entry = document.querySelector('.xtx_entry')
      const elevator = document.querySelector('.xtx-elevator')
      // 1. 当页面滚动大于 300像素,就显示 电梯导航
      // 2. 给页面添加滚动事件
      window.addEventListener('scroll', function () {
        // 被卷去的头部大于 300 
        const n = document.documentElement.scrollTop
        elevator.style.opacity = n >= entry.offsetTop ? 1 : 0
      })

      // 点击返回页面顶部
      const backTop = document.querySelector('#backTop')
      backTop.addEventListener('click', function () {
        // 可读写
        // document.documentElement.scrollTop = 0
        window.scrollTo(0, 0)
      })
    })();

    // 第二第三都放到另外一个执行函数里面
    (function () {
      // 2. 点击页面可以滑动 
      const list = document.querySelector('.xtx-elevator-list')
      list.addEventListener('click', function (e) {
        // console.log(11)
        if (e.target.tagName === 'A' && e.target.dataset.name) {
          // 排他思想  
          // 先移除原来的类active 
          // 先获取这个active的对象
          const old = document.querySelector('.xtx-elevator-list .active')
          // console.log(old)
          // 判断 如果原来有active类的对象,就移除类,如果开始就没有对象,就不删除,所以不报错
          if (old) old.classList.remove('active')
          // 当前元素添加 active 
          e.target.classList.add('active')
          // 获得自定义属性  new   topic 
          // console.log(e.target.dataset.name)
          // 根据小盒子的自定义属性值 去选择 对应的大盒子
          // console.log(document.querySelector(`.xtx_goods_${e.target.dataset.name}`).offsetTop)
          // 获得对应大盒子的 offsetTop
          const top = document.querySelector(`.xtx_goods_${e.target.dataset.name}`).offsetTop
          // 让页面滚动到对应的位置
          document.documentElement.scrollTop = top

        }
      })

      // 3. 页面滚动,可以根据大盒子选 小盒子 添加 active 类
      window.addEventListener('scroll', function () {
        //  3.1  先移除类 
        // 先获取这个active的对象
        const old = document.querySelector('.xtx-elevator-list .active')
        // console.log(old)
        // 判断 如果原来有active类的对象,就移除类,如果开始就没有对象,就不删除,所以不报错
        if (old) old.classList.remove('active')
        // 3.2 判断页面当前滑动的位置,选择小盒子

        // 获取4个大盒子
        const news = document.querySelector('.xtx_goods_new')
        const popular = document.querySelector('.xtx_goods_popular')
        const brand = document.querySelector('.xtx_goods_brand')
        const topic = document.querySelector('.xtx_goods_topic')
        const n = document.documentElement.scrollTop
        if (n >= news.offsetTop && n < popular.offsetTop) {
          // 选择第一个小盒子
          document.querySelector('[data-name=new]').classList.add('active')
        } else if (n >= popular.offsetTop && n < brand.offsetTop) {
          document.querySelector('[data-name=popular]').classList.add('active')
        } else if (n >= brand.offsetTop && n < topic.offsetTop) {
          document.querySelector('[data-name=brand]').classList.add('active')
        } else if (n >= topic.offsetTop) {
          document.querySelector('[data-name=topic]').classList.add('active')
        }
      })
    })();
  </script>

14. 日期对象

**日期对象:**用来表示时间的对象

**作用:**可以得到当前系统时间

14.1 实例化

在代码中发现了 new 关键字时,一般将这个操作称为
实例化

创建一个时间对象并获取时间

获得当前时间:

const date = new Date()

获取指定时间:

const date = new Date('2023-5-25')
14.2 日期对象方法

**使用场景:**因为日期对象返回的数据我们不能直接使用,所以需要转换为实际开发中常用的格式

案例:页面显示时间

<body>
  <div></div>
  <script>
    const div = document.querySelector('div')
    function getMyDate() {
      const date = new Date()
      let h = date.getHours()
      let m = date.getMinutes()
      let s = date.getSeconds()
      h = h < 10 ? '0' + h : h
      m = m < 10 ? '0' + m : m
      s = s < 10 ? '0' + s : s
      return `今天是: ${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}号 ${h}:${m}:${s}`
    }

    div.innerHTML = getMyDate()
    setInterval(function () {
      div.innerHTML = getMyDate()
    }, 1000)
  </script>
</body>
14.3 时间戳

● 使用场景: 如果计算倒计时效果,前面方法无法直接计算,需要借助于时间戳完成

**● 时间戳:**是指1970年01月01日00时00分00秒起至现在的
毫秒数
,是一种特殊的计量时间的方式

● 算法:

将来的时间戳 - 现在的时间戳 = 剩余时间毫秒数

剩余时间毫秒数 转换为 剩余时间的 年月日时分秒 就是 倒计时时间

比如:将来时间戳 2000ms - 现在时间戳 1000ms = 1000ms,1000ms 转换为就是 0小时0分1秒

●****三种方式获取时间戳:

使用 getTime() 方法

const date = new Date()
console.log(date.getTime())

2.简写 +new Date()

重点记住 +new Date() 因为可以返回当前时间戳或者指定的时间戳

console.log(+new Date())
  1. 使用 Date.now()
console.log(Date.now())

无需实例化,
但是只能得到当前的时间戳, 而前面两种可以返回指定时间的时间戳

案例:倒计时效果

  <script>
    // 函数封装 getCountTime
    function getCountTime() {
      // 1. 得到当前的时间戳
      const now = +new Date()
      // 2. 得到将来的时间戳
      const last = +new Date('2022-4-1 18:30:00')
      // console.log(now, last)
      // 3. 得到剩余的时间戳 count  记得转换为 秒数
      const count = (last - now) / 1000
      // console.log(count)
      // 4. 转换为时分秒
      // h = parseInt(总秒数 / 60 / 60 % 24)      // 计算小时
      // m = parseInt(总秒数 / 60 % 60)           // 计算分数
      // s = parseInt(总秒数 % 60)                // 计算当前秒数
      // let d = parseInt(count / 60 / 60 / 24)  // 计算当前天数
      let h = parseInt(count / 60 / 60 % 24)
      h = h < 10 ? '0' + h : h
      let m = parseInt(count / 60 % 60)
      m = m < 10 ? '0' + m : m
      let s = parseInt(count % 60)
      s = s < 10 ? '0' + s : s
      console.log(h, m, s)

      // 把时分秒写到对应的盒子里面
      document.querySelector('#hour').innerHTML = h
      document.querySelector('#minutes').innerHTML = m
      document.querySelector('#scond').innerHTML = s
    }
    // 先调用一次
    getCountTime()

    // 开启定时器
    setInterval(getCountTime, 1000)
  </script>

15.节点操作

15.1 DOM节点

**DOM节点:**DOM树里每一个内容都称之为节点

节点类型:


元素节点:
所有的标签 比如 body、 div,html 是根节点

属性节点:所有的属性 比如 href

文本节点:所有的文本

其他

15.2 查找节点

节点关系:针对的找亲戚返回的都是对象   父节点、子节点、兄弟节点

父节点查找:
parentNode
属性,返回最近一级的父节点 找不到返回为 null

例如:子元素.parentNode

子节点查找:
childNodes
属性,获得所有子节点、包括文本节点(空格、换行)、注释节点等

children 属性(重点)
仅获得所有元素节点,返回的还是一个
伪数组,
例如:父元素.children

兄弟关系查找:

  1. 下一个兄弟节点:
    nextElementSibling
    属性

  2. 上一个兄弟节点:
    previousElementSibling
    属性

15.3 增加节点

**1.创建节点:**创造出一个新的网页元素,再添加到网页内,一般先创建节点,然后插入节点

创建元素节点方法:

document.createElement('标签名')

**2.追加节点:**要想在界面看到,还得插入到某个父元素中

(1)
插入到父元素的最后一个子元素:
父元素.appendChild(要插入的元素)
(2)插入到父元素中某个子元素的前面:父元素.insertBefore(要插入的元素,在哪个元素前面)

**3.克隆节点:**元素.cloneNode(布尔值)

特殊情况下我们新增节点按如下操作: 复制一个原有的节点,把复制的节点放到指定的元素内部

cloneNode会克隆出一个跟原标签一样的元素,括号内传入布尔值。

若为true,则克隆时会包含后代节点一起克隆。若为false(默认),则克隆时不包含后代节点。

15.4 删除节点

在 JavaScript 原生 DOM 操作中,要删除元素必须通过
父元素删除。

语法:
父元素.removeChlid(子元素),
如不存在父子关系则删除不成功

删除节点和隐藏节点(display:none) 是有区别的:

隐藏节点还是存在的,但是删除,则从html中删除节点

16. M端事件

移动端也有自己独特的地方。比如
触屏事件 touch
(也称触摸事件),Android 和 IOS 都有。

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

常见的触屏事件如下:

17.插件

插件: 就是别人写好的一些代码,我们只需要复制对应的代码,就可以直接实现对应的效果

学习插件的基本过程

熟悉官网,了解这个插件可以完成什么需求   https://www.swiper.com.cn/

看在线演示,找到符合自己需求的demo    https://www.swiper.com.cn/demo/index.html

查看基本使用流程    https://www.swiper.com.cn/usage/index.html

查看APi文档,去配置自己的插件    https://www.swiper.com.cn/api/index.html

注意:多个swiper同时使用的时候, 类名需要注意区分

综合案例:学生信息表

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>学生信息管理</title>
    <link rel="stylesheet" href="css/index.css" />
</head>

<body>
    <h1>新增学员</h1>
    <form class="info" autocomplete="off">
        姓名:<input type="text" class="uname" name="uname" />
        年龄:<input type="text" class="age" name="age" />
        性别:
        <select name="gender" class="gender">
            <option value="男">男</option>
            <option value="女">女</option>
        </select>
        薪资:<input type="text" class="salary" name="salary" />
        就业城市:<select name="city" class="city">
            <option value="北京">北京</option>
            <option value="上海">上海</option>
            <option value="广州">广州</option>
            <option value="深圳">深圳</option>
            <option value="曹县">曹县</option>
        </select>
        <button class="add">录入</button>
    </form>

    <h1>就业榜</h1>
    <table>
        <thead>
            <tr>
                <th>学号</th>
                <th>姓名</th>
                <th>年龄</th>
                <th>性别</th>
                <th>薪资</th>
                <th>就业城市</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            <!-- 
        <tr>
          <td>1001</td>
          <td>欧阳霸天</td>
          <td>19</td>
          <td>男</td>
          <td>15000</td>
          <td>上海</td>
          <td>
            <a href="javascript:">删除</a>
          </td>
        </tr> 
        -->
        </tbody>
    </table>
    <script>
        // 获取元素
        const uname = document.querySelector('.uname')
        const age = document.querySelector('.age')
        const gender = document.querySelector('.gender')
        const salary = document.querySelector('.salary')
        const city = document.querySelector('.city')
        const tbody = document.querySelector('tbody')
        // 获取所有带有name属性的元素
        const items = document.querySelectorAll('[name]')
        // 声明一个空的数组, 增加和删除都是对这个数组进行操作
        const arr = []
        // 1. 录入模块
        // 1.1 表单提交事件
        const info = document.querySelector('.info')
        info.addEventListener('submit', function (e) {
            // 阻止默认行为 不跳转
            e.preventDefault()

            // 这里进行表单验证  如果不通过,直接中断,不需要添加数据
            // 先遍历循环
            for (let i = 0; i < items.length; i++) {
                if (items[i].value === '') {
                    return alert('输入内容不能为空')
                }
            }

            // 创建新的对象
            const obj = {
                stuId: arr.length + 1,
                uname: uname.value,
                age: age.value,
                gender: gender.value,
                salary: salary.value,
                city: city.value
            }
            // console.log(obj)
            // 追加给数组里面
            arr.push(obj)
            // 清空表单   reset重置
            this.reset()
            // 调用渲染函数
            render()
        })

        // 2.渲染函数 因为增加和删除都需要渲染
        function render() {
            // 先清空tbody以前的行,把最新数组里面的数据渲染完毕
            tbody.innerHTML = ''
            // 遍历arr数组
            for (let i = 0; i < arr.length; i++) {
                // 生成tr
                const tr = document.createElement('tr')
                tr.innerHTML = `
                    <td>${arr[i].stuId}</td>
                    <td>${arr[i].uname}</td>
                    <td>${arr[i].age}</td>
                    <td>${arr[i].gender}</td>
                    <td>${arr[i].salary}</td>
                    <td>${arr[i].city}</td>
                    <td>
                        <a href="javascript:" date-id=${i}>删除</a>
                    </td>
                `
                // 追加元素  父元素.appendChild(子元素)
                tbody.appendChild(tr)
            }
        }

        // 3.删除操作
        // 3.1 事件委托 tbody
        tbody.addEventListener('click', function (e) {
            if (e.target.tagName === 'A') {
                // 自己的想法 不利用自定义属性
                // arr.splice(e.target.stuId - 1, 1)
                // 得到当前元素的自定义属性 data-id
                // 删除arr数组对应的数据
                arr.splice(e.target.dataset.id, 1)
                // 重新渲染
                render()
            }
        })
    </script>

</body>

</html>

18. 重绘和回流

  1. 浏览器是如何进行界面渲染的?

a.解析(Parser)HTML,生成DOM树(DOM Tree)

b.同时解析(Parser) CSS,生成样式规则 (Style Rules)

c.根据DOM树和样式规则,生成渲染树(Render Tree)

d.进行布局 Layout(回流/重排):根据生成的渲染树,得到节点的几何信息(位置,大小)

e.进行绘制 Painting(重绘): 根据计算和获取的信息进行整个页面的绘制

f.Display: 展示在页面上

回流(重排)

当 Render Tree 中部分或者全部元素的尺寸、结构、布局等发生改变时,浏览器就会重新渲染部分或全部文档的过程称为
回流

重绘

由于节点(元素)的样式的改变并不影响它在文档流中的位置和文档布局时(比如:color、background-color、outline等),称为
重绘

重绘不一定引起回流,而回流一定会引起重绘。

**19.**Window对象

19.1 BOM

BOM(Browser Object Model ) 是浏览器对象模型。

window对象是一个全局对象,也可以说是JavaScript中的顶级对象

像document、alert()、console.log()这些都是window的属性,基本BOM属性和方法都是window的

所有通过var定义在全局作用域中的变量、函数都会变成window对象的属性和方法

window对象下的属性和方法调用的时候可以省略window

19.2 定时器-延时函数

JavaScript 内置的一个用来让代码延迟执行的函数,叫 
setTimeout

语法:
setTimeout(回调函数,等待的毫秒数)

setTimeout 仅仅只执行一次,所以可以理解为就是把一段代码延迟执行, 平时省略window

清除延时函数

let timer = setTimeout(回调函数,等待的毫秒数)
clearTimeout(timer)

注意点:

1.延时器需要等待,所以后面的代码先执行

每一次调用延时器都会产生一个新的延时器

两种定时器对比:
执行的次数

延时函数: 执行一次

间歇函数:每隔一段时间就执行一次,除非手动清除

案例:5秒钟之后消失的广告

<body>
  <img src="./images/ad.png" alt="">
  <script>
    // 1.获取元素
    const img = document.querySelector('img')
    setTimeout(function () {
      img.style.display = 'none'
    }, 5000)
  </script>
</body>
19.3 JS 执行机制

JavaScript 语言的一大特点就是单线程,也就是说,同一个时间只能做一件事。

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

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

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

异步。

同步

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

比如做饭的同步做法:我们要烧水煮饭,等水开了(10分钟之后),再去切菜,炒菜。

异步

你在做一件事情时,因为这件事情会花费很长时间,在做这件事时,还可以去处理其他事情。

比如做饭的异步做法,我们在烧水的同时,利用这10分钟,去切菜,炒菜。

他们的本质区别: 这条流水线上各个流程的执行顺序不同。

同步任务

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

异步任务

JS 的异步是通过回调函数实现的。

一般而言,异步任务有以下三种类型:

1、普通事件,如 click、resize 等

2、资源加载,如 load、error 等

3、定时器,包括 setInterval、setTimeout 等

异步任务相关添加到
任务队列
中(任务队列也称为消息队列)。

JS 执行机制

  1. 先执行
    执行栈中的同步任务

2
. 异步任务放入任务队列中。

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

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

19.4 location对象

location 的数据类型是对象,它拆分并保存了 URL 地址的各个组成部分。

常用属性和方法:

● href 属性获取完整的 URL 地址,对其赋值时用于地址的跳转

● search 属性获取地址中携带的参数,符号 ?后面部分

● hash 属性获取地址中的啥希值,符号 # 后面部分,经常用于不刷新页面,显示不同页面,比如 网易云音乐

● reload 方法用来刷新当前页面,传入参数 true 时表示强制刷新,如:location.reload(true)

案例:5秒钟之后跳转的页面

<body>
  <a href="http://www.itcast.cn">支付成功<span>5</span>秒钟之后跳转到首页</a>
  <script>
    // 1. 获取元素
    const a = document.querySelector('a')
    // 2.开启定时器
    // 3. 声明倒计时变量
    let num = 5
    let timerId = setInterval(function () {
      num--
      a.innerHTML = `支付成功<span>${num}</span>秒钟之后跳转到首页`
      // 如果num === 0 则停止定时器,并且完成跳转功能
      if (num === 0) {
        clearInterval(timerId)
        // 4. 跳转  location.href
        location.href = 'http://www.itcast.cn'
      }
    }, 1000)
  </script>
</body>
19.5 navigator对象

navigator的数据类型是对象,该对象下记录了浏览器自身的相关信息

常用属性和方法: 通过 userAgent 检测浏览器的版本及平台

// 检测 userAgent(浏览器信息)
!(function () {
const userAgent = navigator.userAgent
// 验证是否为Android或iPhone
const android = userAgent.match(/(Android);?[\s\/]+([\d.]+)?/)
const iphone = userAgent.match(/(iPhone\sOS)\s([\d_]+)/)
// 如果是Android或iPhone,则跳转至移动站点
if (android || iphone) {
location.href = 'http://m.itcast.cn' }
})()
19.6 histroy对象

history 的数据类型是对象,主要管理历史记录, 该对象与浏览器地址栏的操作相对应,如前进、后退、历史记录等

常用属性和方法

20.本地存储

20.1 本地存储介绍

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

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

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

3、容量较大,sessionStorage和localStorage约 5M 左右

常见的使用场景:
https://todomvc.com/examples/vanilla-es6/
页面刷新数据不丢失

20.2 本地存储分类- localStorage

作用:
可以将数据永久存储在本地(用户的电脑), 除非手动删除,否则关闭页面也会存在

特性: 可以多窗口(页面)共享(同一浏览器可以共享)、以键值对的形式存储使用

语法:

存储数据:
localStorage.setItem(key, value)

获取数据:
localStorage.getItem(key)

删除数据:localStorage.removeItem(key)

20.2 本地存储分类- sessionStorage

特性:

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

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

以键值对的形式存储使用

用法跟
localStorage 基本相同

20.3 存储复杂数据类型

本地只能存储字符串,无法存储复杂数据类型。

解决:
需要将复杂数据类型转换成JSON字符串,在存储到本地

语法:JSON.stringify(复杂数据类型)

**问题:**因为本地存储里面取出来的是字符串,不是对象,无法直接使用

解决:
把取出来的字符串转换为对象

语法:JSON.parse(JSON字符串)

综合案例:学生就业信息表

拓展1:数组中map方法

**作用:**迭代数组

语法:

const arr = ['red', 'blue', 'green']
    // map 方法也是遍历  处理数据  可以返回一个数组
    const newArr = arr.map(function (item, i) {
      // console.log(item)  // 数组元素  'red'
      // console.log(i)  // 下标
      return item + '老师'
    })
    console.log(newArr)  // ['red老师', 'blue老师', 'green老师']

**使用场景:**map 可以处理数据,并且
返回新的数组

拓展2:数组中join方法

作用: join() 方法用于把数组中的所有元素转换一个字符串

语法:

<script>
    const arr = ['red', 'blue', 'green']
    // 把数组元素转换为字符串
    console.log(arr.join(' ')) 
  </script>

参数: 数组元素是通过参数里面指定的分隔符进行分隔的

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>学生信息管理</title>
    <link rel="stylesheet" href="css/index.css" />
</head>

<body>
    <h1>新增学员</h1>
    <form class="info" autocomplete="off">
        姓名:<input type="text" class="uname" name="uname" />
        年龄:<input type="text" class="age" name="age" />
        性别:
        <select name="gender" class="gender">
            <option value="男">男</option>
            <option value="女">女</option>
        </select>
        薪资:<input type="text" class="salary" name="salary" />
        就业城市:<select name="city" class="city">
            <option value="北京">北京</option>
            <option value="上海">上海</option>
            <option value="广州">广州</option>
            <option value="深圳">深圳</option>
            <option value="曹县">曹县</option>
        </select>
        <button class="add">录入</button>
    </form>

    <h1>就业榜</h1>

    <table>
        <thead>
            <tr>
                <th>学号</th>
                <th>姓名</th>
                <th>年龄</th>
                <th>性别</th>
                <th>薪资</th>
                <th>就业城市</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody>
            <!-- 
        <tr>
          <td>1001</td>
          <td>欧阳霸天</td>
          <td>19</td>
          <td>男</td>
          <td>15000</td>
          <td>上海</td>
          <td>
            <a href="javascript:">删除</a>
          </td>
        </tr> 
        -->
        </tbody>
    </table>
    <script>
        // 参考数据
        const initData = [
            {
                stuId: 1001,
                uname: '欧阳霸天',
                age: 19,
                gender: '男',
                salary: '20000',
                city: '上海',
            }
        ]
        // localStorage.setItem('data', JSON.stringify(initData))
        // 1.渲染业务
        // 1.1 读取本地存储的数据   student-data  本地存储的命名
        // (1) 本地存储有数据则记得转换为对象然后存储到变量里面,后期用于渲染页面
        // (2) 如果没有数据,则用空数组来替代
        const arr = JSON.parse(localStorage.getItem('data')) || []
        // 1.2 利用map和join方法来渲染页面
        const tbody = document.querySelector('tbody')
        function render() {
            // (1) 利用map遍历数组,返回对应tr的数组
            const trArr = arr.map(function (ele, index) {
                return `
                <tr>
                    <td>${ele.stuId}</td>
                    <td>${ele.uname}</td>
                    <td>${ele.age}</td>
                    <td>${ele.gender}</td>
                    <td>${ele.salary}</td>
                    <td>${ele.city}</td>
                    <td>
                        <a href="javascript:" data-id='${index}'>删除</a>
                    </td>
                </tr> 
                `
            })
            // (2) 把数组转换为字符串 join
            // (3) 把生成的字符串追加给tbody
            tbody.innerHTML = trArr.join('')
        }
        render()

        // 2.新增业务
        // 2.1 form表单注册提交事件,阻止默认行为
        const info = document.querySelector('.info')
        const uname = document.querySelector('.uname')
        const age = document.querySelector('.age')
        const salary = document.querySelector('.salary')
        const gender = document.querySelector('.gender')
        const city = document.querySelector('.city')
        info.addEventListener('submit', function (e) {
            e.preventDefault()
            // 2.2 非空判断
            if (!uname.value || !age.value || !salary.value) {
                return alert('输入内容不能为空')
            }
            // 2.3 给arr数组追加对象,里面存储 表单获取过来的数据
            arr.push({
                stuId: arr.length ? arr[arr.length - 1].stuId + 1 : 1,
                uname: uname.value,
                age: age.value,
                gender: gender.value,
                salary: salary.value,
                city: city.value,
            })
            // 2.4 渲染页面和重置表单reset()方法
            render()
            this.reset()
            // 2.5 把数组重新存入本地存储里面,记得转换为JSON字符串存储
            localStorage.setItem('data', JSON.stringify(arr))
        })

        // 3.删除业务
        // 3.1 采用事件委托形式,给tbody注册点击事件
        tbody.addEventListener('click', function (e) {
            if (e.target.tagName === 'A') {
                // 3.2 得到当前点击的索引号。渲染数据时,动态给a链接添加自定义属性data-id
                console.log(e.target.dataset.id)
                // 确认框confirm 确认是否要真的删除
                if (confirm('您确定要删除这条数据吗?')) {
                    // 3.3 根据索引号,利用splice删除数组这条数据
                    arr.splice(e.target.dataset.id, 1)
                    // 3.4 重新渲染页面
                    render()
                    // 3.5 把最新arr数组存入本地存储
                    localStorage.setItem('data', JSON.stringify(arr))
                }
            }
        })

    </script>
</body>

</html>

21.正则表达式

21.1 什么是正则表达式

正则表达式
(Regular Expression)是用于
匹配字符串中字符组合
的模式。在 JavaScript中,正则表达式也是对象。通常用来查找、替换那些符合正则表达式的文本,许多语言都支持正则表达式。

正则表达式在 JavaScript中的使用场景

例如
验证表单
:用户名表单只能输入英文字母、数字或者下划线, 昵称输入框中可以输入中文。
(
匹配
) ,比如用户名: /1{3,16}$/

过滤掉页面内容中的一些
敏感词
(
替换
)

3.从字符串中获取我们想要的特定部分(
提取
)等 。

21.2 语法

1. 定义正则表达式语法:
const 变量名 = /表达式/

其中 / / 是正则表达式字面量,比如:const reg = /前端/

2.判断是否有符合规则的字符串:

test()
方法 用来查看正则表达式与指定的字符串是否匹配

语法:
regObj.test(被检测的字符串)

如果正则表达式与指定的字符串匹配 ,返回true,否则false

<script>
    const str = '我们在学习前端,希望学习前端能高薪毕业'
    // 正则表达式使用:
    // 1. 定义规则
    const reg = /前端/
    // 2. 是否匹配
    console.log(reg.test(str))  // true
</script>

3.检索(查找)符合规则的字符串:

exec()
方法 在一个指定字符串中执行一个搜索匹配

语法:
regObj.exec(被检测的字符串)

如果匹配成功,exec() 方法返回一个数组,否则返回null

<script>
    const str = '我们在学习前端,希望学习前端能高薪毕业'
    // 正则表达式使用:
    // 1. 定义规则
    const reg = /前端/
    // 2. exec()
    console.log(reg.exec(str))  // 返回数组
  </script>

正则表达式检测查找 test方法和exec方法有什么区别?


test方法 用于判断是否有符合规则的字符串,返回的是布尔值 找到返回true,否则false


exec方法用于检索(查找)符合规则的字符串,找到返回数组,否则为 null

21.3 元字符

**元字符(特殊字符)😗*是一些具有特殊含义的字符,可以极大提高了灵活性和强大的匹配功能。

比如,规定用户只能输入英文26个英文字母,普通字符的话 abcdefghijklm….但是换成元字符写法: [a-z]

参考文档:

MDN:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions

正则测试工具:
http://tool.oschina.net/regex

为了方便记忆和学习,我们对众多的元字符进行了分类:

1.边界符(表示位置,开头和结尾,必须用什么开头,用什么结尾)

正则表达式中的边界符(位置符)用来
提示字符所处的位置
,主要有两个字符

如果 ^ 和 $ 在一起,表示必须是精确匹配。

<script>
    // 元字符
    // 1. 边界符
    console.log(/^哈/.test('哈')) // true
    console.log(/^哈/.test('哈哈')) // true
    console.log(/^哈/.test('二哈')) // false
    console.log(/^哈$/.test('哈')) // true  只有这种情况为true 否则全是false
    console.log(/^哈$/.test('哈哈')) // false
    console.log(/^哈$/.test('二哈')) // false
  </script>

2.量词 (表示重复次数)

量词用来
设定某个模式出现的次数

注意: 逗号左右两侧千万不要出现空格

<script>
    // 元字符
    // 量词 * 类似 >=0 次
    // console.log(/^哈$/.test('哈')) // true
    // console.log(/^哈*$/.test('')) // true
    // console.log(/^哈*$/.test('哈')) // true
    // console.log(/^哈*$/.test('哈哈')) // true
    // console.log(/^哈*$/.test('二哈很傻')) //  false
    // console.log(/^哈*$/.test('哈很傻')) //  false
    // console.log(/^哈*$/.test('哈很哈')) // false
    // console.log('------------------')

    // //  量词 + 类似 >=1 次
    // console.log(/^哈$/.test('哈')) // true
    // console.log(/^哈+$/.test('')) // false
    // console.log(/^哈+$/.test('哈')) // true
    // console.log(/^哈+$/.test('哈哈')) // true
    // console.log(/^哈+$/.test('二哈很傻')) //  false
    // console.log(/^哈+$/.test('哈很傻')) //  false
    // console.log(/^哈+$/.test('哈很哈')) // false
    // console.log('------------------')

    // //  量词 ? 类似  0 || 1
    // console.log(/^哈?$/.test('')) // true
    // console.log(/^哈?$/.test('哈')) // true
    // console.log(/^哈?$/.test('哈哈')) // false
    // console.log(/^哈?$/.test('二哈很傻')) //  false
    // console.log(/^哈?$/.test('哈很傻')) //  false
    // console.log(/^哈?$/.test('哈很哈')) // false

    // 量词 {n} 写几,就必须出现几次
    console.log(/^哈{4}$/.test('哈'))
    console.log(/^哈{4}$/.test('哈哈'))
    console.log(/^哈{4}$/.test('哈哈哈'))
    console.log(/^哈{4}$/.test('哈哈哈哈'))
    console.log(/^哈{4}$/.test('哈哈哈哈哈'))
    console.log(/^哈{4}$/.test('哈哈哈哈哈哈'))
    console.log('------------------')

    // 量词 {n,}   >=n
    console.log(/^哈{4,}$/.test('哈'))
    console.log(/^哈{4,}$/.test('哈哈'))
    console.log(/^哈{4,}$/.test('哈哈哈'))
    console.log(/^哈{4,}$/.test('哈哈哈哈'))
    console.log(/^哈{4,}$/.test('哈哈哈哈哈'))
    console.log(/^哈{4,}$/.test('哈哈哈哈哈哈'))
    console.log('------------------')

    // 量词 {n,m}  逗号左右两侧千万不能有空格    >=n && <= m
    console.log(/^哈{4,6}$/.test('哈'))
    console.log(/^哈{4,6}$/.test('哈哈'))
    console.log(/^哈{4,6}$/.test('哈哈哈'))
    console.log(/^哈{4,6}$/.test('哈哈哈哈'))
    console.log(/^哈{4,6}$/.test('哈哈哈哈哈'))
    console.log(/^哈{4,6}$/.test('哈哈哈哈哈哈'))
    console.log(/^哈{4,6}$/.test('哈哈哈哈哈哈哈'))
    console.log('------------------')
   
  </script>

3.字符类 (比如 \d 表示 0~9)

(1) 
[ ]
匹配字符集合,后面的字符串只要包含[ ]中任意
一个字符
,都返回 true。

a.[ ] 里面加上 - 连字符,使用
连字符 - 表示一个范围

比如:

[a-z] 表示
a 到 z
26个英文字母都可以

[a-zA-Z] 表示大小写都可以

[0-9] 表示 0~9 的数字都可以

b.[ ] 里面加上
^ 取反符号

比如: [^a-z] 匹配除了小写字母以外的字符

注意要写到中括号里面

  <script>
    // 元字符
    // 字符类   [abc]  只选1个
    console.log(/^[abc]$/.test('a'))  // true
    console.log(/^[abc]$/.test('b'))  // true
    console.log(/^[abc]$/.test('c'))  // true
    console.log(/^[abc]$/.test('ab'))  // false
    console.log(/^[abc]{2}$/.test('ab'))  // true
    console.log('------------------')

    // 字符类   [a-z]  只选1个
    console.log(/^[A-Z]$/.test('p'))  // false
    console.log(/^[A-Z]$/.test('P'))  // true
    console.log(/^[0-9]$/.test(2))  // true
    console.log(/^[a-zA-Z0-9]$/.test(2))  // true
    console.log(/^[a-zA-Z0-9]$/.test('p'))  // true
    console.log(/^[a-zA-Z0-9]$/.test('P'))  // true
    console.log('------------------')
  </script>

(2)
.
匹配除换行符之外的任何单个字符

(3)预定义:
指的是
某些常见模式的简写方式。

案例:用户名验证案例

<body>
    <input type="text">
    <span></span>
    <script>
        // 1. 准备正则
        const reg = /^[a-zA-Z0-9-_]{6,16}$/
        const input = document.querySelector('input')
        const span = input.nextElementSibling
        input.addEventListener('blur', function () {
            // console.log(reg.test(this.value))
            if (reg.test(this.value)) {
                span.innerHTML = '输入正确'
                span.className = 'right'
            } else {
                span.innerHTML = '请输入6~16位的英文数字下划线'
                span.className = 'error'
            }
        })
    </script>
</body>
21.4 修饰符

修饰符约束正则执行的某些细节行为,如是否区分大小写、是否支持多行匹配等

语法:/表达式/修饰符

i
是单词 ignore 的缩写,
正则匹配时字母不区分大小写

g
是单词 global 的缩写,
匹配所有满足正则表达式的结果

替换 replace

语法:
字符串.replace(/正则表达式/,‘替换掉文本’)

案例:
过滤敏感字

<body>
  <textarea name="" id="" cols="30" rows="10"></textarea>
  <button>发布</button>
  <div></div>
  <script>
    const tx = document.querySelector('textarea')
    const btn = document.querySelector('button')
    const div = document.querySelector('div')
    btn.addEventListener('click', function () {
      // console.log(tx.value)
      div.innerHTML = tx.value.replace(/激情|基情/g, '**')
      tx.value = ''
    })
  </script>
</body>

综合案例:小兔鲜页面注册

<script>
        (function () {
            // 1.发送短信验证码模块
            const code = document.querySelector('.code')
            let flag = true // 通过一个变量来控制   节流阀
            // 1.1 点击事件
            code.addEventListener('click', function () {
                if (flag) {
                    // flag取反,不能第二次点击
                    flag = false
                    let i = 5
                    code.innerHTML = `${i}秒后重新获取`
                    let timerId = setInterval(function () {
                        i--
                        // 这里不能用 this,定时器的调用者指向 window
                        code.innerHTML = `${i}秒后重新获取`
                        if (i === 0) {
                            // 清除定时器
                            clearInterval(timerId)
                            // 重新获取
                            code.innerHTML = `重新获取`
                            // 到时间了,可以开启flag 即可以点击了
                            flag = true
                        }
                    }, 1000)
                }
            })
        })();

        // 2. 验证的是用户名
        // 2.1 获取用户名表单
        const username = document.querySelector('[name=username]')
        // 2.2 使用change事件  值发生变化的时候
        username.addEventListener('change', verifyName)
        // 2.3 封装 verifyName 函数
        function verifyName() {
            const span = username.nextElementSibling
            // 2.4 定义规则  用户名
            const reg = /^[a-zA-Z0-9-_]{6,10}$/
            if (!reg.test(username.value)) {
                span.innerText = '输入不合法,请输入6~10位'
                return false
            }
            // 2.5 合法的 清空span
            span.innerText = ''
            return true
        }

        // 3. 验证的是手机号
        // 3.1 获取手机表单
        const phone = document.querySelector('[name=phone]')
        // 3.2 使用change事件  值发生变化的时候
        phone.addEventListener('change', verifyPhone)
        // 3.3 封装 verifyPhone 函数
        function verifyPhone() {
            const span = phone.nextElementSibling
            // 3.4 定义规则  手机号
            const reg = /^1(3\d|4[5-9]|5[0-35-9]|6[567]|7[0-8]|8\d|9[0-35-9])\d{8}$/
            if (!reg.test(phone.value)) {
                span.innerText = '输入不合法,请输入正确的11位手机号'
                return false
            }
            // 3.5 合法的 清空span
            span.innerText = ''
            return true
        }

        // 4. 验证的是验证码
        // 4.1 获取验证码表单
        const codeInput = document.querySelector('[name=code]')
        // 4.2 使用change事件  值发生变化的时候
        codeInput.addEventListener('change', verifyCode)
        // 4.3 封装 verifyCode 函数
        function verifyCode() {
            const span = codeInput.nextElementSibling
            // 4.4 定义规则  手机号
            const reg = /^\d{6}$/
            if (!reg.test(codeInput.value)) {
                span.innerText = '输入不合法,请输入6位数字'
                return false
            }
            // 4.5 合法的 清空span
            span.innerText = ''
            return true
        }

        // 5. 验证的是密码框
        // 5.1 获取密码框表单
        const password = document.querySelector('[name=password]')
        // 5.2 使用change事件  值发生变化的时候
        password.addEventListener('change', verifyPwd)
        // 5.3 封装 verifyPwd 函数
        function verifyPwd() {
            const span = password.nextElementSibling
            // 5.4 定义规则  手机号
            const reg = /^[a-zA-Z0-9-_]{6,20}$/
            if (!reg.test(password.value)) {
                span.innerText = '输入不合法,请输入6~20位数字'
                return false
            }
            // 5.5 合法的 清空span
            span.innerText = ''
            return true
        }

        // 6. 密码的再次验证
        // 6.1 获取再次验证表单
        const confirm = document.querySelector('[name=confirm]')
        // 6.2 使用change事件  值发生变化的时候
        confirm.addEventListener('change', verifyConfirm)
        // 6.3 封装 verifyConfirm 函数
        function verifyConfirm() {
            const span = confirm.nextElementSibling
            // 6.4 当前表单的值不等于密码框的值 就是错误的
            if (confirm.value !== password.value) {
                span.innerText = '两次密码输入不一致'
                return false
            }
            // 6.5 合法的 清空span
            span.innerText = ''
            return true
        }

        // 7. 同意模块
        const queren = document.querySelector('.icon-queren')
        queren.addEventListener('click', function () {
            // 切换类 toggle 原来有的就删除,没有的就添加
            this.classList.toggle('icon-queren2')
        })

        // 8.表单提交模块
        const form = document.querySelector('form')
        form.addEventListener('submit', function (e) {
            // 判断是否勾选同意模块 ,如果有 icon-queren2说明就勾选了,否则没勾选
            if (!queren.classList.contains('icon-queren2')) {
                alert('请勾选同意协议')
                // return 中止程序,但不阻止提交  所以要阻止提交
                e.preventDefault()
            }
            // 依次判断上面的每个框框是否通过,只有有一个没有通过就阻止
            // 因为每一个都要判断 所以要分开写 如果用 || 只会显示第一个不通过的错误信息
            if (!verifyName()) e.preventDefault()
            if (!verifyPhone()) e.preventDefault()
            if (!verifyCode()) e.preventDefault()
            if (!verifyPwd()) e.preventDefault()
            if (!verifyConfirm()) e.preventDefault()
        })
    </script>

阶段案例:小兔鲜登录页面

 <script>
        // 1. tab栏切换 事件委托
        const tab_nav = document.querySelector('.tab-nav')
        const pane = document.querySelectorAll('.tab-pane')
        // 1.1 事件监听
        tab_nav.addEventListener('click', function (e) {
            if (e.target.tagName === 'A') {
                // 取消上一个active
                tab_nav.querySelector('.active').classList.remove('active')
                // 当前元素添加active
                e.target.classList.add('active')

                // 先干掉所有人 for循环
                for (let i = 0; i < pane.length; i++) {
                    pane[i].style.display = 'none'
                }
                // 让对应序号的 pane 显示
                pane[e.target.dataset.id].style.display = 'block'
            }
        })

        // 点击提交模块
        const form = document.querySelector('form')
        const agree = document.querySelector('[name=agree]')
        const username = document.querySelector('[name=username]')
        form.addEventListener('submit', function (e) {
            e.preventDefault()
            // 判断是否勾选同意协议
            if (!agree.checked) {
                return alert('请勾选同意按钮')
            }

            // 记录用户名到本地存储
            localStorage.setItem('xtx-uname', username.value)
            // 跳转到首页
            location.href = './小兔鲜index.html'
        })
    </script>

阶段案例:小兔鲜首页页面

<script>
        // 1. 获取第一个小li
        const li1 = document.querySelector('.xtx_navs li:first-child')
        const li2 = li1.nextElementSibling
        // 2.最好做个渲染函数 因为退出登录需要重新渲染
        function render() {
            // 2.1 读取本地存储的用户名
            const uname = localStorage.getItem('xtx-uname')
            // console.log(uname);
            if (uname) {
                li1.innerHTML = `<a href="javascript:;"><i class="iconfont icon-user"> ${uname}</i></a>`
                li2.innerHTML = `<a href="javascript:;">退出登录</a>`
            } else {
                li1.innerHTML = `<a href="./小兔鲜login.html">请先登录</a>`
                li2.innerHTML = `<a href="./小兔鲜register.html">免费注册</a>`
            }
        }
        render()    // 调用函数

        // 2.点击退出登录模块
        li2.addEventListener('click', function () {
            // 删除本地存储的数据
            localStorage.removeItem('xtx-uname')
            // 重新渲染
            render()
        })
    </script>

实战案例:放大镜效果

<script>
        // 1. 获取三个盒子
        // 2. 小盒子 图片切换效果
        const small = document.querySelector('.small')
        //  中盒子
        const middle = document.querySelector('.middle')
        //  大盒子
        const large = document.querySelector('.large')
        // 2. 事件委托
        // mouseenter 没有冒泡,因此不能实现事件委托,需要使用 mouseover 通过事件冒泡触发 small
        small.addEventListener('mouseover', function (e) {
            if (e.target.tagName === 'IMG') {
                // 排他思想 干掉以前的active
                this.querySelector('.active').classList.remove('active')
                // 当前元素的爸爸添加 active
                e.target.parentNode.classList.add('active')
                // 拿到当前小图片的src
                // 让中等盒子里面的图片,src更换为小图片的src
                middle.querySelector('img').src = e.target.src
                // 大盒子更换背景图片
                large.style.backgroundImage = `url(${e.target.src})`
            }
        })

        // 3.鼠标经过中等盒子,显示隐藏大盒子
        middle.addEventListener('mouseenter', show)
        middle.addEventListener('mouseleave', hide)
        let timeId = null
        // 显示函数 显示大盒子
        function show() {
            // 先清除定时器
            clearTimeout(timeId)
            large.style.display = 'block'
        }
        // 隐藏函数 隐藏大盒子
        function hide() {
            timeId = setTimeout(function () {
                large.style.display = 'none'
            }, 200)
        }

        // 4.鼠标经过大盒子,显示隐藏大盒子
        large.addEventListener('mouseenter', show)
        large.addEventListener('mouseleave', hide)

        // 5.鼠标经过中等盒子,显示隐藏 黑色遮罩层
        const layer = document.querySelector('.layer')
        middle.addEventListener('mouseenter', function () {
            layer.style.display = 'block'
        })
        middle.addEventListener('mouseleave', function () {
            layer.style.display = 'none'
        })

        // 6.移动黑色遮罩盒子
        middle.addEventListener('mousemove', function (e) {
            // 鼠标在middle 盒子里面的坐标 = 鼠标在页面中的坐标 - middle 中等盒子的坐标 
            // middle 中等盒子的坐标 使用 getBoundingClientRect() 来获取相当于可视区的盒子坐标,不用 offsetLeft 和 offsetTop,因为这两个属性容易被带有定位的父级影响
            // 鼠标在页面中的坐标
            // console.log(e.pageX);
            // middle 中等盒子的坐标
            // console.log(middle.getBoundingClientRect().left);
            let x = e.pageX - middle.getBoundingClientRect().left - document.documentElement.scrollLeft
            let y = e.pageY - middle.getBoundingClientRect().top - document.documentElement.scrollTop
            // console.log(x, y);
            // 黑色遮罩移动在 middle 盒子内限制移动的距离
            if (x >= 0 && x <= 400 && y >= 0 && y <= 400) {
                // 黑色盒子不是一直移动的
                // 声明2个变量 黑色盒子移动的 mx my 变量
                let mx = 0, my = 0
                if (x < 100) mx = 0
                if (x >= 100 && x <= 300) mx = x - 100
                if (x > 300) mx = 200

                if (y < 100) my = 0
                if (y >= 100 && y <= 300) my = y - 100
                if (y > 300) my = 200

                layer.style.left = mx + 'px'
                layer.style.top = my + 'px'

                // 大盒子的背景图片要跟随中等盒子移动 存在的关系是 2倍
                large.style.backgroundPositionX = -2 * mx + 'px'
                large.style.backgroundPositionY = -2 * my + 'px'
            }
        })
    </script>

三、JS进阶

1.作用域

作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问,

作用域分为: 局部作用域、
全局作用域

1.1 局部作用域

局部作用域分为函数作用域和块作用域。

1.
函数作用域:

在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。

总结:

  1. 函数内部声明的变量,在函数外部无法被访问

  2. 函数的参数也是函数内部的局部变量

  3. 不同函数内部声明的变量无法互相访问

  4. 函数执行完毕后,函数内部的变量实际被清空了

2. 块作用域:

在JavaScript中使用{ }包裹的代码称为代码块,代码块内部声明的变量外部将
有可能
无法被访问。

总结:

  1. let 声明的变量会产生块作用域,var 不会产生块作用域

  2. const 声明的常量也会产生块作用域

  3. 不同代码块之间的变量无法互相访问

  4. 推荐使用 let 或 const

1.2 全局作用域

### 阶段案例:小兔鲜首页页面




### 实战案例:放大镜效果




## 三、JS进阶


### 1.作用域



作用域(scope)规定了变量能够被访问的“范围”,离开了这个“范围”变量便不能被访问, 



作用域分为: 局部作用域、
全局作用域



#### 1.1 局部作用域



局部作用域分为函数作用域和块作用域。 



**1.** 
**函数作用域:** 


在函数内部声明的变量只能在函数内部被访问,外部无法直接访问。
![](https://img-blog.csdnimg.cn/869a4d155d1a4b658303a70da6e57c91.png)


**总结:** 


1. 函数内部声明的变量,在函数外部无法被访问 


2. 函数的参数也是函数内部的局部变量 


3. 不同函数内部声明的变量无法互相访问 


4. 函数执行完毕后,函数内部的变量实际被清空了



**2. 块作用域:** 


在JavaScript中使用{ }包裹的代码称为代码块,代码块内部声明的变量外部将
有可能
无法被访问。
![](https://img-blog.csdnimg.cn/e2c62118c64a445d95bdfd2552999533.png)


**总结:** 


1. let 声明的变量会产生块作用域,var 不会产生块作用域 


2. const 声明的常量也会产生块作用域 


3. 不同代码块之间的变量无法互相访问 


4. 推荐使用 let 或 const


#### 1.2 全局作用域



<script> 标签 
和 
.js 文件 
的【最外层】就是全局作用域,在此声明的变量在函数内部也可以被访问。 全局作用域中声明的变量,任何其它作用域都可以被访问。
![](https://img-blog.csdnimg.cn/93633899bc04432db32e99018e069126.png)


**注意:** 


1. 为 window 对象动态添加的属性默认也是全局的,不推荐! 


2. 函数中未使用任何关键字声明的变量为全局变量,不推荐!!! 


3. 尽可能少的声明全局变量,防止全局变量被污染




JavaScript 中的作用域是程序被执行时的底层机制,了解这一机制有助于规范代码书写习惯,避免因作用域导致的语法错误。



#### 1.3 作用域链



作用域链本质上是底层的
变量查找机制
。 



在函数被执行时,会
优先查找当前
函数作用域中查找变量。如果当前作用域查找不到则会依次
逐级查找父级作用域
直到全局作用域。


![](https://img-blog.csdnimg.cn/c03c02ab83da49e485c392fab2db653c.png)
![](https://img-blog.csdnimg.cn/c48ba527e381405392c495c7ab889fed.png)



**总结:** 


1. 嵌套关系的作用域串联起来形成了作用域链 


2. 相同作用域链中按着
从小到大
的规则查找变量 


3. 
子作用域能够访问父作用域,父级作用域无法访问子级作用域


#### 1.4 JS垃圾回收机制



**1. 什么是垃圾回收机制?** 



垃圾回收机制(Garbage Collection) 简称 GC 


JS中内存的分配和回收都是自动完成的,内存在不使用的时候会被垃圾回收器自动回收。


正因为垃圾回收器的存在,许多人认为JS不用太关心内存管理的问题。


但如果不了解JS的内存管理机制,我们同样非常容易成内存泄漏(内存无法被回收)的情况。


不再用到的内存,没有及时释放,
就叫做
**内存泄漏。**




**2.内存的生命周期** 



JS环境中分配的内存, 一般有如下生命周期: 


1. 内存分配
:当我们声明变量、函数、对象的时候,系统会自动为他们分配内存 


2. 内存使用
:即读写内存,也就是使用变量、函数等 


3. 内存回收
:使用完毕,由垃圾回收自动回收不再使用的内存 


4. 
说明:全局变量一般不会回收(关闭页面回收); 
一般情况下局部变量的值不用了, 会被自动回收


#### 算法刷题

大厂面试还是很注重算法题的,尤其是字节跳动,算法是问的比较多的,关于算法,推荐《LeetCode》和《算法的乐趣》,这两本我也有电子版,字节跳动、阿里、美团等大厂面试题(含答案+解析)、学习笔记、Xmind思维导图均可以分享给大家学习。



![](https://img-blog.csdnimg.cn/img_convert/c582a01373152bb4cd38bc6ad5cc8027.png)



**写在最后**

**最后,对所以做Java的朋友提几点建议,也是我的个人心得:**

1.  疯狂编程

2.  学习效果可视化

3.  写博客
4.  阅读优秀代码
5.  心态调整




**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**需要这份系统化的资料的朋友,可以添加V获取:vip1024c (备注前端)**
[外链图片转存中...(img-WuFuHSZA-1713307536588)]

**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  1. a-z0-9_- ↩︎

转载请注明出处或者链接地址:https://www.qianduange.cn//article/6247.html
标签
评论
发布的文章

@JsonCreator和@JsonValue

2024-05-05 22:05:05

Python 字符串转换为 JSON

2024-05-05 22:05:00

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!