目录

一、JS基础语法

1、JS书写位置

1.1、行内

1.1.1、概念
  • 含义:在html标签属性中编写的js代码,即为行内写法
1.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>
</head>
<body>
  <!-- js行内写法,用于阻止a标签的默认跳转行为 -->
  <a href="javascript:void(0)">点击我,不跳转</a>
</body>
</html>
1.1.3、页面效果

在这里插入图片描述

1.2、内部

1.2.1、概念
  • 含义:js代码写在html文件内部
  • 书写位置:必须放在body标签中最后的位置,避免标签未加载时js代码已经执行。
1.2.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>
</head>
<body>
  <!-- js代码写在html文件内容 -->
  <script>
    alert("hello, world~")
  </script>
</body>
</html>
1.2.3、页面效果

在这里插入图片描述

1.3、外部

1.3.1、概念
  • 含义:单独编写js文件,然后使用script标签引入html文件之中
1.3.2、代码示例

helloworld.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <!-- 引入外部js文件 -->
  <script src="./helloworld.js"></script>
</body>
</html>

helloworld.js:

alert('hello, world~')
1.3.3、页面效果

在这里插入图片描述

2、JS注释

2.1、概念

  • 划分:
    • 单行注释://
    • 多行注释:/* */

2.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>
</head>
<body>
  <!-- 引入外部js文件 -->
  <script>
    // 1、单行注释

    /*
      2、多行注释
    */
  </script>
</body>
</html>

3、JS结束符

  • 作用:使用英文的分号,代表语句结束
  • 必须要写:否,可以写也可不写,浏览器(Javascript引擎)会自动推断语句的结束位置
  • 约定:为了团队编程风格统一,结束符要么都写,要么都不写,需要按照团队要求来
  • 特殊情况:如果有多个连续的立即执行-匿名函数(function(){})(),那就必须使用分号分隔
    在这里插入图片描述

4、JS输入输出语法

  • 输入:
    • 在页面弹出弹框,让用户输入内容:let name = prompt("请输入您的名字:")
  • 输出:
    • 在控制台输出内容:console.log("hello world~")
    • 使用弹窗形式输出内容:alert("hello world~")
    • 在Dom文档中输出内容,当输出html标签时,页面还会自动渲染html标签:document.write("hello world~")

5、JS变量和常量

5.1、变量

5.1.1、声明、赋值、更新变量
  • 语法:let 变量名(只声明,不赋值)、let 变量名 = 变量值(声明 + 赋值)、let 变量名1 = 变量值1, 变量名2 = 变量值2(多个变量同时声明 + 赋值)
  • 声明变量:let name。如果没有直接赋值,此时nameundefined
  • 变量赋值: name = "明快de玄米61"
  • 声明变量 + 赋值:let name = "明快de玄米61"
  • 更新变量:name = "明天会更好~"
  • 其他操作:
    • 一次定义多个变量:let name = "明快de玄米61", age = 18
5.1.2、命名规则和规范
  • 规则(必须遵守,不遵守就出错):
    • 只能使用数字、字母、下划线、$符,并且不能以数字开头
    • 不能使用关键字。即JS内置的一些英文词汇,例如left、var、if、for、while、switch、case、break、continue等
    • 变量名称中的字母严格区分大小写,例如age和Age是不同的变量
  • 规范(建议遵守,不遵守也不会报错)
    • 变量命名需要见名知意
    • 变量命名遵守小驼峰命名法(第一个首字母小写,后面每一个首字母大写,例如:userName)
5.1.3、let和var的区别
  • 我们使用谁:使用let,不使用var
  • 使用var的缺点:
    • 可以先使用,然后在声明
      在这里插入图片描述
    • 可以重复声明
      在这里插入图片描述

5.2、常量

  • 语法:const 常量名 = 常量值
  • 命名:基本和常量一致,不过一般采用字母全大写方式,单词中间使用下划线_分隔
  • 声明、赋值、更新:声明的时候必须赋值,不能进行更新,写法:const PI = 3.14
  • let和const用哪个?如果一个数据的值以后不会改变,那就使用const,所以优先使用const。其中数组或者对象优先使用const来声明

6、JS数据类型

6.1、分类

  • 基本数据类型:
    • 数字型(number)
    • 字符串型(string)
    • 布尔型(boolean)
    • 未定义型(undefined)
  • 引用数据类型(统一为object类型):
    • 数组
    • 对象

6.2、基本数据类型

6.2.1、数字类型(Number)
  • 含义:即我们数学中学习到的数字,可以是整数、小数,无论正负都可以

  • NaN:

    • 当计算错误或者出现一个未定义的数学操作,将返回NaN。例如:console.log('老师' - 2)
    • 任何针对NaN的操作都会返回NaN。例如:console.log(NaN - 2)
  • 常见的属性和方法(以下属性和方法其实是字符串的包装类Number对象的)

    • 实例方法 toFixed() 可以用于设置小数位数

      const num = 12.345
      // 保留两位小数,遵循四舍五入原则
      console.log(num.toFixed(2)) // 12.35
      
6.2.2、字符串类型(String)
6.2.2.1、概念
  • 含义:使用单引号(')、双引号(")、反引号(`)包裹起来的都是字符串。

    • 单引号和双引号没有本质上的区别,推荐使用单引号
    • 反引号(`)里面可以写模板字符串,其中模板字符串可以使用变量,也支持表达式计算,也可以结合document.write(XXX)将html代码写到body标签中
  • 使用注意事项:

    • 无论单引号或者双引号,必须成对使用
    • 单引号/双引号可以相互嵌套,但是不能自己嵌套自己(不支持console.log('我的名字是'明快de玄米61''))。支持外单内双(console.log('我的名字是"明快de玄米61"')),或者外双内单console.log("我的名字是'明快de玄米61'")
    • 在必要的时候,可以使用转义符\,用于输出单引号或者双引号,例如:console.log('我的名字是\'明快de玄米61\'')或者console.log("我的名字是\"明快de玄米61\"")
  • 字符串和数字的区别:如果使用console.log(XXX),其中字符串是黑色字,数字是蓝色字
    在这里插入图片描述

  • 常见的属性和方法(以下属性和方法其实是字符串的包装类String对象的)

    • 实例属性length可以获取字符串的长度(重点

      const str = 'hello world'
      console.log(str.length) // 11
      
    • 实例方法split('分隔符')可以用来将字符串拆分成数组(重点

      const str = "The quick brown fox jumps over the lazy dog.";
      
      const words = str.split(" ");
      console.log(words[3]); // "fox"
      
      const chars = str.split("");
      console.log(chars[8]); // "k"
      
      const strCopy = str.split();
      console.log(strCopy); // ["The quick brown fox jumps over the lazy dog."]
      
    • 实例方法substring() 返回该字符串从起始索引到结束索引(不包括)的部分,如果未提供结束索引,则返回到字符串末尾的部分。其作用等价于slice(start, end),但是slice功能更加强大,所以推荐使用(重点

      const str = "Mozilla";
      console.log(str.substring(1, 3)); // "oz"
      console.log(str.substring(2)); // "zilla"
      
    • 实例方法 startsWith() 用来判断当前字符串是否以另外一个给定的子字符串开头,并根据判断结果返回 true 或 false。

      const str1 = "Saturday night plans";
      console.log(str1.startsWith("Sat")); // true
      console.log(str1.startsWith("Sat", 3)); // false
      
    • 实例方法 includes() 执行区分大小写的搜索,以确定是否可以在一个字符串中找到另一个字符串,并根据情况返回 true 或 false。

      const sentence = "The quick brown fox jumps over the lazy dog.";
      const word = "fox";
      console.log(sentence.includes(word)); //  true
      
    • 实例方法 toUpperCase() 将该字符串转换为大写形式。

      const sentence = "The quick brown fox jumps over the lazy dog.";
      console.log(sentence.toUpperCase()); // THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG.
      
    • 实例方法 toLowerCase() 将该字符串转换为小写形式。

      const sentence = "The quick brown fox jumps over the lazy dog.";
      console.log(sentence.toLowerCase()); // the quick brown fox jumps over the lazy dog.
      
    • 实例方法 indexOf() 在字符串中搜索指定子字符串,并返回其第一次出现的位置索引。它可以接受一个可选的参数指定搜索的起始位置,如果找到了指定的子字符串,则返回的位置索引大于或等于指定的数字。如果没有找到,则返回 -1。

      const paragraph = "I think Ruth's dog is cuter than your dog!";
      const searchTerm = "dog";
      console.log(paragraph.indexOf(searchTerm)) // 15
      
    • 实例方法 startsWith() 用来判断当前字符串是否以另外一个给定的子字符串开头,并根据判断结果返回 true 或 false。

      const str1 = "Saturday night plans";
      console.log(str1.startsWith("Sat")); // true
      
    • 实例方法 endsWith() 用于判断一个字符串是否以指定字符串结尾,如果是则返回 true,否则返回 false。

      const str1 = "Cats are the best!";
      console.log(str1.endsWith("best!")); // true
      
    • 实例方法 replace(pattern, replacement) 返回一个新字符串,其中一个、多个或所有匹配的 pattern 被替换为 replacement。pattern 可以是字符串或 正则表达式,replacement 可以是字符串或一个在每次匹配时调用的函数。如果 pattern 是字符串,则只会替换第1个匹配项。字符串模式只会被替换一次。要执行全局搜索和替换,请使用带有 g 标志的正则表达式或使用 replaceAll()

      const paragraph = "I think Ruth's dog is cuter than your dog!";
      console.log(paragraph.replace("Ruth's", "my")); // I think my dog is cuter than your dog!
      const regex1 = /Dog/i;
      console.log(paragraph.replace(regex1, "ferret")); // I think Ruth's ferret is cuter than your dog!
      const regex2 = /Dog/ig;
      console.log(paragraph.replace(regex2, "ferret")); // I think Ruth's ferret is cuter than your ferret!
      
    • 实例方法 replaceAll(pattern, replacement) 方法返回一个新字符串,其中所有匹配 pattern 的部分都被替换为 replacement。pattern 可以是一个字符串或一个 正则表达式(注意:正则表达式必须使用g全局搜索),replacement 可以是一个字符串或一个在每次匹配时调用的函数。

      const paragraph = "I think Ruth's dog is cuter than your dog!";
      console.log(paragraph.replaceAll("dog", "monkey")); // I think Ruth's monkey is cuter than your monkey!
      
      // 正则表达式必须使用g全局搜索
      const regex = /Dog/ig;
      console.log(paragraph.replaceAll(regex, "ferret")); // I think Ruth's ferret is cuter than your ferret!
      
    • 实例方法 match(regexp) 检索字符串与正则表达式进行匹配的结果。如果没有匹配,则返回 null。

      • 如果使用 g 标志,则将返回与完整正则表达式匹配的所有结果,但不会返回捕获组。
      • 如果没有使用 g 标志,则只返回第一个完整匹配及其相关捕获组。
        const paragraph = "The quick brown fox jumps over the lazy dog. It barked.";
        const regex = /[A-Z]/g;
        const found = paragraph.match(regex);
        console.log(found); // ["T", "I"]
        
6.2.2.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>
</head>
<body>
  <script>
    // 1、使用变量
    let name = '明快de玄米61'
    console.log(`我的名字是${name}`)

    // 2、使用计算表达式
    let age = 18
    console.log(`我明年的年纪是${age + 1}`)

    // 3、打印html标签
    document.write(`
      <h3>${name}</h3>
      <p>年龄:${age}</p>
    `)
  </script>
</body>
</html>

页面示例:

在这里插入图片描述

6.2.3、布尔值类型(Boolean)
  • 含义:true代表真,false代表假
6.2.4、未定义类型(undefined)
6.2.4.1、概念
  • 含义:未定义是比较特殊的类型,只有一个值undefined。
  • 什么情况下会出现未定义类型:只声明变量,但是没有赋值,那么变量的默认值是undefined,但是很少直接为某个变量undefined
  • 工作中的使用场景:我们开发中经常声明一个变量,等待数据传送过来,如果我们不知道这个数据是否传递过来,可以检测这个变量是不是undefined,就可以进行判断
6.2.4.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>
</head>
<body>
  <script>
    let a
    console.log(`a = ${a}`)

    let b = undefined
    console.log(`b = ${b}`)

    let f = function(c) {
      console.log(`c = ${c}`)
    }
    f()
  </script>
</body>
</html>
6.2.4.3、页面效果

在这里插入图片描述

6.3、引用数据类型

6.3.1、数组(Array)
6.3.1.1、概念
  • 含义:将一组数据存储到单个变量名中的方式

  • 写法:let 变量名 = [值1, 值2……],如果不写值let 变量名 = []就是空数组。例如:let arr = [](空数组)、let arr = [1, '中国', true](有值数组,支持多个不同类型的数据)

  • 另外一种写法(了解即可):let 变量名 = new Array(值1, 值2……),例如:let arr = new Array(1, '中国', true)

  • 获取数组长度:数组变量名.length,例如:arr.length

  • 从数组中取值:数组变量名[数组下标],其中数组下标从0开始,例如:arr[0]

  • 更新数组值:数组变量名[数组下标] = 新值,例如:arr[0] = 2

  • 新增数组值:

    • 在数组尾部新增值:示例 arr.push(100, 'hello world')
    • 在数组头部新增值:示例 arr.unshift(20, 'my world')
  • 删除数组值:

    • 在数组尾部删除值:示例 arr.pop()
    • 在数组头部删除值:示例 arr.shift()
  • 支持新增、删除、修改功能:splice()

    • 语法:array.splice(start, deleteCount, item1, item2, ...)
    • 参数说明:
      • start(必需):开始修改的索引(从 0 开始)。
        • 若为负数,则从数组末尾开始计数(例如 -1 表示最后一个元素)。
        • 若绝对值超过数组长度,则根据正负决定起始位置(正数超长则从末尾追加,负数则从 0 开始)。
      • deleteCount(可选):要删除的元素个数。
        • 如果省略,则从 start 一直删除到数组末尾。
        • 如果为 0 或负数,则不删除任何元素。
      • item1, item2, ...(可选):要插入到数组的新元素,从 start 位置开始依次插入。
    • 返回值:
      • 返回一个由被删除元素组成的新数组。如果没有删除元素,则返回空数组。
    • 特点:
      • splice() 会直接修改原数组。
    • 补充说明:
      • 如果 start 超出数组长度,且 deleteCount 为 0,插入操作会在数组末尾进行。
      • 如果 deleteCount 大于 start 到末尾的实际元素个数,则删除到末尾为止。
      • 负索引同样有效,例如 arr.splice(-2, 1) 表示从倒数第二个元素开始删除 1 个。
    • 三种主要用法的代码示例:
        1. 删除元素(示例:只指定前两个参数,从指定位置开始删除指定数量的元素。)
        let arr = ['a', 'b', 'c', 'd'];
        arr.splice(2, 1);      // 从索引 2 开始删除 1 个元素
        console.log(arr);      // ['a', 'b', 'd']
        
        1. 插入元素(示例:将 deleteCount 设为 0,然后传入要插入的元素。)
        let arr = ['a', 'b', 'c'];
        arr.splice(1, 0, 'x', 'y');   // 在索引 1 处插入 'x', 'y'
        console.log(arr);              // ['a', 'x', 'y', 'b', 'c']
        
        1. 替换元素(示例:先删除指定数量的元素,再在相同位置插入新元素。删除的数量和插入的数量可以不同。)
        let arr = ['a', 'b', 'c', 'd'];
        arr.splice(1, 2, 'x', 'y', 'z');   // 从索引 1 开始删除 2 个元素,再插入 3 个新元素
        console.log(arr);                   // ['a', 'x', 'y', 'z', 'd']
        
  • for循环遍历数组:

    let result = ''
    for (let i = 0; i < arr.length; i++) {
      const element = arr[i];
      result += element
      result += ','
    }
    console.log(`遍历数组:${result}`)
    
  • forEach()遍历数组(参数index可以省略):

    const foodArr = ['牛肉', '土豆', '玉米']
    foodArr.forEach(function(element, index) {
        console.log(element, index)
    })
    
  • 使用join()方法借助特定字符串,然后将数组元素连接起来,最终返回连接后的字符串:

    // 原材料
    const oldArr = ['牛肉', '土豆', '玉米']
    // 中间不分隔
    console.log(oldArr.join(''))
    // 中间以分号分隔
    console.log(oldArr.join(';'))
    
  • 使用map()方法遍历数组,当处理数据后,返回新的数组(参数index可以省略):

    // 原材料
    const oldArr = ['牛肉', '土豆', '玉米']
    // 处理结果
    const newArr = oldArr.map(function(element, index) {
        // 模拟数据处理的过程
        if(element === '牛肉') {
            return '汉堡包'
        } else if(element === '土豆') {
            return '薯条'
        } else if(element === '玉米') {
            return '爆米花'
        }
        return ''
    })
    console.log(newArr) // ['汉堡包', '薯条', '爆米花']
    
  • 使用filter()方法过滤数组元素,当处理数据后,返回符合条件的新数组(参数index可以省略):

    // 原材料
    const arr = [1, 2, 3, 4, 5, 6]
    // 过滤结果
    const newArr = arr.filter((element, index) => {
        return element > 3
    })
    console.log(newArr) // [4, 5, 6]
    
  • 使用reduce()进行数值累加操作

    // 求和(注意:如果不设置起始值,那么第一次累加时prev=arr[0],current=arr[1])
    const arr = [1, 2, 3, 4, 5, 6]
    const result1 = arr.reduce((prev, current) => {
        return prev + current;
    })
    console.log(result1) // 21
    
    // 求和(注意:如果设置起始值,那么第一次累加时prev=起始值,current=arr[0])
    const result2 = arr.reduce((prev, current) => {
        return prev + current;
    }, 10)
    console.log(result2) // 31
    
    // 加薪 30%,计算总工资(综合案例)
    const info = [
        {
            name: '张三',
            salary: 1000
        },
        {
            name: '李四',
            salary: 2000
        },
        {
            name: '王五',
            salary: 3000
        }
    ]
    const result3 = info.reduce((prev, current) => prev + current.salary * 1.3, 0) // 初始值0是必须存在的,在上面例子中已经说明了作用
    console.log(result3) // 7800
    
  • find() 方法返回数组中符合要求的第一个元素的值。否则返回 undefined。

    const array1 = [5, 12, 8, 130, 44];
    const found = array1.find((element) => element > 10);
    console.log(found); // 12
    
  • findIndex() 方法返回数组中符合要求的第1个元素的索引。若没有找到对应元素则返回 -1。

    const array1 = [5, 12, 8, 130, 44];
    const isLargeNumber = (element) => element > 13;
    console.log(array1.findIndex(isLargeNumber)); // 3
    
  • every() 方法:如果数组内的所有元素都满足要求,那返回值是true,否则是false

    const array1 = [1, 30, 39, 29, 10, 13];
    const isBelowThreshold = (currentValue) => currentValue < 40;
    console.log(array1.every(isBelowThreshold)); // true
    
  • some() 方法:只要数组中有任意一个元素符合要求,那返回值是true,否则是false

    const array = [1, 2, 3, 4, 5];
    const even = (element) => element % 2 === 0;
    console.log(array.some(even)); // true
    
  • concat() 方法用于合并两个或多个数组,最终返回一个新数组。

    const array1 = ["a", "b", "c"];
    const array2 = ["d", "e", "f"];
    const array3 = array1.concat(array2);
    console.log(array3); // ["a", "b", "c", "d", "e", "f"]
    
  • sort() 方法就地对数组的元素进行排序,并返回对相同数组的引用。如果想要不改变原数组的排序方法,可以使用 toSorted()

    const months = ["March", "Jan", "Feb", "Dec"];
    months.sort();
    console.log(months); // ["Dec", "Feb", "Jan", "March"]
    
    const array1 = [1, 30, 4, 21, 100000];
    array1.sort();
    console.log(array1); // [1, 100000, 21, 30, 4]
    
  • reverse() 方法就地反转数组中的元素,并返回同一数组的引用。数组的第一个元素会变成最后一个,数组的最后一个元素变成第一个。换句话说,数组中的元素顺序将被翻转,变为与之前相反的方向。如果要在不改变原始数组的情况下反转数组中的元素,使用 toReversed()

    const array1 = ["one", "two", "three"];
    console.log("array1:", array1); // "array1:" Array ["one", "two", "three"]
    
    const reversed = array1.reverse();
    console.log("reversed:", reversed); // "reversed:" Array ["three", "two", "one"]
    
    // reverse()方法会改变原数组
    console.log("array1:", array1); // "array1:" Array ["three", "two", "one"]
    
  • 用于判断一个数组是否包含某个指定的值,根据情况返回 true 或 false:arr.includes(valueToFind[, fromIndex])

    • 参数:

      • valueToFind:需要查找的元素值,其中查找时使用严格相等(===)比较。
      • fromIndex(可选):从该索引位置开始查找(默认 0)。若为负数,表示从倒数第 abs(fromIndex) 位开始往后查找。
    • 示例:

      // 基本用法
      const arr = [1, 2, 3, 'a', 'b'];
      
      console.log(arr.includes(2));      // true
      console.log(arr.includes(4));      // false
      console.log(arr.includes('a'));    // true
      console.log(arr.includes('A'));    // false(大小写敏感)
      
      // 使用 fromIndex
      const arr = ['apple', 'banana', 'orange'];
      
      console.log(arr.includes('banana', 1));   // true(从索引1开始查)
      console.log(arr.includes('apple', 1));    // false(从索引1开始查,找不到 apple)
      console.log(arr.includes('orange', -1));  // true(-1 表示从倒数第1个,即索引2开始)
      console.log(arr.includes('banana', -2));  // true(-2 表示从倒数第2个,即索引1开始)
      
  • 转换成数据类型:Array.from(liEls)[...liEls](扩展运算符)

    • 使用场景:document.querySelectorAll 返回的是 NodeList 对象,它没有数组的 map 方法。
    • 示例:
      // 1、使用Array.from(liEls)
      function updateTodoList() {
          const liEls = document.querySelectorAll('.todoList li');  // 修正类名
          const todoList = Array.from(liEls).map(element => {       // 转换为数组再 map
              return {
                  content: element.innerText,
                  del: element.classList.contains('del')
              };
          });
          localStorage.setItem('todos', JSON.stringify(todoList));
      }
      
      // 2、使用扩展运算符
      const todoList = [...liEls].map(element => ({ ... }));
      
6.3.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>
</head>
<body>
  <script>
    // 定义空数组
    let emptyArr = []
    console.log(`定义空数组:${emptyArr}`)

    // 定义有值数组
    let arr = [1, '中国', true]
    console.log(`定义有值数组:${arr}`)

    // 从数组中取值,数组下标从0开始
    console.log(`获取数组下标为0的值:${arr[0]}`)

    // 更新数组值
    arr[0] = 2
    console.log(`更新数组值:${arr}`)
    
    // 在数组尾部新增值
    arr.push(100, 'hello world')
    console.log(`在数组尾部新增值:${arr}`)

    // 在数组头部新增值
    arr.unshift(20, 'my world')
    console.log(`在数组头部新增值:${arr}`)

    // 在数组尾部删除值
    arr.pop()
    console.log(`在数组尾部删除值:${arr}`)

    // 在数组头部删除值
    arr.shift()
    console.log(`在数组头部删除值:${arr}`)
    
    // 删除数组中特定下标的数组值
    // splice()方法一共有2个参数,第2个参数非必填,第1个参数代表数组下标,第二个参数代表数量
    // 方法含义:从特定数组下标开始,删除指定数量的元素。如果没有填写第2个参数,那就直接从数组下标删除到最后一个元素
    arr.splice(2, 1)

    // 遍历数组
    let result = ''
    for (let i = 0; i < arr.length; i++) {
      const element = arr[i];
      result += element
      result += ','
    }
    console.log(`遍历数组:${result}`)
  </script>
</body>
</html>
6.3.1.3、页面效果

在这里插入图片描述

6.3.2、对象(Object)
6.3.2.1、概念
  • 含义:支持设置属性和方法
  • 写法:let 变量名 = {属性名: 属性值, 方法: 函数……},如果不写值let 变量名 = {}就是空对象。例如:let obj = {}(空对象)、let obj = {"name":"明快de玄米61","age":18,"isMan":true} (有值对象)
  • 另外一种写法(了解即可):let 变量名 = new Object(属性名: 属性值, 方法: 函数……),例如:let obj = new Object("name":"明快de玄米61","age":18,"isMan":true)
  • 属性名:除非名称中存在特殊符号或者中横线等,一般不使用双引号或者单引号将属性名括起来
  • 从对象中取值:对象变量名.属性名或者对象变量名['属性名'],第1种方式比较常用,第2种方式在使用forin方式遍历对象时会使用到,并且也只能使用方式2
  • 调用对象的方法:对象变量名.方法名()或者对象变量名.方法名(实参列表)
  • 新增对象值、更新对象值:对象变量名.属性名 = 新属性值,对于对象来说,有则更新,无则新增
  • 删除对象值:delete 对象变量名.属性名,示例:delete obj.age
  • 遍历对象:
    // 遍历对象
    for (const key in obj) {
      // 在这里不使用obj.key的原因:key是一个字符串,相当于'name',所以不能使用,因此只能使用这种方法
      let val = obj[key]
      console.log(`属性名:${key},属性值:${val}`)
    }
    
  • 特殊类型null
    • 代表无的意思,如果let a = null,此时a的类型是object
    • null和undefined的区别:
      • null:代表空,啥都没有。例如:console.log(null + 1)的结果是1,原因是数据类型转换之后null是0
      • undefined:代表未定义。例如:console.log(undefined + 1)的结果是NaN,原因是数据类型转换之后undefined是NaN
  • 注意事项:字符串、数值、布尔等基本数据类型也有对应的构造函数,例如字符串对应String,这也被我们也称为包装类型。那就可以解释在部分情况下,我们使用的是基本数据类型,但是依然可以直接调用属性和方法(例如:const str = 'hello'; console.log(str.length)),其实JS底层将基本数据类型自动转换成了包装类型对象,所以我们才能调用包装类型的属性和方法
6.3.2.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>
</head>
<body>
  <script>
    // 定义空对象
    let emptyObj = {}
    console.log(`定义空对象:${JSON.stringify(emptyObj)}`)

    // 定义有值对象
    let obj = {
      name: '明快de玄米61',
      age: 18,
      isMan: true,
      run: function() {
        console.log('调用对象run方法')
      },
      eat: function(food) {
        console.log(`调用对象eat方法,传参:${food}`)
      }
    }
    console.log(`定义有值对象${JSON.stringify(obj)}`)

    // 从对象中取值
    console.log(`从对象中取值-方式1:${obj.name}`)
    console.log(`从对象中取值-方式2:${obj['name']}`)

    // 调用对象的方法
    console.log(`调用对象的方法-不传参-返回值:${obj.run()}`)
    console.log(`调用对象的方法-传参-返回值:${obj.eat('汉堡')}`)

    // 更新对象值
    obj.name = '张三'
    console.log(`更新对象值:${obj.name}`)
    
    // 新增对象值
    obj.address = '湖北省武汉市'
    console.log(`新增对象值:${obj.address}`)

    // 删除对象值
    delete obj.age
    console.log(`删除对象值:${JSON.stringify(obj)}`)

    // 遍历对象
    console.log('遍历对象:')
    for (const key in obj) {
      // 在这里不使用obj.key的原因:key是一个字符串,相当于'name',所以不能使用,因此只能使用这种方法
      let val = obj[key]
      console.log(`属性名:${key},属性值:${val}`)
    }
  </script>
</body>
</html>
6.3.2.3、页面效果

在这里插入图片描述

6.4、类型检测

6.4.1、概念
  • 含义:typeof运算符可以返回被检测的数据类型。
  • 语法:
    • 运算符形式(常用语法):typeof 变量名
    • 函数形式:typeof(变量名)
6.4.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>
</head>
<body>
  <script>
    let num = 1
    console.log(typeof num)

    let str = ''
    console.log(typeof str)

    let bol = true
    console.log(typeof bol)

    let arr = []
    console.log(typeof arr)

    let obj = {}
    console.log(typeof obj)

    console.log(typeof null)

    console.log(typeof undefined)
  </script>
</body>
</html>
6.4.3、页面效果

数组居然也是object类型
在这里插入图片描述

6.5、数据类型转换

6.5.1、数据类型转换的作用

当使用prompt()方法的时候(例如:let age = prompt('您今年多大了')),我们本意想获取数字,但是现在获取的是字符串类型,这分明不是我们想要的结果,如下图:

在这里插入图片描述

以及获取form表单元素时,获取到的输入值都是字符串,但是这可能不是我们需要的类型,所以需要进行数据类型转换,最终得到我们需要的数据类型

6.5.2、显式转换
6.5.2.1、概念
  • 含义:隐式转换是程序语言自动转换的,但是有些时候的转换效果并不是我们想要的,所以就需要显式转换告诉程序语言应该怎么转换
6.5.2.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>
</head>
<body>
  <script>
    // 将字符串转换成数字类型
    console.log(`Number('1') = ${Number('1')}`)

    // 将字符串转换成整数类型(数字类型),但是仅保留数字,并且单位等字符串信息必须在最后面,不能在最前面
    console.log(`parseInt('1.5px') = ${parseInt('1.5px')}`)

    // 将字符串转换成小数类型(数字类型),但是仅保留数字,并且单位等字符串信息必须在最后面,不能在最前面
    console.log(`parseFloat('1.5px') = ${parseFloat('1.5px')}`)

    // 将其他数据类型转换成布尔值。
    // 注意:''、0、undefined、null、false、NaN转换成布尔值后都是flase,其他情况是true
    console.log(`Boolean(undefined) = ${Boolean(undefined)}`)
    console.log(`Boolean('1') = ${Boolean('1')}`)
  </script>
</body>
</html>
6.5.2.3、页面效果

在这里插入图片描述

6.5.3、隐式转换
6.5.3.1、概念
  • 含义:在计算过程中,根据计算符号的不同,可能发生数据类型转换。比如1 + '1',前面的数字1就会被隐式转换成字符串
6.5.3.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>
</head>
<body>
  <script>
    // 将数字1转换成字符串'1'
    // 注意:+加号两边只要有一个是字符串,都会把另外一个数据转换成字符串
    console.log(`'1' + 1 = ${'1' + 1}`)

    // 在字符串前面加一个+,可以将字符串类型转换成数字类型,这个加号其实是正号,做了强制转换
    // 如果字符串不是数字,那结果是NaN
    console.log(`+'1' = ${+'1'}`)

    // 根据加号执行顺序,先执行加法,在执行字符串拼接
    console.log(`1 + 1 + '1' = ${1 + 1 + '1'}`)

    // 除了加法之外的符号,例如-、*、/、%,都会将字符串转换为数字进行计算
    console.log(`2 - '2' = ${2 - '2'}`)
  </script>
</body>
</html>
6.5.3.3、页面效果

在这里插入图片描述

7、JS运算符

7.1、算术运算符

  • 含义:数学运算符也叫做算术运算符,主要包括加、减、乘、除、取余
  • 分类:
    • 加:加法或者拼接符(字符串拼接),例如:console.log(1 + 1)的结果是2,console.log(1 + '1')的结果是字符串11
    • 减:减法,例如:console.log(1 - 1)的结果是0
    • 乘:减法,例如:console.log(1 * 1)的结果是1
    • 除:减法,例如:console.log(1 / 1)的结果是1
    • 取余:减法,例如:console.log(6 % 4)的结果是2,可以用于判断是否可以整除某个数字

7.2、赋值运算符

  • 含义:对变量进行赋值的运算符
  • 分类:
    • =:直接赋值,例如:let a = 1
    • +=:等于= X + Y,例如:let a = 1; a+=1,此时a=2
    • -=:等于= X + Y,例如:let a = 1; a-=1,此时a=0
    • *=:等于= X + Y,例如:let a = 1; a*=1,此时a=1
    • /=:等于= X + Y,例如:let a = 1; a/=1,此时a=1
    • %=:等于= X + Y,例如:let a = 1; a%=1,此时a=0

7.3、自增 / 自减 运算符

  • 含义:进行++或者–,其中++或者–可以放在变量前面,也可以放在变量后面
  • 分类:
    • 变量++:自增1,等价于变量 = 变量 + 1;例如:let a = 1; a++,此时a是2
    • 变量–:自减1,等价于变量 = 变量 - 1;例如:let a = 1; a--,此时a是0
    • ++变量:自增1,和前面不同的地方是“前面是先赋值在自增,这里是先自增,在赋值”,例如:let a = 1, b = 1; a = a++; b = ++b;,此时a是1,b是2
    • –变量:自减1,和前面不同的地方是“前面是先赋值在自减,这里是先自减,在赋值”,例如:let a = 1, b = 1; a = a--; b = --b;,此时a是1,b是0

7.5、三元运算符

  • 语法:条件 ? 当条件为真时执行的代码 : 当条件为假时执行的代码
  • 示例:let a = 3 > 5 ? 1 : 2,由于3>5条件为假,此时a是2

7.6、比较运算符

  • 含义:比较两个数据是否相等、大小
  • 分类:
    • :左边是否大于右边,例如:let a = 3 > 5的结果是false

    • <:左边是否小于右边,例如:let a = 3 < 5的结果是true
    • =:左边是否大于等于右边,例如:let a = 3 >= 5的结果是false

    • <=:左边是否小于等于右边,例如:let a = 3 <= 5的结果是true
    • ==:左右两边的值是否相等,和数据类型无关,例如:let a = 3 == '3'的结果是true
    • ===:左右两边的值和类型是否相等,例如:let a = 3 === '3'的结果是false
    • !=:左右两边的值是否不相等,和数据类型无关,例如:let a = 3 != '3'的结果是false
    • !==:左右两边的值和类型是否不相等,例如:let a = 3 !== '3'的结果是true

7.7、逻辑运算符

  • 含义:逻辑与、或、非,当然也叫做短路与(如果前一个值是false,那后面的就不用计算了,最终结果就是false)、或(如果前一个值是true,那后面的就不用计算了,最终结果就是true)

  • 分类:

    • 逻辑与:两边都是真,结果才是真。例如:let a = true && true的结果是true
    • 逻辑或:有任意一个为真,结果就是真。例如:let a = true || false的结果是true
    • 逻辑非:将结果反过来。例如:let a = !true的结果是false
  • 特殊情况:

    console.log(`3 > 5 && 20 = ${3 > 5 && 20}`) // 结果是false
    console.log(`10 && 20 = ${10 && 20}`) // 结果是20,两边都是真,那么最后一个值作为最终返回值
    

7.8、可选链操作符(?.)

说明: 当操作符前面的元素值为null或者undefined时,后面的就不会执行,常用语axios获取返回值中使用

用法:

import axios from 'axios';

axios.get('/api/user/info')
  .then(res => console.log('请求成功:', res))
  .catch(error => {
    // 安全获取 HTTP 状态码,等同于:error && error.response && error.response.status;
    const statusCode = error?.response?.status;

    // 根据状态码做不同处理
    switch (statusCode) {
      case 401:
        // 未授权:跳转登录页
        console.log('登录状态过期,请重新登录');
        window.location.href = '/login';
        break;
      case 403:
        // 禁止访问:提示权限不足
        console.log('您暂无该操作权限');
        break;
      case 404:
        // 接口不存在:提示资源未找到
        console.log('请求的资源不存在');
        break;
      case 500:
        // 服务器错误:提示系统异常
        console.log('服务器内部错误,请稍后重试');
        break;
      default:
        // 其他状态码/无状态码(如网络错误)
        console.log('请求失败:', error.message);
    }
  });

7.9、运算符优先级

说明: 当根据运算符优先级进行数据处理之后,从左到右依次计算

在这里插入图片描述

8、JS语句

8.1、概念

8.1.1、表达式和语句的区别

在这里插入图片描述

8.1.2、程序三大流程控制语句

在这里插入图片描述

8.2、if

8.2.1、单分支
  • 语法:

    if (条件) {
    	满足条件需要执行的代码
    }
    
  • 使用说明:

    • 当条件执行的结果不是布尔类型时,将隐式转换为布尔类型,相当于执行Boolean()方法
    • 如果大括号中只有一条语句,那么大括号可以省略,但是不提倡这么做
8.2.2、双分支
  • 语法:

    if (条件) {
    	满足条件需要执行的代码
    } else {
    	不满足条件需要执行的代码
    }
    
  • 使用说明:

    • 当条件执行的结果不是布尔类型时,将隐式转换为布尔类型,相当于执行Boolean()方法
8.2.2、多分支
  • 语法:

    if (条件1) {
    	满足条件1需要执行的代码
    } else if (条件2) {
    	满足条件2需要执行的代码
    } else if (条件3) {
    	满足条件3需要执行的代码
    } else {
    	以上所有条件都不满足时需要执行的代码
    }
    
  • 使用说明:

    • 当条件执行的结果不是布尔类型时,将隐式转换为布尔类型,相当于执行Boolean()方法
    • 最后一个else语句不是必须的,可以省略
    • 只要满足任何一个条件,那么下面的条件都不会在判断了

8.3、break、continue

  • 含义:break代表跳出循环,continue代表结束本次循环,然后开始下一次循环
  • 使用位置:常常和switch、while语句一起使用

8.4、switch

  • 语法:
switch (待比较值) {
	case1:
		代码1
		break
	case2:
		代码2
		break
	default:
		代码n
		break
}
  • 使用说明:
    • 每一个case一定要以break结尾,如果满足case1,然后也满足case2,如果case1最后不写break,那么也会执行case2中的代码2
    • 只有case后面的值和待比较值完全相等,包括数据和类型,类似于===判断标准,才能执行对应case中的代码。如果所有case都不满足,那么将执行default中的代码
    • default语句不是必须写的

8.5、while

  • 含义:只要满足循环条件,代码都会重复执行
  • 语法:
while (循环条件) {
	循环体
}
  • 使用注意:当条件执行的结果不是布尔类型时,将隐式转换为布尔类型,相当于隐式执行Boolean()方法

8.6、for

8.6.1、普通for循环
8.6.1.1、概念
  • 含义:经常用于遍历数组
8.6.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>
</head>
<body>
  <script>
    let arr = [1, 2, 3]
    for (let index = 0; index < arr.length; index++) {
      const element = arr[index];
      console.log(element)
    }
  </script>
</body>
</html>
8.6.1.3、页面效果

在这里插入图片描述

8.6.2、forin循环
8.6.2.1、概念
  • 含义:经常用于遍历对象
8.6.2.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>
</head>
<body>
  <script>
    let obj = {
      name: '明快de玄米61',
      age: 18
    }
    for (const key in obj) {
      const val = obj[key];
      console.log(`属性名 = ${key}, 属性值 = ${val}`)
    }
  </script>
</body>
</html>
8.6.2.3、页面效果

在这里插入图片描述

9、JS函数

9.1、概念

  • 作用:执行特定任务的代码块,可以一次编写,多次执行

  • 分类:

    • 无参函数:
    function 函数名() {
    	函数体
    }
    
    • 有参函数:
    // 定义
    function 函数名(形式参数1, 形式参数2, 形式参数3) {
    	函数体
    }
    
    // 使用
    // 注意:实参和形参个数不需要完全一致,如果形参不对应实参,那么形参的值是undefined
    函数名(实际参数1, 实际参数2, 实际参数3)
    
  • 为形参设置默认值的函数:

    // 如果实参数量不够,部分形参没有设置值,那默认值就会生效
    function 函数名(形式参数1, 形式参数2 = '小明', 形式参数3 = 18) {
    	函数体
    }
    
    // 使用
    // 注意:实参和形参个数不需要完全一致,如果形参不对应实参,那么形参的值是undefined
    函数名(实际参数1, 实际参数2, 实际参数3)
    
    • 带返回值的函数
    function 函数名(形式参数1, 形式参数2, 形式参数3) {
    	函数体
    	return XXX
    }
    
    // 使用
    // 注意:实参和形参个数不需要完全一致,如果形参不对应实参,那么形参的值是undefined
    let result = 函数名(实际参数1, 实际参数2, 实际参数3)
    
    // 打印结果
    console.log(result)
    
    • 另外一种函数声明方式—匿名函数:
    // 定义
    let 变量名 = function(形式参数1, 形式参数2, 形式参数3) {
    	函数体
    }
    
    // 使用
    // 注意:实参和形参个数不需要完全一致,如果形参不对应实参,那么形参的值是undefined
    变量名(实际参数1, 实际参数2, 实际参数3)
    
  • 立即执行函数(作用:立即执行函数中是的全局变量只在函数体内有效,所以可以避免多个js文件的全局变量污染)

    // 语法1-无参
    (function(){
      console.log('没有参数')
    })();
    
    // 语法1-有参
    (function(window, document){
      console.log(window)
      console.log(document)
    })(window, document);
    
    // 语法2-无参
    (function(){
      console.log('没有参数')
    }());
    
    // 语法2-有参
    (function(window, document){
      console.log(window)
      console.log(document)
    }(window, document));
    
    • 在对象中如何定义函数:
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>Document</title>
    </head>
    <body>
      <script>
        let obj = {
          name: '明快de玄米61',
          age: 18,
          eat: function() {
            console.log('吃零食')
          },
          run: function(time) {
            console.log(`${time}奔跑`)
          },
          info: function() {
            return this.name + ' ' + this.age
          }
        }
        console.log(obj.eat())
        console.log(obj.run('2025年11月30日'))
        console.log(obj.info())
      </script>
    </body>
    </html>
    

    在这里插入图片描述

9.2、内置函数

9.2.1、数学内置对象—Math

在这里插入图片描述

9.2.2、随机数函数
9.2.2.1、概念
  • Math.random()可以生成0~1(包括0,不包括1)的随机数,例如:0.1443634636
9.2.2.2、案例1—打印随机数
9.2.2.2.1、代码示例
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    console.log(`0~1(包括0,不包括1)的随机数:${Math.random()}`)

    // 生成start~end的随机数
    function generateRandomNum(start, end) {
      return Math.floor(Math.random() * (end - start + 1)) + start
    }
    console.log(`生成5~10的随机数:${generateRandomNum(5, 10)}`)
  </script>
</body>
</html>
9.2.2.2.2、页面效果

在这里插入图片描述

9.2.2.3、案例2—从数组中随机选出一个元素
9.2.2.3.1、代码示例
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <script>
    // 从数组中随机选出一个元素
    let arr = ['赵云', '张飞', '马超', '刘备', '关羽']
    let randomIndex = Math.floor(Math.random() * arr.length)
    document.write(`本次选出的是:${arr[randomIndex]}`) 
  </script>
</body>
</html>
9.2.2.3.2、页面效果

在这里插入图片描述

9.2.2.4、案例3—猜数字
9.2.2.4.1、代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        let start = 1,
            end = 10;
        let randomNum = Math.floor(Math.random() * (end - start + 1)) + start
        console.log(`本次选中的随机数字是:${randomNum}`)
        while (true) {
            let userInput = +prompt(`请输入一个数字(${start}~${end}):`);
            if (userInput > randomNum) {
                alert('数字猜大了,继续猜')
            } else if (userInput < randomNum) {
                alert('数字猜小了,继续猜')
            } else {
                alert('猜对了,程序结束')
                break
            }
        }
    </script>
</body>

</html>
9.2.2.4.2、页面效果

在这里插入图片描述

9.2.2.5、案例4—输出两种格式颜色
9.2.2.5.1、代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <script>
        // 输出两种格式颜色
        document.write(`16进制颜色:${getRandomColor(true)}<br>`)
        document.write(`rgb格式颜色:${getRandomColor(false)}`)

        // 获取随机颜色,输入flag=true返回16进制颜色,输入flag=false返回rgb格式颜色
        function getRandomColor(flag) {
            if (flag) {
                return getHexRandomColor()
            } else {
                return getRgbRandomColor()
            }
        }

        // 输出16进制颜色
        function getHexRandomColor() {
            const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f']
            let result = "#"
            for (let index = 0; index < 6; index++) {
                const randomIndex = Math.floor(Math.random() * arr.length)
                result += arr[randomIndex]
            }
            return result
        }

        // 输出rgb格式颜色
        function getRgbRandomColor() {
            let r = Math.floor(Math.random() * 256)
            let g = Math.floor(Math.random() * 256)
            let b = Math.floor(Math.random() * 256)
            return `rgb(${r}, ${g}, ${b})`
        }
    </script>
</body>

</html>
9.2.2.5.2、页面效果

在这里插入图片描述

10、JS术语

10.1、常用术语

在这里插入图片描述

10.2、环境对象this

10.2.1、概念
  • 含义:函数内部特殊的变量this,代表当前函数运行时所处的环境
  • 作用:this可以帮助我们让代码更加简洁。
  • 判断this指向的粗略规则:谁调用,this就是谁
10.2.2、案例1—普通案例
10.2.2.1、代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <button class="btn">点击我</button>
    <script>
        // this指代window
        alertHello()
        function alertHello() {
            console.log(this)
        }

        // this指代btnDom
        const btnDom = document.querySelector('.btn')
        btnDom.addEventListener('click', function(event) {
            console.log(this)
        })
    </script>
</body>

</html>
10.2.2.2、页面效果

在这里插入图片描述

10.2.3、案例2—页签切换案例
10.2.3.1、代码示例
<!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;
            box-sizing: border-box;
        }

        ul {
            list-style: none;
        }

        a {
            text-decoration: none;      /* 去掉下划线 */
            color: inherit;             /* 继承父级颜色,避免默认蓝/紫 */
            /* 可选:保留手型 cursor: pointer; 其实浏览器默认就是 pointer */
        }

        .goods {
            margin: 50px 100px;
        }

        .goods .tab {
            width: 300px;
            height: 30px;
        }

        .goods .tab ul {
            display: flex;
        }

        .goods .tab li {
            width: 100px;
            height: 30px;
            border: 1px solid #ccc;
            background-color: pink;
            text-align: center;
            line-height: 30px;
            color: black;
            font-family: '楷体';
        }

        .goods .tab li a.active {
            color: red;
        }

        .goods .content div {
            width: 300px;
            height: 100px;
            text-align: center;
            line-height: 100px;
            font-size: 30px;
            display: none;
            background-color: goldenrod;
        }

        .goods .content div.active {
            display: block;
        }

        .goods .content div.remen {
            background-color: rgb(255, 0, 0, 0.5);
        }

        .goods .content div.yifu {
            background-color: rgb(173, 255, 47, 0.5);
        }

        .goods .content div.jiushui {
            background-color: rgb(138, 43, 226, 0.5);
        }
    </style>
</head>

<body>
    <div class="goods">
        <div class="tab">
            <ul>
                <li><a class="active" href="#">热门</a></li>
                <li><a href="#">衣服</a></li>
                <li><a href="#">酒水</a></li>
            </ul>
        </div>
        <div class="content">
            <div class="remen active">热门</div>
            <div class="yifu">衣服</div>
            <div class="jiushui">酒水</div>
        </div>
    </div>
    <script>
        // 启用标签和内容所用的class类
        const ACTIVE_CLASS_NAME = 'active'
        // 遍历所有标签列表
        const tabDomArr = document.querySelectorAll('.goods .tab a')
        for (let index = 0; index < tabDomArr.length; index++) {
            const tabDom = tabDomArr[index]
            tabDom.addEventListener('mouseenter', function() {
                // 1、处理标签
                // 移除其他标签已有效果
                const tabActiveDom = document.querySelector('.goods .tab a.active')
                if(tabActiveDom) tabActiveDom.classList.remove(ACTIVE_CLASS_NAME)
                // 为当前标签添加效果
                this.classList.add(ACTIVE_CLASS_NAME)

                // 2、处理内容
                // 移除其他内容已有效果
                const contentActiveDom = document.querySelector('.goods .content div.active')
                if(contentActiveDom) contentActiveDom.classList.remove(ACTIVE_CLASS_NAME)
                // 为当前内容添加效果
                document.querySelector(`.goods .content div:nth-child(${index + 1})`).classList.add(ACTIVE_CLASS_NAME)
            })
        }
    </script>
</body>

</html>
10.2.3.2、页面效果

在这里插入图片描述

10.2.4、案例3—复选框全选反选案例
10.2.4.1、代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <!-- 表头 -->
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>你好,春天~</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        table {
            width: 500px;
            margin: 50px 100px;
            border-radius: 20px;
            overflow: hidden;
            /* 新增:消除单元格间距 */
            border-collapse: collapse;
        }

        thead {
            background-color: yellowgreen;
        }

        tbody {
            background-color: #ccc;
        }

        input {
            width: 22px;
            height: 22px;
            /* 让复选框与旁边的文本居中对齐 */
            vertical-align: middle;
            /* 可选:增加 margin-right 让复选框和文字间距更舒适 */
            margin-right: 6px;
        }

        th {
            padding: 16px 12px;
            font-weight: 500;
            letter-spacing: 0.5px;
            text-align: left;
            white-space: nowrap;
        }

        td {
            padding: 14px 12px;
            border-bottom: 1px solid #d0e0d0;
            color: #1e3a1e;
        }

        tbody tr:hover {
            background-color: grey;
        }
    </style>
</head>

<body>
    <table>
        <thead>
            <tr>
                <th>
                    <input type="checkbox" class="checkAll"> 全选
                </th>
                <th>商品</th>
                <th>商家</th>
                <th>价格</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>
                    <input type="checkbox" class="check">
                </td>
                <td>小米14手机</td>
                <td>小米</td>
                <td>1999元</td>
            </tr>
            <tr>
                <td>
                    <input type="checkbox" class="check">
                </td>
                <td>SK-II</td>
                <td>铂兰娜</td>
                <td>2020元</td>
            </tr>
            <tr>
                <td>
                    <input type="checkbox" class="check">
                </td>
                <td>牤牛模型车</td>
                <td>牤牛</td>
                <td>176元</td>
            </tr>
        </tbody>
    </table>
    <script>
        const checkAllDom = document.querySelector('.checkAll')
        const checkDomArr = document.querySelectorAll('.check')

        // 全选/取消全选
        checkAllDom.addEventListener('change', function(event) {
            const isChecked = event.target.checked
            checkDomArr.forEach(cb => cb.checked = isChecked);
        })

        // 单个复选框变化时更新全选状态
        for(let index = 0; index < checkDomArr.length; index++) {
            checkDomArr[index].addEventListener('change', function() {
                const allChecked = Array.from(checkDomArr).every(c => c.checked);
                checkAllDom.checked = allChecked
            })
        }
    </script>
</body>

</html>
10.2.4.2、页面效果

在这里插入图片描述

10.3、回调函数

10.3.1、概念
  • 含义:如果将函数A作为参数,然后传给函数B,此时我们称函数A为回调函数
10.3.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>
</head>

<body>
    <script>
        setInterval(fn, 1000)

        function fn() {
            console.log('我是回调函数~')
        }
    </script>
</body>

</html>
10.3.3、页面效果

在这里插入图片描述

10.4、日期对象

10.4.1、概念
  • 创建日期对象:
    • 获取当前日期对象:new Date()
    • 手动指定时间,创建日期对象:new Date('2026-2-22 12:13:00')
  • 获取年、月、日、时、分、秒、星期
    • 获取四位年份:日期对象.getFullYear()
    • 获取月份(取值为0~11。注意:真实月份等于取值+1):日期对象.getMonth() + 1
    • 获取月份中的每一天(不同月份取值也不相同):日期对象.getDate()
    • 获取小时(取值0~23):日期对象.getHours()
    • 获取分钟(取值0~59):日期对象.getMinutes()
    • 获取秒(取值0~59):日期对象.getSeconds()
    • 获取星期(取值为0~6。注意:除了星期日对应0,其他正常对应):日期对象.getDay() !== 0 ? 日期对象.getDay() : 7
  • 快速展示本地时间
    • 日期+时间(取值示例:2026/3/5 12:54:09):日期对象.toLocaleString()
    • 日期(取值示例:2026/3/5):日期对象.toLocaleDateString()
    • 时间(取值示例:12:54:09):日期对象.toLocaleTimeString()
  • 获取时间戳(毫秒数):
    • 获取任意时间时间戳:日期对象.getTime()
    • 获取任意时间时间戳:+日期对象
    • 获取当前时间的时间戳:Date.now()
10.4.2、案例1—功能演示
10.4.2.1、代码示例
<!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;
        }
        .btn {
            position:relative;
            width: 50px;
            height: 50px;
            padding: 10px;
            border: 2px solid black;
            margin: 10px;
            background-color: pink;
            text-align: center;
            line-height: 50px;
            left: 30px;
            top: 1000px;
            cursor: pointer;
        }

        .btn:hover {
            background-color: yellowgreen;
        }
    </style>
</head>

<body>
    <script>
        console.log('1、创建日期对象:')
        console.log('    - 获取当前日期对象:new Date()')
        console.log(new Date())
        console.log('    - 手动指定时间,创建日期对象:new Date("2026-2-22 12:13:00")')
        console.log(new Date("2026-2-22 12:13:00"))

        console.log('2、获取年、月、日、时、分、秒、星期')
        console.log('    - 获取四位年份:日期对象.getFullYear()')
        const time = new Date()
        console.log(time.getFullYear())
        console.log('    - 获取月份(取值为0~11。注意:真实月份等于取值+1):日期对象.getMonth() + 1')
        console.log(time.getMonth() + 1)
        console.log('    - 获取月份中的每一天(不同月份取值也不相同):日期对象.getDate()')
        console.log(time.getDate())
        console.log('    - 获取小时(取值0~23):日期对象.getHours()')
        console.log(time.getHours())
        console.log('    - 获取分钟(取值0~59):日期对象.getMinutes()')
        console.log(time.getMinutes())
        console.log('    - 获取秒(取值0~59):日期对象.getSeconds()')
        console.log(time.getSeconds())
        console.log('    - 获取星期(取值为0~6。注意:除了星期日对应0,其他正常对应):日期对象.getDay() !== 0 ? 日期对象.getDay() : 7')
        console.log(time.getDay() !== 0 ? time.getDay() : 7)

        console.log('3、快速展示本地时间')
        console.log('    - 日期+时间(取值示例:2026/3/5 12:54:09):日期对象.toLocaleString()')
        console.log(time.toLocaleString())
        console.log('    - 日期(取值示例:2026/3/5):日期对象.toLocaleDateString()')
        console.log(time.toLocaleDateString())
        console.log('    - 时间(取值示例:12:54:09):日期对象.toLocaleTimeString()')
        console.log(time.toLocaleTimeString())

        console.log('4、获取时间戳(毫秒数):')
        console.log('    - 获取任意时间时间戳:日期对象.getTime()')
        console.log(time.getTime())
        console.log('    - 获取任意时间时间戳:+日期对象')
        console.log(+time)
        console.log('    - 获取当前时间的时间戳:Date.now()')
        console.log(Date.now())
    </script>
</body>

</html>
10.4.2.2、页面效果

在这里插入图片描述

10.4.3、案例2—开奖倒计时
10.4.3.1、代码示例
<!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>
        
    </style>
</head>

<body>
    <h1 class="info"></h1>
    <script>
        // 倒计时
        const deadline = new Date('2026-12-18 14:10:00')
        // 当前时间
        const now = new Date()
        // 剩余秒数
        let remainSecond = parseInt((deadline.getTime() - now.getTime()) / 1000)
        // 使用定时器完成倒计时
        let timer = setInterval(function(){
            if(remainSecond === 0) {
                // 清除定时器
                clearInterval(timer)
                document.querySelector('.info').innerText = `
                    开奖时间是${deadline.toLocaleString()},现在开奖,恭喜你中奖了~
                `
            } else {
                const day = parseInt(remainSecond / 60 / 60 / 24 % 365)
                const hour = parseInt(remainSecond / 60 / 60 % 24)
                const minute = parseInt(remainSecond / 60 % 60)
                const second = parseInt(remainSecond % 60)
                document.querySelector('.info').innerText = `
                    开奖时间是${deadline.toLocaleString()},现在还有${day}${hour}小时${minute}${second}`
                remainSecond--
            }
        }, 1000)
    </script>
</body>

</html>
10.4.3.2、页面效果

在这里插入图片描述

11、DOM

11.1、概念

  • 作用:使用JS语法操作网页标签和浏览器
  • 分类:DOM(文档对象模型,即操作网页内容使用的)、BOM(浏览器对象模型,即操作浏览器执行动作使用的)
  • DOM和BOM的关系:
    在这里插入图片描述
  • DOM树:将网页标签通过树状结构表示出来,我们称之为文档树或者DOM树
    在这里插入图片描述
  • DOM对象:
    • 概念:html标签对应的JS对象。在DOM对象上可以找到标签的所有属性,然后修改DOM对象的属性也会自动映射到标签身上
    • 核心思想:将网页内容当做对象来处理
    • document对象:这是DOM中提供的一个对象,网页所有内容都在document对象里面

11.2、查找、增加、删除Dom节点

11.2.1、概念
  • Dom节点:Dom树中每一个内容都可以被称之为节点
  • 节点类型:
    • 元素节点:所有的标签都是元素节点,例如html、body、div等。注意:我们基本只会关注这种节点
    • 属性节点(了解概念即可):元素属性,例如src
    • 文本节点(了解概念即可):文本内容
    • 其他节点(了解概念即可)
      在这里插入图片描述
11.2.2、获取DOM对象
11.2.2.1、概念
  • 语法:
    • 查找匹配的第1个元素:document.querySelector(CSS选择器)。示例:const box = document.querySelector('.box ul li')。说明:使用该方法得到的是第1个符合要求的DOM对象
    • 查找匹配的全部元素(数组):document.querySelectorAll(CSS选择器)。示例:const liArr = document.querySelectorAll('.box ul li')。说明:使用该方法得到的是一个DOM对象数组
      • 注意点1: 通过querySelectorAll()方法最终得到的数组经过了改造,拥有长度和索引号,但是没有pop()push()等数组方法,可以通过for循环得到数组中的DOM对象
      • 注意点2: querySelector()、querySelectorAll()不仅可以被document元素调用,也可以普通Dom元素调用,例如:
        const noteEl = document.createElement('div')
        noteEl.innerHTML = `
            <div class="function">
                <button class="btn editBtn"><i class="fas fa-edit"></i></button>
                <button class="btn deleteBtn"><i class="fas fa-trash-alt"></i></button>
            </div>
            <div class="markdown ${text ? '' : 'hidden'}"></div>
            <textarea class="${text ? 'hidden' : ''}">${text}</textarea>
        `
        noteEl.className = 'note'
        notesEl.appendChild(noteEl)
        
        // 学习点:从Dom节点中可以获取里面的元素,不一定非要是document
        const editBtnEl = noteEl.querySelector('.editBtn');
        const deleteBtnEl = noteEl.querySelector('.deleteBtn');
        const markdownEl = noteEl.querySelector('.markdown');
        const textareaEl = noteEl.querySelector('textarea');
        
        markdownEl.innerHTML = marked(text);
        
        // 绑定事件
        editBtnEl.addEventListener('click', () => {
            markdownEl.classList.toggle('hidden');
            textareaEl.classList.toggle('hidden');
        })
        deleteBtnEl.addEventListener('click', () => {
            noteEl.remove();
            addToLocalStorage();
        })
        textareaEl.addEventListener('input', () => {
            markdownEl.innerText = marked(textareaEl.value);
            addToLocalStorage();
        })
        
    • 查找父节点(不存在父节点时结果为null):Dom节点对象.parentNode
    • 查找子节点(数组):Dom节点对象.children
    • 查找上一个兄弟节点(不存在上一个兄弟节点时结果为null):Dom节点对象.previousElementSibling
    • 查找上一个兄弟节点(不存在上一个兄弟节点时结果为null):Dom节点对象.nextElementSibling
  • 其他语法(了解即可,不在使用):
    在这里插入图片描述
11.2.2.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>
</head>
<body>
    <div class="box">
        <ul>
            <li>hello</li>
            <li>你好</li>
            <li>Ni Hao</li>
        </ul>
    </div>
    <script>
        console.log('————————查找单个节点——————————')
        const liDom = document.querySelector('.box ul li')
        console.dir(liDom)

        console.log('————————查找节点数组——————————')
        const liDomArr = document.querySelectorAll('.box ul li')
        console.dir(liDomArr)

        console.log('————————查找父节点(不存在父节点时结果为null)——————————')
        const firstLiDom = document.querySelector('ul li:nth-child(1)')
        console.dir(firstLiDom.parentNode)

        console.log('————————查找子节点——————————')
        const ulDom = document.querySelector('ul')
        console.dir(ulDom.children)

        console.log('————————查找上一个兄弟节点(不存在上一个兄弟节点时结果为null)——————————')
        const secondLiDom = document.querySelector('ul li:nth-child(2)')
        console.dir(secondLiDom.previousElementSibling)

        console.log('————————查找上一个兄弟节点(不存在上一个兄弟节点时结果为null)——————————')
        console.dir(secondLiDom.nextElementSibling)
    </script>
</body>

</html>
11.2.2.3、页面效果

在这里插入图片描述

11.2.3、创建、追加、克隆、删除节点
11.2.3.1、概念
  • 语法:
    • 创建节点:document.createElement('标签名称'),例如document.createElement('li')
    • 追加节点:
      • 在父元素最后面追加一个节点:父元素.appendChild(新Dom节点)
      • 在父元素最后追加多个节点或字符串:父元素.append(新Dom节点, '一些文本', 其他新的Dom节点);
      • 在父元素中指定子元素前面追加节点:父元素.insertBefore(新Dom元素, 指定子Dom元素)
    • 克隆节点:Dom元素.cloneNode(是否深度克隆【非必填】)
      • 参数:是否深度克隆,该参数非必填,默认是false。true:代表克隆时会包括后代节点,也就是深克隆。false:和true相关,代表克隆时不会包括后代节点,也就是浅客隆
    • 删除节点:
      • 方式1:父元素.removeChild(需要删除的元素本身)
        • 注意:必须通过父元素才能删除子元素,如果不存在父子关系,那么删除失败
      • 方式2:当前元素.remove()
        • 注意:remove() 是元素自身的方法,直接在该元素上调用,它会将自己从其父节点中移除。
11.2.3.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>
</head>
<body>
    <div class="box">
        <ul>
            <li>hello</li>
            <li>你好</li>
            <li>Ni Hao</li>
        </ul>
    </div>
    <script>
        const ulDom = document.querySelector('ul')
        const liDomArr = document.querySelectorAll('ul li')

        console.log('————————创建节点——————————')
        const newLiDom1 = document.createElement('li')
        newLiDom1.innerText = '萨瓦迪卡'

        console.log('————————追加节点——————————')
        // 在父元素最后面追加节点
        ulDom.appendChild(newLiDom1)
        // 在父元素中指定子元素前面追加节点
        const newLiDom2 = document.createElement('li')
        newLiDom2.innerText = 'HaHa'
        ulDom.insertBefore(newLiDom2, liDomArr[0])

        console.log('————————克隆节点——————————')
        // 浅克隆
        console.dir(liDomArr[2].cloneNode())
        // 深克隆
        console.dir(liDomArr[2].cloneNode(true))

        console.log('————————删除节点——————————')
        console.dir(ulDom.removeChild(liDomArr[0]))
    </script>
</body>

</html>
11.2.3.3、页面效果

在这里插入图片描述

11.2.4、综合案例—学员录入表

代码示例:

<!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;
        }
        
        ul {
            list-style: none;
        }

        a {
            text-decoration: none;      /* 去掉下划线 */
            color: inherit;             /* 继承父级颜色,避免默认蓝/紫 */
            /* 可选:保留手型 cursor: pointer; 其实浏览器默认就是 pointer */
        }

        table {
            width: 800px;
            margin: 0 auto;
            /* 新增:消除单元格间距 */
            border-collapse: collapse;
        }

        thead {
            background-color: yellowgreen;
        }

        tbody {
            background-color: #ccc;
        }
        
        th {
            padding: 16px 12px;
            font-weight: 500;
            letter-spacing: 0.5px;
            text-align: center;
            white-space: nowrap;
            border: 1px solid #d0e0d0;
        }

        td {
            padding: 14px 12px;
            border: 1px solid #d0e0d0;
            color: #1e3a1e;
        }

        .title {
            margin: 30px auto;
            text-align: center;
        }

        .inputRegion {
            text-align: center;
        }

        .inputRegion button {
            background-color: rgb(64, 224, 208, 0.5);
            border: 0;
            padding: 3px 10px;
            margin-left: 10px;
            border-radius: 5px;
            cursor: pointer;
        }

        .inputRegion button:hover {
            background: rgb(64, 224, 208);
        }

        .showInfo a {
            padding: 10px;
            background-color: #555;
            color: white;
            border-radius: 20%;
        }

        .showInfo a:hover {
            background-color: black;
        }
    </style>
</head>
<body>
    <h2 class="title">新增学员</h2>
    <form class="inputRegion" autocomplete="off">
        姓名:<input type="text" name="name" class="name">
        年龄:<input type="text" name="age" class="age">
        性别:
        <select name="gender" class="gender">
            <option value=""></option>
            <option value=""></option>
        </select>
        薪资:<input type="text" name="salary" class="salary">
        就业城市:
        <select name="city" class="city">
            <option value="北京">北京</option>
            <option value="上海">上海</option>
        </select>
        <button type="submit">录入</button>
    </form>
    <h2 class="title">就业榜</h2>
    <table class="showInfo">
        <thead>
            <tr>
                <th>学号</th>
                <th>姓名</th>
                <th>年龄</th>
                <th>性别</th>
                <th>薪资</th>
                <th>就业城市</th>
                <th>操作</th>
            </tr>
        </thead>
        <tbody></tbody>
    </table>
    <script>
        const tbodyDom = document.querySelector('.showInfo tbody')
        const formDom = document.querySelector('.inputRegion')
        const nameDom = document.querySelector('.inputRegion .name')
        const ageDom = document.querySelector('.inputRegion .age')
        const genderDom = document.querySelector('.inputRegion .gender')
        const salaryDom = document.querySelector('.inputRegion .salary')
        const cityDom = document.querySelector('.inputRegion .city')
        const inputDomArr = document.querySelectorAll('.inputRegion [name]')
        const infoArr = []

        // 监听表单提交操作
        formDom.addEventListener('submit', function(event) {
            // 阻止默认提交行为
            event.preventDefault()
            
            // 校验参数完整性
            for(let index = 0; index < inputDomArr.length; index++) {
                if(!inputDomArr[index].value) {
                    alert('请将数据填写完整~')
                    return
                }
            }

            // 创建对象
            const infoObj = {
                'no': Date.now(),
                'name': nameDom.value,
                'age': ageDom.value,
                'gender': genderDom.value,
                'salary': salaryDom.value,
                'city': cityDom.value
            }
            
            // 将对象添加到数组中
            infoArr.push(infoObj)

            // 重置表单
            this.reset()

            // 重新渲染数据
            resetTable()
        })

        // 监听删除操作
        tbodyDom.addEventListener('click', function(event) {
            if(event.target.tagName === 'A' && event.target.dataset && event.target.dataset.index) {
                const info = infoArr[+event.target.dataset.index]
                const answer = confirm(`您确定要删除【姓名:${info.name}】吗?`)
                if(answer) {
                    // 删除数据
                    infoArr.splice(+event.target.dataset.index, 1)

                    // 重新渲染数据
                    resetTable()
                }
            }
        })

        function resetTable() {
            // 清空原有数据
            tbodyDom.innerHTML = ''

            // 展示新数据
            for(let index = 0; index < infoArr.length; index++) {
                const newTrDom = document.createElement('tr')
                newTrDom.innerHTML = `
                    <td>${infoArr[index].no}</td>
                    <td>${infoArr[index].name}</td>
                    <td>${infoArr[index].age}</td>
                    <td>${infoArr[index].gender}</td>
                    <td>${infoArr[index].salary}</td>
                    <td>${infoArr[index].city}</td>
                    <td>
                        <a href="#" data-index="${index}">删除</a>
                    </td>
                `
                tbodyDom.appendChild(newTrDom)
            }
        }
    </script>
</body>

</html>

页面效果:

在这里插入图片描述

11.3、操作元素内容

11.3.1、innerText属性
11.3.1.1、概念
  • 语法:
    • innerText: 将文本内容添加/更新到任意标签位置,只会显示纯文本,不解析标签。也可用于获取标签文本内容
      在这里插入图片描述
11.3.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>
</head>
<body>
    <h2>当前网站已经迁移到新网址,<span style="color: red;"><span class="second">10</span>s</span>后将跳转到新网址</h2>
    <script>
        // 获取显示倒计时的Dom元素
        const secondDom = document.querySelector('.second')
        // 倒计时
        let countdownSecond = 10
        // 定时器
        let timer = setInterval(function(){
            // 每隔1s倒计时减1
            countdownSecond--
            /*
                情况1:当倒计时不等于0,需要改变显示倒计时的元素内容
                情况2:当倒计时等于0,停止定时器,跳转到新网址
            */
            if(countdownSecond === 0) {
                clearInterval(timer)
                location.href = 'https://www.baidu.com'
            } else {
                secondDom.innerText = countdownSecond + ''
            }
        }, 1000)
    </script>
</body>
</html>
11.3.1.3、页面效果

在这里插入图片描述

11.3.2、innerHtml属性
11.3.2.1、概念
  • 语法:
    • innerHtml: 将文本内容添加/更新到任意标签位置,会解析html标签,多标签建议使用模板字符串。也可用于获取标签html内容
      在这里插入图片描述
11.3.2.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>
</head>
<body>
    <!-- 模拟数据加载的效果 -->
    <span class="loading"></span>
    <!-- 展示数据加载的结果 -->
    <div>
        <ul></ul>
    </div>
    <script>
        // 模拟数据加载的效果
        const loadingDom = document.querySelector('.loading')
        loadingDom.innerText = '数据加载中,请耐心等待~'
        
        // 模拟数据请求的效果
        setTimeout(function() {
            // 加载完成,清空加载效果
            loadingDom.innerText = ''
            // 展示数据加载结果
            const likeArr = ['抽烟', '喝酒', '烫头']
            const ulDom = document.querySelector('ul')
            // 使用模板字符串
            likeArr.forEach(function(like) {
                const liStr = `
                    <li> ${like} </li>
                `
                ulDom.innerHTML += liStr
            });
        }, 1000)
    </script>
</body>
</html>
11.3.2.3、页面效果

在这里插入图片描述

11.4、操作元素属性

11.4.1、操作元素常用属性
11.4.1.1、概念
  • 语法:
    • 概念:通过JS可以设置/修改标签元素属性,例如常见属性:src、href、title、alt等
    • 写法:Dom对象.属性 = 值
  • 举例:
    在这里插入图片描述
11.4.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>
</head>
<body>
    <img style="width: 300px;" src="big.png" title="大图片">
    <script>
        // 3秒之后,将改变图片路径
        setTimeout(function(){
            const imgDom = document.querySelector('img')
            imgDom.src = '刘亦菲头像.jpg'
            imgDom.title = '刘亦菲头像'
        }, 3000)
    </script>
</body>
</html>
11.4.1.3、页面效果

在这里插入图片描述

11.4.2、操作元素样式属性
11.4.2.1、概念
  • 语法:通过style属性操作样式属性,包括设置、删除、获取等操作
  • 写法:Dom对象.style.样式属性 = 值
  • 注意:
    • 样式属性存在连接符:需要转换成小驼峰命名法,例如:将background-color改成backgroundColor
    • 赋值添加单位:例如为字体大小赋值,记得添加px单位
  • 举例:
    在这里插入图片描述
11.4.2.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>
</head>
<body>
    <div style="width: 300px;height: 300px;background-color: blueviolet;color: red; font-size: 30px;">你好</div>
    <script>
        // 1秒之后,将随机改变盒子颜色、文字颜色
        setInterval(function(){
            const divDom = document.querySelector('div')
            // 单个单词情况:直接使用即可
            divDom.style.color = getHexRandomColor()
            // 多单词情况:使用小驼峰命名法,例如将background-color改成backgroundColor
            divDom.style.backgroundColor = getHexRandomColor()
            // 多属性值:支持多属性值的情况
            divDom.style.border = '5px solid black'
        }, 1000)

        // 输出16进制颜色
        function getHexRandomColor() {
            const arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 'a', 'b', 'c', 'd', 'e', 'f']
            let result = "#"
            for (let index = 0; index < 6; index++) {
                const randomIndex = Math.floor(Math.random() * arr.length)
                result += arr[randomIndex]
            }
            return result
        }
    </script>
</body>
</html>
11.4.2.3、页面效果

在这里插入图片描述

11.4.3、操作类名(class)属性
11.4.3.1、概念
  • 方案1:直接设置class属性值(使用className直接赋值)
    • 作用:有些情况下,css样式变化比较复杂,如果使用Dom元素.style.XXX一点点操作,那太麻烦了,所以有些时候也会设置class属性,从而完成一次修改多个style标签的目的
    • 注意:
      • 1、className用途:由于class属性名称是关键字,所以不能直接使用Dom元素.class设置class属性值,因此延伸出了className用于操作class属性值
      • 2、替换class属性值:直接赋值就是替换操作,如果想留下原来的class属性值,那就需要执行追加操作吧
    • 用法:Dom元素.className = 'XXX'
  • 方案2:通过数组方式来操作class属性(使用classList进行追加、删除、切换类名操作)
    • 作用:如果Dom元素的class属性存在值,那么上面直接替换class属性值的方案就不太合适了,但是可以使用classList方案
    • 用法:
      • 追加类:Dom元素.classList.add('类名')
      • 删除类:Dom元素.classList.remove('类名')
      • 切换类(有则删除,无则添加):Dom元素.classList.toggle('类名')
11.4.3.2、方案1:直接设置class属性值(使用className直接赋值)
11.4.3.2.1、代码示例
<!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>
        div {
            width: 300px;
            height: 300px;
            background-color: pink;
            border: 1px solid black;
        }

        .active {
            background-color: greenyellow;
            border: 1px solid red;
        }
    </style>
</head>
<body>
    <div></div>
    <script>
        // 1秒之后,将改变盒子颜色、边框颜色
        setTimeout(function(){
            const divDom = document.querySelector('div')
            divDom.className = 'active'
        }, 2000)
    </script>
</body>
</html>
11.4.3.2.2、页面效果

在这里插入图片描述

11.4.3.3、方案2:通过数组方式来操作class属性(使用classList进行追加、删除、切换类名、判断是否包含类名的操作)
11.4.3.3.1、概念
// 获取元素的 classList 对象
const classList = element.classList;

// 追加类名(可多个)
element.classList.add('class1', 'class2', ...);

// 删除类名(可多个)
element.classList.remove('class1', 'class2', ...);

// 切换类名:有则删除,无则添加,返回布尔值表示切换后是否存在
element.classList.toggle('className', [force]); // 可选 force 参数强制添加/删除

// 判断是否包含某个类名,返回 true/false
element.classList.contains('className');

// 替换类名(较新方法)
element.classList.replace('oldClass', 'newClass');
11.4.3.3.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>
        .box {
            width: 300px;
            height: 300px;
            background-color: pink;
            border: 1px solid black;
        }

        .active {
            background-color: greenyellow;
            border: 1px solid red;
        }
    </style>
</head>
<body>
    <div class="box"></div>
    <script>
        // 1秒之后,将改变盒子颜色、边框颜色
        setTimeout(function(){
            const divDom = document.querySelector('div.box')
            divDom.classList.add('active')
        }, 2000)
    </script>
</body>
</html>
11.4.3.3.3、页面效果

在这里插入图片描述

11.4.4、操作表单元素属性
11.4.4.1、概念
  • 语法:表单元素的部分属性不太一样,所以特殊讲一下
  • 用法:
    • 字符串类型:
      • value:可以用于获取文本框(type=text、textarea)、密码框(type=password)中的文本,例如:document.querySelector('.text').value
      • text:可以获取select下拉列表某个选项的文本:document.querySelector('.select option:nth-child(2)').text
      • type:获取表单元素类型,这个就是获取普通属性的用法了,不在赘述
    • 布尔值类型:
      • checked:对于input框的type类型是radiocheckbox类型的情况,用于判断选中状态
      • selected:对于select下拉列表,可以用于判断option元素按钮的选中状态
11.4.4.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>
</head>
<body>
    <input class="text" type="text" value="小王">
    <br>
    <input class="password" type="password" value="123456">
    <br>
    <input class="checkbox1" type="checkbox" checked>喝酒
    <br>
    <input class="checkbox2" type="checkbox">烫头
    <br>
    <input class="radio" type="radio"><br>
    <select class="select" name="like">
        <option value="apple" selected>苹果</option>
        <option value="banana">香蕉</option>
        <option value="orange">橘子</option>
    </select>
    <br>
    <button class="button">按钮</button>
    <script>
        // 1、获取值、选中状态、下拉列表选择情况、按钮置灰状态
        console.log('1、初次获取值、选中状态、下拉列表选择情况、按钮置灰状态')
        showInfo()

        // 2、设置值、选中状态、下拉列表选择情况、按钮置灰状态
        console.log('————————————————————————————————————')
        document.querySelector('.text').value = '小李'
        document.querySelector('.password').value = '654321'
        document.querySelector('.checkbox1').checked = false
        document.querySelector('.checkbox2').checked = true
        document.querySelector('.radio').checked = false
        document.querySelector('.select option:nth-child(2)').selected = true
        document.querySelector('.button').disabled = true
        console.log('2、设置值、选中状态、下拉列表选择情况、按钮置灰状态后,再次获取值、选中状态、下拉列表选择情况')
        showInfo()
        
        function showInfo() {
            console.log('文本框:', document.querySelector('.text').value)
            console.log('密码框:', document.querySelector('.password').value)
            console.log('复选框1选中状态:', document.querySelector('.checkbox1').checked)
            console.log('复选框2选中状态:', document.querySelector('.checkbox2').checked)
            console.log('单选框选中状态:', document.querySelector('.radio').checked)
            console.log('下拉列表选项1选中状态:', document.querySelector('.select option:nth-child(1)').selected)
            console.log('下拉列表选项2选中状态:', document.querySelector('.select option:nth-child(2)').selected)
            console.log('下拉列表选项3选中状态:', document.querySelector('.select option:nth-child(3)').selected)
            console.log('下拉列表选中value:', document.querySelector('.select').value)
            console.log('按钮不可用状态:', document.querySelector('.button').disabled)
        }
    </script>
</body>
</html>
11.4.4.3、页面效果

在这里插入图片描述

11.4.5、操作自定义元素属性
11.4.5.1、概念
  • 语法:
    • 标准属性:标签自带的属性,比如class、id、title等
    • 自定义属性:在html5中推出了自定义属性,在标签上一律以data-XXX开头,通过DOM对象.dataset.XXX获取属性值
  • 注意:如果data-后面是通过-拼接的多个单,那就需要使用小驼峰命名法取值
11.4.5.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>
</head>
<body>
    <div data-id="1" data-name="机器人" data-bg-color="red"></div>
    <script>
        const divDom = document.querySelector('div')
        console.dir(divDom.dataset)
        console.dir(divDom.dataset.id)
        console.dir(divDom.dataset.name)
        console.dir(divDom.dataset.bgColor)
    </script>
</body>
</html>
11.4.5.3、页面效果

在这里插入图片描述

11.5、事件监听

11.5.1、使用方法
11.5.1.1、概念
  • 概念:当监听到Dom / Bom(例如window对象)对象上发生了一些事件(例如click点击、resize改变窗口大小等),就会触发函数执行,这就是事件监听
  • 语法:元素对象.addEventListener('事件类型', 函数),含义解释如下:
    • 元素对象:事件源头,可能是Dom元素,也可能是Bom元素
    • 事件类型:也就是触发方式,比如鼠标单击click、鼠标经过mouseover等
    • 函数:当监听到事件发生后,便会执行这个函数。其中函数中可以添加参数event,event对象中记录着事件信息,包括事件类型、鼠标点击位置等
11.5.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>
        .advertisement {
            position: relative;
            width: 500px;
            height: 200px;
            background-color: pink;
            margin: 100px auto;
            text-align: center;
            line-height: 200px; 
            font-size: 50px;
            font-weight: 700;
        }

        .advertisement .close {
            position: absolute;
            right: 10px;
            top: 10px;
            width: 30px;
            height: 30px;
            background-color: black;
            border-radius: 100%;
            text-align: center;
            line-height: 30px;
            font-size: 15px;
            color: white;
            user-select: none;
            cursor: pointer;
            transition: all 0.5s;
        }

        .advertisement .close:hover {
            background-color: white;
            color: red;
            font-size: 20px;
        }
    </style>
</head>

<body>
    <div class="advertisement">
        我是广告
        <div class="close">X</div>
    </div>
    <script>
        // 需要关闭的大盒子
        const advertisementDom = document.querySelector('.advertisement')
        // 事件源
        const closeDom = document.querySelector('.advertisement .close')
        // 添加click监听时间
        closeDom.addEventListener('click', function(event) {
            // 关闭大盒子
            console.dir(event)
            advertisementDom.style.display = 'none'
        })
    </script>
</body>

</html>
11.5.1.3、页面效果

点击广告右上角的关闭按钮后,广告关闭
在这里插入图片描述

11.5.2、事件监听版本

在这里插入图片描述

11.5.3、事件类型
11.5.3.1、概念
  • 鼠标事件(点击鼠标触发):
    • click:鼠标点击
    • dblclick:鼠标双击
    • mouseenter:鼠标指针进入元素时触发1次(不冒泡,进入子元素不会重复触发父元素的进入事件)
    • mouseleave:鼠标指针离开元素时触发1次(不冒泡,进入子元素不会重复触发父元素的进入事件)
    • mouseover(不推荐使用):鼠标指针进入元素时触发1次(mouseover。会冒泡,进入子元素也会触发父元素的 mouseover)。注意:如果目标元素含有子元素,那么进出子元素也会触发父元素的进入操作,所以可能就触发多次触发操作,建议不要使用
    • mouseout(不推荐使用):鼠标指针离开元素时触发1次(mouseout。会冒泡,进入子元素也会触发父元素的 mouseout)
    • mousemove:鼠标指针在元素内移动时持续触发(mousemove。会冒泡,进入子元素也会触发父元素的mousemove。 常用于实时跟踪鼠标位置)。注意:在计算鼠标位置时需要特别注意,不能直接使用offsetX和offsetY,可以看下示例
  • 文本事件:
    • input:用户输入事件。发生时机:每当元素的值发生变化时立即触发。
    • change:对于文本输入框,当元素的值被修改且失去焦点(blur)时触发;
  • 焦点事件(表单控件获得/失去光标):
    • focus:获得焦点
    • blur:失去焦点
  • 键盘事件(敲击键盘触发):
    • keydown:键盘按下
    • keyup:键盘抬起
  • 单选框/复选框事件
    • change:对于单选按钮、复选框、下拉列表等,当用户直接选择新选项时立即触发(无需失去焦点)。
  • 页面加载事件(一般用于window对象、document对象,也支持普通Dom元素使用):
    • load:当全部资源加载完成时触发
    • DOMContentLoaded:当HTML标签加载完成时触发
    • scroll:页面滚动事件
11.5.3.2、案例1—多事件实测

代码示例:

<!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>
        div {
            width: 300px;
            height: 300px;
            margin-bottom: 30px;
            background-color: pink;
            text-align: left;
            line-height: 20px;
            overflow: auto;
        }
    </style>
</head>

<body>
    <div class="cursor">
        <h1>鼠标事件</h1>
        <p>鼠标点击(click)</p>
        <p>鼠标双击(dblclick)</p>
        <p>鼠标指针进入元素时触发1次(mouseenter。不冒泡,进入子元素不会重复触发父元素的进入事件)</p>
        <p>鼠标指针离开元素时触发1次(mouseleave。不冒泡,进入子元素不会重复触发父元素的进入事件)</p>
        <p>鼠标指针进入元素时触发1次(mouseover。会冒泡,进入子元素也会触发父元素的 mouseover)</p>
        <p>鼠标指针离开元素时触发1次(mouseout。会冒泡,进入子元素也会触发父元素的 mouseout)</p>
        <p>鼠标指针在元素内移动时持续触发(mousemove。会冒泡,进入子元素也会触发父元素的mousemove。 常用于实时跟踪鼠标位置)</p>
    </div>
    <div class="text">
        <h1>文本事件</h1>
        <p>用户输入事件</p>
        <input type="text" placeholder="用户输入事件"/>
    </div>
    <div class="focus">
        <h1>焦点事件</h1>
        <p>获得焦点(focus)</p>
        <p>失去焦点(blur)</p>
        <input type="text" placeholder="焦点事件"/>
    </div>
    <div class="keyboard">
        <h1>键盘事件</h1>
        <p>键盘按下(keydown)</p>
        <p>键盘抬起(keyup)</p>
        <input type="text" placeholder="键盘事件"/>
    </div>
    <div class="check">
        <h1>单选框/复选框事件</h1>
        <p>当选择改变时触发(change)</p>
        <input type="radio" />爱吃苹果
        <input type="checkbox">喜欢梨子
    </div>
    <div class="load">
        <h1 style="font-size: 20px;">页面加载事件(一般用于window对象、document对象,也支持普通Dom元素使用)</h1>
        <p>当全部资源加载完成时触发(load)</p>
        <p>当HTML标签加载完成时触发(DOMContentLoaded)</p>
        <p>页面滚动事件(scroll)</p>
    </div>
    <script>
        // 鼠标事件
        const cursorDom = document.querySelector('.cursor')
        cursorDom.addEventListener('click', function(event) {
            console.log(`用户单击鼠标,在盒子内的坐标:${event.offsetX} ${event.offsetY}`)
        })
        cursorDom.addEventListener('dblclick', function() {
            console.log(`用户双击鼠标`)
        })
        cursorDom.addEventListener('mouseenter', function() {
            console.log(`鼠标指针进入元素时触发1次(mouseenter。不冒泡,进入子元素不会重复触发父元素的进入事件)`)
        })
        cursorDom.addEventListener('mouseleave', function() {
            console.log(`鼠标指针离开元素时触发1次(mouseleave。不冒泡,进入子元素不会重复触发父元素的进入事件)`)
        })
        // 当鼠标进入子元素时,mouseover 事件在子元素上生成,然后冒泡到父元素(因为事件会沿着 DOM 树向上传播)。
        // 父元素监听了 mouseover,自然就会捕获到这个冒泡上来的事件。
        // 同样,鼠标离开子元素进入另一个子元素时,mouseout 事件也会从子元素冒泡到父元素。
        // 因此下面代码就隐藏了,避免控制台出现太多打印内容
        // cursorDom.addEventListener('mouseover', function() {
        //     console.log(`鼠标指针进入元素时触发1次(mouseover。会冒泡,进入子元素也会触发父元素的 mouseover)`)
        // })
        // cursorDom.addEventListener('mouseout', function() {
        //     console.log(`鼠标指针离开元素时触发1次(mouseout。会冒泡,进入子元素也会触发父元素的 mouseout)`)
        // })
        cursorDom.addEventListener('mousemove', function(event) {
            const rect = cursorDom.getBoundingClientRect();
            const x = event.clientX - rect.left;   // 相对于父元素左边界的偏移
            const y = event.clientY - rect.top;    // 相对于父元素上边界的偏移
            console.log(`鼠标指针在元素内移动时持续触发(mousemove。常用于实时跟踪鼠标位置),在盒子内的坐标:${x} ${y}`)
        })

        // 文本事件
        const textTextDom = document.querySelector('.text input')
        textTextDom.addEventListener('input', function(event) {
            console.log(`文本内容改变,当前输入框内容是:${textTextDom.value}`)
        })

        // 焦点事件
        const focusTextDom = document.querySelector('.focus input')
        focusTextDom.addEventListener('focus', function(event) {
            console.log(`输入框获得焦点,当前输入框内容是:${focusTextDom.value}`)
        })
        focusTextDom.addEventListener('blur', function(event) {
            console.log(`输入框失去焦点,当前输入框内容是:${focusTextDom.value}`)
        })

        // 键盘事件(可以将keyboardTextDom换成window,那就可以监听全部按键了)
        const keyboardTextDom = document.querySelector('.keyboard input')
        keyboardTextDom.addEventListener('keydown', function(event) {
            console.log(`用户按下按键${event.key}`)
        })
        keyboardTextDom.addEventListener('keyup', function(event) {
            console.log(`用户抬起按键${event.key}`)
        })

        // 单选框/复选框事件
        const radioDom = document.querySelector('.check input[type="radio"]')
        const checkboxDom = document.querySelector('.check input[type="checkbox"]')
        radioDom.addEventListener('change', function(event) {
            console.log(`用户改变单选框值:${event.target.checked}`)
        })
        checkboxDom.addEventListener('change', function(event) {
            console.log(`用户改变复选框值:${event.target.checked}`)
        })

        // 页面加载事件(一般用于window对象、document对象,也支持普通Dom元素使用)
        // 注意:在window上添加load事件,代表整个页面资源加载完毕,包括图片、外联CSS、外链JS等
        window.addEventListener('load', function() {
            console.log('全部页面资源都加载完成了~~~')
        })
        // 注意:在document上添加DOMContentLoaded时间,代表HTML标签加载完成,不包括图片、外联CSS、外链JS等
        document.addEventListener('DOMContentLoaded', function() {
            console.log('页面HTML标签加载完成了~~~')
        })
        // document.documentElement代表html标签,只有html标签才进行了滚动操作,然后html标签需要这么写才能获取到,不要惊讶
        window.addEventListener('scroll', function() {
            console.log(`页面滚动坐标:${document.documentElement.scrollLeft} ${document.documentElement.scrollTop}`)
        })
    </script>
</body>

</html>

页面效果:
在这里插入图片描述

11.5.3.3、案例—拖拽案例

代码实例:

1、index.html

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

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <div class="empty">
        <!-- 默认情况下图片、链接等是可以拖拽的,但普通 div 需要显式设置 draggable="true" -->
        <div class="fill" draggable="true"></div>
    </div>
    <div class="empty"></div>
    <div class="empty"></div>
    <div class="empty"></div>
    <div class="empty"></div>
    <script src="script.js"></script>
</body>

</html>

2、script.js

/* 总结:拖拽工作流
1. 用户按住 `draggable=true` 的元素 → 触发 `dragstart`。
2. 开始移动鼠标 → 根据 `dragover` 的 `preventDefault` 寻找可放置目标。
3. 进入目标区域 → 触发 `dragenter`(高亮);离开目标区域 → `dragleave`(取消高亮)。
4. 在目标区域内松开鼠标 → 触发 `drop`(执行移动或数据存储)。
5. 无论成功与否 → 触发 `dragend`(清理状态)。 */
const fillEl = document.querySelector('.fill')
const emptyEls = document.querySelectorAll('.empty')

fillEl.addEventListener('dragstart', dragstart)
fillEl.addEventListener('dragend', dragend)

emptyEls.forEach(emptyEl => {
    emptyEl.addEventListener('dragenter', dragenter)
    emptyEl.addEventListener('dragover', dragover)
    emptyEl.addEventListener('dragleave', dragleave)
    emptyEl.addEventListener('drop', drop)
})

function dragstart() {
    fillEl.classList.add('hold')      // 给被拖拽元素加一个半透明边框等效果

    // 隐藏被拖拽元素
    setTimeout(() => fillEl.className = 'invisible', 0) // 加一个延迟到下一个宏任务,确保拖拽镜像已经生成后再隐藏原元素。
}

function dragenter() {
    this.classList.add('hovered')   // 高亮提示:可以松手
}

function dragover(event) {
    event.preventDefault();  // 这行必不可少!告诉浏览器这是一个有效的放置目标
}

function dragleave() {
    this.classList.remove('hovered'); // 离开时移除高亮
}

function drop() {
    this.classList.remove('hovered')    // 移除高亮
    this.appendChild(fillEl)            // 把被拖拽元素移动到当前容器中
}

function dragend() {
    fillEl.className = 'fill'   // 拖拽结束,恢复原始样式,让方块重新可见
}

3、style.css

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

a {
    text-decoration: none;
}

li {
    list-style: none;
}

body {
    background-image: linear-gradient(to bottom, #eafbff, #5290f9);
    height: 100vh;
    color: #fff;
    display: flex;
    align-items: center;
    justify-content: center;
}

.empty {
    width: 100px;
    height: 100px;
    border: 3px solid black;
    margin: 30px;
    background-color: white;
}

.hovered {
    background-color: #333;
    border-color: white;
    border-style: dashed;
}

.fill {
    width: 100%;
    height: 100%;
    background-color: pink;
}

.hold {
    border: 3px solid #ccc;
}

.invisible {
    opacity: 0;
}

页面效果:

在这里插入图片描述

11.5.3.4、常用事件汇总表格

说明:

  • “大多数可视元素”指能够显示并接收鼠标/触摸事件的元素,不包括如 <head><title> 等非可视元素。
  • 对于键盘事件,若希望全局捕获按键,通常将监听器添加到 documentwindow 上,因为只有获得焦点的元素才能触发键盘事件。
  • wheel 事件可以在任意元素上触发,即使元素不可滚动;默认滚动行为只发生在可滚动区域(如页面主体或 overflow 容器),可通过
    preventDefault 阻止默认滚动,实现自定义滚轮效果。
  • 拖拽事件的放置目标需要监听 dragover 并调用 preventDefault() 才能允许 drop 事件触发。

表格:

分类 事件名称 触发时机 常见用途 适用对象
鼠标 click 单击鼠标左键 按钮点击、链接跳转 大多数可视元素(如 divbuttona 等)
dblclick 双击鼠标左键 编辑操作、放大视图 同上
mousedown 按下鼠标任意键 拖拽开始、绘画应用 同上
mouseup 释放鼠标按键 拖拽结束、释放操作 同上
mousemove 鼠标在元素上移动 绘图工具、鼠标跟随效果 同上
mouseenter 鼠标进入元素(不冒泡) 悬停提示、菜单展开 同上(不冒泡,适合精确控制)
mouseleave 鼠标离开元素(不冒泡) 隐藏提示、菜单收起 同上
mouseover 鼠标进入元素或子元素(冒泡) 复杂悬停交互 同上(注意冒泡影响)
mouseout 鼠标离开元素或子元素(冒泡) 复杂离开交互 同上
contextmenu 右键点击打开上下文菜单 自定义右键菜单 同上
wheel 鼠标滚轮滚动 缩放、自定义滚动行为 任何可视元素(鼠标指针位于元素上时触发,常用于 window 或可滚动容器)
键盘 keydown 按下任意键 快捷键、游戏控制 可获得焦点的元素(inputtextareabutton[tabindex]),或 documentwindow(全局监听)
keyup 释放按键 表单输入验证、实时搜索触发 同上
表单 input 输入框内容变化(实时) 实时搜索、字数统计 <input><textarea><select>(现代浏览器支持)
change 元素失去焦点且值变化 / 复选框等切换 表单验证、选项保存 <input><textarea><select>
submit 提交表单 表单提交处理 <form>
reset 重置表单 重置表单数据 <form>
focus 元素获得焦点 高亮当前输入框 可获得焦点的元素(如 inputabutton[tabindex]
blur 元素失去焦点 输入验证、隐藏提示 同上
select 选中输入框中的文本 提示用户选中文本 <input><textarea>
窗口/文档 load 页面及所有资源加载完成 初始化页面、加载动画结束 window(也可用于 <img><script> 等资源元素)
DOMContentLoaded HTML 解析完成(无需等待样式、图片) 提前执行脚本、操作 DOM document
resize 窗口大小改变 响应式布局调整 window
scroll 窗口或元素滚动 无限滚动、返回顶部按钮显示 window、可滚动的元素(设置了 overflow: auto/scroll 的容器)
beforeunload 页面即将卸载 提醒用户保存更改 window
hashchange URL 哈希部分变化 单页应用路由 window
popstate 浏览器历史记录变化(如前进/后退) 单页应用导航 window
拖拽 dragstart 开始拖拽元素 记录拖拽数据 设置了 draggable="true" 的元素
drag 拖拽过程中持续触发 实时反馈 同上
dragend 拖拽结束 清理状态 同上
dragenter 拖拽元素进入目标元素 高亮目标区域 作为放置目标的元素,需监听 dragover 并阻止默认行为
dragover 拖拽元素在目标元素上悬停 阻止默认行为以允许放置 同上
dragleave 拖拽元素离开目标元素 取消高亮 同上
drop 在目标元素上释放鼠标(放置) 处理放置的数据 同上,需监听 dragover 并阻止默认行为
触摸 touchstart 手指触摸屏幕 移动端交互开始 大多数可视元素(移动端)
touchmove 手指在屏幕上移动 滑动操作、手势识别 同上(移动端常需调用 preventDefault 阻止页面滚动)
touchend 手指离开屏幕 交互结束 同上
touchcancel 触摸被中断(如来电) 异常处理 同上
动画/过渡 transitionend CSS 过渡完成后触发 执行后续动画 应用了 CSS transition 的元素(属性值变化且过渡结束后触发)
animationstart CSS 动画开始 动画同步 应用了 CSS animation 的元素
animationend CSS 动画结束 清理状态、循环控制 同上
剪贴板 copy 复制操作 自定义复制内容 大多数可视元素(常用于全局监听 document
cut 剪切操作 自定义剪切行为 同上
paste 粘贴操作 处理粘贴的图片或文本 同上
11.5.4、事件对象event
11.5.4.1、概念
  • 语法:监听到事件发生之后,程序会自动调用函数,可以在函数中传入一个形参event,这就是事件对象event,用于记录事件发生时的相关信息
  • 用途:
    • 判断用户的按键信息,比如监听密码输入框的按键按下事件,进而通过事件对象event获取按键信息,从而判断用户按下的是否是回车键,进而决定是否发送登录请求
      在这里插入图片描述

    • 判断鼠标点击的那个元素,比如一个列表展示多条数据,每一条数据后面都有删除按钮,当点击删除按钮的时候,我们可以监听点击事件,通过事件对象event.target找到那个删除按钮元素,通过在元素上记录的data-id信息,从而执行删除操作
      在这里插入图片描述

11.5.4.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>
</head>

<body>
    <button class="btn">点击我</button>
    <script>
        const btnDom = document.querySelector('.btn')
        btnDom.addEventListener('click', function() {
            console.dir(event)
        })
    </script>
</body>

</html>
11.5.4.3、页面效果

在这里插入图片描述

11.5.5、事件流
11.5.5.1、概念
  • 含义:事件完整执行过程中的流动路径。假设页面中有个div,当触发div的点击事件时,会经历两个阶段,分别是捕获阶段和冒泡阶段
    在这里插入图片描述

  • 捕获阶段(基本不用):从外向内的过程

  • 冒泡阶段(开发常用):从内向外的过程。一般情况下,我们会利用事件冒泡特性,将事件处理维度拔高,以便统一处理,当然也是要判断事件event.target对象是否是预期的Dom对象,避免误处理。

11.5.5.2、事件捕获(很少使用)
11.5.5.2.1、概念
  • 含义:从DOM的根元素开始去执行对应的事件
  • 代码:DOM对象.addEventListener(事件类型, 事件处理函数, 是否使用捕获机制)
    • 参数含义:
      • 参数3:非必填。默认值是false,代表使用冒泡机制。填写为true代表使用捕获机制
11.5.5.2.2、代码示例

在事件冒泡的代码示例一致,这里不在赘述

11.5.5.3、事件冒泡
11.5.5.2.1、概念
  • 含义:当一个元素触发事件后,会一次向上调用所有父级元素的同名事件。事件冒泡是默认存在的,不需要特殊传参
11.5.5.2.2、代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <!-- 表头 -->
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>你好,春天~</title>
    <style>
        .father {
            width: 200px;
            height: 200px;
            background-color: rebeccapurple;
        }

        .son {
            width: 100px;
            height: 100px;
            background-color: pink;
            text-align: center;
            line-height: 100px;
            cursor: pointer;
        }
        
        .son:hover {
            color: red;
        }
    </style>
</head>

<body>
    <div class="father">
        <div class="son">点击我</div>
    </div>
    <script>
        const fatherDom = document.querySelector('.father')
        const sonDom = document.querySelector('.son')

        // 捕获阶段
        document.addEventListener('click', function() {
            console.log('捕获阶段:我是document')
        }, true)
        fatherDom.addEventListener('click', function() {
            console.log('捕获阶段:我是父亲')
        }, true)
        sonDom.addEventListener('click', function() {
            console.log('捕获阶段:我是儿子')
        }, true)

        // 冒泡阶段
        sonDom.addEventListener('click', function() {
            console.log('冒泡阶段:我是儿子')
        })
        fatherDom.addEventListener('click', function() {
            console.log('冒泡阶段:我是父亲')
        })
        document.addEventListener('click', function() {
            console.log('冒泡阶段:我是document')
        })
    </script>
</body>

</html>
11.5.5.2.3、页面效果

点击粉红色盒子后,控制台效果如下:

在这里插入图片描述

11.5.5.3、阻止事件传播(捕获+冒泡)
11.5.5.3.1、概念
  • 含义:将事件限制在当前元素内,阻止继续传播,一旦阻止后,后面的捕获或者冒泡阶段都不会执行
    在这里插入图片描述

  • 代码:event事件对象.stopPropagation()

  • 作用:我们在某些情况下需要阻止事件传播(捕获/冒泡)

11.5.5.3.2、代码示例
<!DOCTYPE html>
<html lang="en">

<head>
    <!-- 表头 -->
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>你好,春天~</title>
    <style>
        .father {
            width: 200px;
            height: 200px;
            background-color: rebeccapurple;
        }

        .son {
            width: 100px;
            height: 100px;
            background-color: pink;
            text-align: center;
            line-height: 100px;
            cursor: pointer;
        }
        
        .son:hover {
            color: red;
        }
    </style>
</head>

<body>
    <div class="father">
        <div class="son">点击我</div>
    </div>
    <script>
        const fatherDom = document.querySelector('.father')
        const sonDom = document.querySelector('.son')

        // 捕获阶段
        document.addEventListener('click', function(event) {
            console.log('捕获阶段:我是document')
        }, true)
        fatherDom.addEventListener('click', function(event) {
        		 // 阻止事件传播,那么后面的捕获或者冒泡阶段都不会执行了
            event.stopPropagation()
            console.log('捕获阶段:我是父亲')
        }, true)
        sonDom.addEventListener('click', function(event) {
            console.log('捕获阶段:我是儿子')
        }, true)

        // 冒泡阶段
        sonDom.addEventListener('click', function(event) {
            console.log('冒泡阶段:我是儿子')
        })
        fatherDom.addEventListener('click', function() {
            console.log('冒泡阶段:我是父亲')
        })
        document.addEventListener('click', function() {
            console.log('冒泡阶段:我是document')
        })
    </script>
</body>

</html>
11.5.5.3.3、页面效果

一旦阻止后,后面的捕获或者冒泡阶段都不会执行

在这里插入图片描述

11.5.6、阻止元素默认行为
11.5.6.1、概念
  • 含义:在某些情况下,需要阻止默认行为的发生,比如阻止链接的跳转,表单域发请求
  • 语法:event事件对象.preventDefault()
11.5.6.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>
</head>

<body>
    <h1>文章标题</h1>
    <p>测试一下<a href="https://www.baidu.com">百度一下</a>测试一下</p>
    <script>
        document.addEventListener('click', function (event) {
            if (
                event.target.tagName === 'A' &&
                event.target.href &&
                !event.target.href.startsWith('https://XXX.com')
            ) {
                const answer = confirm(`您确定需要跳转到该网址【${event.target.href}`)
                if(answer) {
                    window.open(event.target.href, '_blank');
                    
                }
                event.preventDefault()
            }
        })
    </script>
</body>

</html>
11.5.6.3、页面效果

点击网页中的超链接,如果域名不是我们网站的,那就需要询问用户是否要跳转到该网站,如果用户点击确定,那就新开一个页签用于展示超链接网站内容,否则不执行任何操作。当然当前网页的网址不会发生改变。
在这里插入图片描述

11.5.7、事件解绑
11.5.7.1、on方式绑定事件(了解即可)

注意: 对于同一个dom对象来说,on方式绑定事件可以被覆盖,所以直接赋值为null就可以实现时间的解绑,代码如下:

在这里插入图片描述

11.5.7.1、addEventListener方式绑定事件

注意: 仅支持解绑具名函数,不支持解绑匿名函数。并且支持通过第3个参数决定解绑捕获阶段(设置为true),还是冒泡阶段的事件(使用默认值false或者直接设置成false),默认解绑冒泡阶段的事件。代码如下:

在这里插入图片描述

11.5.8、事件委托
11.5.8.1、概念
  • 含义:利用事件冒泡特点,为父元素注册事件,当我们触发元素的时候,事件会冒泡到父元素身上,从而触发父元素的事件(例如: 将click事件加到ul身上,那么所有li身上的click事件都会被ul接收到)
  • 实现方式:通过event.target.tagName可以获取真正触发事件的元素,然后判断是否是目标Dom元素,进而执行相关操作,当然也可以加其他判断条件哈
11.5.8.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;
            box-sizing: border-box;
        }

        ul {
            list-style: none;
        }

        a {
            text-decoration: none;      /* 去掉下划线 */
            color: inherit;             /* 继承父级颜色,避免默认蓝/紫 */
            /* 可选:保留手型 cursor: pointer; 其实浏览器默认就是 pointer */
        }

        .goods {
            margin: 50px 100px;
        }

        .goods .tab {
            position: relative;
            width: 300px;
            height: 30px;
        }

        .goods .tab ul {
            display: flex;
        }

        .goods .tab li {
            width: 100px;
            height: 30px;
            border: 1px solid #ccc;
            background-color: pink;
            text-align: center;
            line-height: 30px;
            color: black;
            font-family: '楷体';
        }

        .goods .tab li a.active {
            color: red;
        }

        .goods .line {
            transform: translateY(-4px);
            width: 0px;
            height: 2px;
            background-color: red;
            transition: all 0.3s;
        }

        .goods .content div {
            width: 300px;
            height: 100px;
            text-align: center;
            line-height: 100px;
            font-size: 30px;
            display: none;
            background-color: goldenrod;
        }

        .goods .content div.active {
            display: block;
        }

        .goods .content div.remen {
            background-color: rgb(255, 0, 0, 0.5);
        }

        .goods .content div.yifu {
            background-color: rgb(173, 255, 47, 0.5);
        }

        .goods .content div.jiushui {
            background-color: rgb(138, 43, 226, 0.5);
        }
    </style>
</head>

<body>
    <div class="goods">
        <div class="tab">
            <ul>
                <li><a data-index="0" class="active" href="#">热门</a></li>
                <li><a data-index="1" href="#">衣服</a></li>
                <li><a data-index="2" href="#">酒水饮料</a></li>
            </ul>
            <div class="line">
        </div>

        </div>
        <div class="content">
            <div class="remen active">热门</div>
            <div class="yifu">衣服</div>
            <div class="jiushui">酒水饮料</div>
        </div>
    </div>
    <script>
        // tab栏文字滚动条
        const tabLineDom = document.querySelector('.goods .tab .line')
        // 启用标签和内容所用的class类
        const ACTIVE_CLASS_NAME = 'active'
        // 事件委托(本来需要监听a标签的移入事件,现在变成了监听ul标签的移入事件,这正是利用了mouseover会冒泡的特性)
        const tabUlDom = document.querySelector('.goods .tab ul')
        tabUlDom.addEventListener('mouseover', function(event) {
            // 判断是否是我们所需元素
            if(event.target.tagName === 'A' && event.target.dataset.index) {
                const currentTabDom = event.target
                const index = +currentTabDom.dataset.index

                // 1、处理标签
                // 移除其他标签已有效果
                const oldTabActiveDom = document.querySelector('.goods .tab a.active')
                if(oldTabActiveDom) oldTabActiveDom.classList.remove(ACTIVE_CLASS_NAME)
                // 为当前标签添加效果
                currentTabDom.classList.add(ACTIVE_CLASS_NAME)
                
                // 2、处理tab栏文字滚动条
                setTabLine(currentTabDom)

                // 3、处理内容
                // 移除其他内容已有效果
                const contentActiveDom = document.querySelector('.goods .content div.active')
                if(contentActiveDom) contentActiveDom.classList.remove(ACTIVE_CLASS_NAME)
                // 为当前内容添加效果
                document.querySelector(`.goods .content div:nth-child(${index + 1})`).classList.add(ACTIVE_CLASS_NAME)
            }
        })

        // 2、初始化tab栏文字滚动条
        setTabLine(document.querySelector('.goods .tab a.active'))

        function setTabLine(currentActiveTabDom) {
            const rect = currentActiveTabDom.getBoundingClientRect();
            const leftDistance = rect.left + window.scrollX;
            console.log(leftDistance)
            tabLineDom.style.width = `${currentActiveTabDom.offsetWidth}px`
            // 由于距离a标签最近的定位父级就是tab标签,所以才能使用offsetLeft
            tabLineDom.style.transform = `translate(${currentActiveTabDom.offsetLeft}px, -4px)`
        }
    </script>
</body>

</html>
11.5.8.3、页面效果

在这里插入图片描述

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐