Web APIs

Source

Web API基本认知

严格意义上讲,我们在 JavaScript 阶段学习的知识绝大部分属于 ECMAScript 的知识体系,ECMAScript 简称 ES 它提供了一套语言标准规范,如变量、数据类型、表达式、语句、函数等语法规则都是由 ECMAScript 规定的。浏览器将 ECMAScript 大部分的规范加以实现,并且在此基础上又扩展一些实用的功能,这些被扩展出来的内容我们称为 Web APIs。

作用和分类

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

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

什么是DOM

  • DOM(Document Object Model——文档对象模型)是用来呈现以及与任意 HTML 或 XML文档交互的API

  • 白话文:DOM是浏览器提供的一套专门用来操作网页内容的功能

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

DOM树

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

  • 描述网页内容关系的名词

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

DOM对象(重要)

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

    • 所有的标签属性都可以在这个对象上面找到

    • 修改这个对象的属性会自动映射到标签身上

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

  • document 对象

    • 是 DOM 里提供的一个对象

    • 所以它提供的属性和方法都是用来访问和操作网页内容的

      • 例:document.write()

    • 网页所有内容都在document里面

获取DOM元素

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

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

querySelector 满足条件的第一个元素

语法:document.querySelector('css选择器')

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

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

<body>
    <div class="box">css</div>
    <div class="box">呵呵哈哈哈</div>
    <p id="nav">hhhhhhh</p>
    <ul>
        <li>测试1</li>
        <li></li>
        <li></li>
    </ul>
    <script>
        // 获取匹配的第一个元素
        // 方法一
        //const box = document.querySelector('div');
        //console.log(box);
        // 方法二
		const box = document.querySelector('.box');
        console.log(box);//<div class="box">css</div>
        // 获取id选择器
        const p = document.querySelector('#nav');
        console.log(p);// <p id="nav">hhhhhhh</p>
        // 获取第一个li
        //const li = document.querySelector('ul li');
        //console.log(li);// <li>测试1</li>
        // 第二种
        const li = document.querySelector('ul li:first-child');
        console.log(li);// <li>测试1</li>
    </script>
</body>
querySelectorAll 满足条件的元素集合 返回伪数组

document.querySelectorAll('css选择器')

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

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

<body>
    <ul>
        <li>测试1</li>
        <li>测试2</li>
        <li>测试3</li>
    </ul>
    <script>
        // 获取所有li
        const liList = document.querySelectorAll('ul li');
        console.log(liList);// NodeList(3) [li, li, li]

    </script>
</body>

得到的是一个伪数组:

  • 有长度有索引号的数组

  • 但是没有 pop() push() 等数组方法 想要得到里面的每一个对象,则需要遍历(for)的方式获得。

<body>
    <ul class="nav">
        <li>首页</li>
        <li>介绍</li>
        <li>联系</li>
    </ul>
    <script>
        // 获取所有li的dom对象
        const liList = document.querySelectorAll('.nav li');
        for(let i = 0; i < liList.length; i++) {
            console.log(liList[i]);
        }

    </script>
</body>

其他获取DOM元素方法

  • getElementById

根据id获取第一个元素

  • getElementsByTagName

根据标签获取一类元素 获取页面

  • getElementsByClassName

根据类名获取元素

操作元素内容

通过修改 DOM 的文本内容,动态改变网页的内容。

元素innerText 属性

<body>
    <div class="box">我是文字内容</div>
    <script>
        // 获取元素
        const box = document.querySelector('.box');
        // 修改文字内容
        console.log(box.innerText);// 我是文字内容
        box.innerText = '修改后的内容';
        console.log(box.innerText);// 修改后的内容
        box.innerText = '<h3>我是标题</h3>';
        console.log(box.innerText);// <h3>我是标题</h3>    页面中也是如此,并不会被解析
    </script>
</body>

将文本内容添加/更新到任意标签位置,文本中包含的标签不会被解析。

 
   

元素.innerHTML 属性

将文本内容添加/更新到任意标签位置,文本中包含的标签会被解析。

<body>
    <div class="box">我是文字内容</div>
    <script>
        // 获取元素
        const box = document.querySelector('.box');
        // 修改文字内容
        console.log(box.innerHTML);// 我是文字内容
        box.innerHTML = '我是修改后的文字内容';
        console.log(box.innerHTML);// 我是修改后的文字内容
        box.innerHTML = '<h3>我是修改后的h3标签</h3>';
        console.log(box.innerHTML);// <h3>我是修改后的h3标签</h3>   页面中会解析显示的是 :我是修改后的h3标签
    </script>
</body>

小练习:

从数组随机抽取一等奖、二等奖和三等奖,显示到对应的标签里面。

<!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{
            height: 300px;
            width: 300px;
            margin: 0 auto;
            border: pink;
            background-color: rgb(242, 19, 224);
            text-align: center;
        }
        .title{
            font-size: 50px;
            color:rgb(242, 199, 8);
        }
        h1,h2,h3{
            color: rgb(14, 14, 236);
        }
        .one{
            color: rgb(5, 216, 135);
        }
    </style>
</head>
<body>
    <div class="box">
        <strong class="title">抽奖</strong>
        <h1>一等奖:<span class="one"></span></h1>
        <h2>二等奖:<span class="two"></span></h2>
        <h3>三等奖:<span class="three"></span></h3>
    </div>
    <script>
        const personArr = ['周杰伦', '刘德华', '周星驰', '彭于晏', '张学友']
        // 一等奖
        const random = Math.floor(Math.random() * personArr.length)
        // 获取one
        const one = document.querySelector('.one')
        one.innerHTML = personArr[random]
        // 删除one获取到的元素
        personArr.splice(random, 1)
        // 二等奖
        const random2 = Math.floor(Math.random() * personArr.length)
        const two = document.querySelector('.two')
        two.innerHTML = personArr[random2]
        // 删除two获取到的元素
        personArr.splice(random2, 1)
        // 三等奖
        const random3 = Math.floor(Math.random() * personArr.length)
        const three = document.querySelector('.three')
        three.innerHTML = personArr[random3]
    </script>
</body>
</html>

操作元素常用属性

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

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

小练习:当我们刷新页面,页面中的图片随机显示不同的图片

<body>
    <img src="./images/1.webp" alt="">
    <script>
        function getRandom(n,m){
            return Math.floor(Math.random()*(m-n+1))+n;
        }
        // 获取图片对象
        const img = document.querySelector('img');
        // 生成随机数
        const random = getRandom(1,6);
        console.log(random);
        // 修改图片的src属性
        img.src = `./images/${random}.webp`;
    </script>
</body>

操作元素样式属性

比如通过 轮播图小圆点自动更换颜色样式

点击按钮可以滚动图片,这是移动的图片的位置 left 等等

1 通过 style 属性操作CSS
<!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{
            height: 300px;
            width: 300px;
            margin: 0 auto;
            border: pink;
            background-color: rgb(242, 19, 224);
        }
        
    </style>
</head>
<body>
    <div class="box"></div>
    <script>
        const box = document.querySelector('.box')
        // 修改元素样式
        box.style.width =  '200px'
        box.style.backgroundColor = 'red'
    </script>
</body>
</html>
2 操作类名(className) 操作CSS

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

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

  • className是使用新值换旧值, 如果需要添加一个类,需要保留之前的类名

<!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{
            height: 300px;
            width: 300px;
            margin: 0 auto;
            border: pink;
            background-color: rgb(242, 19, 224);
        }
        .name{
            color: red;
            background-color: blue;
        }
        .nav{
            color: rgb(44, 222, 17);
            background-color: rgb(215, 238, 10);
        }
        
    </style>
</head>
<body>
    <div></div>
    <p class="name">111</p>
    <script>
        const div = document.querySelector('div')
        // 添加类名
        div.className = 'box'
        const p = document.querySelector('.name')
        //p.className = 'nav'// 自身有类名会被替换 
        p.className = 'nav name'// 两个样式都会有
    </script>
</body>
</html>
3 通过 classList 操作类控制CSS

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

classList 是追加和删除不影响以前类名

<!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{
            height: 200px;
            width: 200px;
            margin: 0 auto;
            background-color: rgb(242, 19, 224);
        }
        .box1{
            color: blue;
        }
        
        
    </style>
</head>
<body>
    <div class="box box1">111111</div>
    <script>
       const box = document.querySelector('.box')
       // 通过classList添加
       //box.classList.add('box1')
       // 通过classList移除
       //box.classList.remove('box1')
       // 通过classList切换 有就删除没有就加上
       box.classList.toggle('box1')

        
    </script>
</body>
</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>
    <style>
        .slider{
            width:800px;
            height:420px;
            margin:0 auto;
            overflow: hidden;
        }
        .sliderBox{
            width:100%;
            height: 350px;
        }
        .sliderBox img{
            width:100%;
            height:100%;
        }
        .sliderFooter{
            width: 100%;
            height:70px;
            background-color: rgb(2, 93, 85);
            padding: 8px 8px 0 8px;
            position:relative;
        }
        .sliderFooter p{
            font-size: 20px;
            font-weight: 700;
            color:blueviolet;
            margin:0;
            margin-bottom: 10px;
        }
        .sliderFooter ul{
            margin:0;
            padding: 0;
            display: flex;
            list-style: none;
        }
        .sliderFooter .sliderBtn{
            position: absolute;
            right: 0;
            top:10px;

        }
        .sliderFooter .sliderBtn button{
            margin-right:15px;
            appearance: none;
            border:none;
            background: rgba(255,255,255,0.1);
            color:#fff;
            cursor: pointer;
        }
        .sliderFooter .sliderBtn button:hover{
            background: rgba(12, 117, 238, 0.5);
        }
        ul li{
            width: 8px;
            height: 8px;
            background-color: #fff;
            border-radius: 50%;
            margin-right: 5px;
            opacity: 0.4;
            cursor: pointer;
        }
        .sliderFooter ul li.active{
            opacity: 1;
            width: 10px;
            height: 10px;
        }
    </style>
</head>
<body>
    <div class="slider">
        <div class="sliderBox">
            <img src="./images/desktop_1.jpg" alt="">   
        </div>
        <div class="sliderFooter">
            <p>天好蓝</p>
            <ul>
                <li></li>
                <li></li>
                <li></li>
                <li></li>
            </ul>
            <div class="sliderBtn">
                <button><</button>
                <button>></button>
            </div>
        </div>
    </div>
    <script>
       const arr = [
       {url:'./images/desktop_1.jpg',title:'天好蓝',background:'rgb(2, 93, 85)'},
       {url:'./images/desktop_2.jpg',title:'建筑好美',background:'rgb(114, 155, 120)'},
       {url:'./images/desktop_3.jpg',title:'灯火通明',background:'rgb(134, 185, 110)'},
       {url:'./images/desktop_4.jpg',title:'海景好美',background:'rgb(104, 115, 190)'}
       ]
        //创建随机数
        const random = parseInt(Math.random()*arr.length)
        //获取元素
        const img = document.querySelector('.sliderBox img')
        img.src = arr[random].url
        const sliderFooter = document.querySelector('.sliderFooter')
        sliderFooter.style.backgroundColor = arr[random].background
        const p = document.querySelector('.sliderFooter p')
        p.innerHTML = arr[random].title
        // 小圆点
        const li = document.querySelector(`ul li:nth-child(${random+1})`)
        li.classList.add('active')
        
    </script>
</body>
</html>

操作表单元素属性

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

  • 正常的有属性有取值的 跟其他的标签属性没有任何区别

获取: DOM对象.属性名

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

表单属性中添加就有效果,移除就没有效果,一律使用布尔值表示

如果为true 代表添加了该属性 如果是false 代表移除了该属性

比如: disabled、checked、selected

<body>
    <!-- <input type="text" value="请输入"> -->
    <input type="checkbox" value="请选择" checked>
    <button disabled>按钮</button>

    <script>
       const input = document.querySelector('input')
       console.log(input.value) 
       // input.type='password'     
       console.log(input.checked)  
       const button = document.querySelector('button')
       button.disabled = true
       console.log(button.disabled)

    </script>
</body>

自定义属性

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

自定义属性

  • 在html5中推出来了专门的data-自定义属性

  • 在标签上一律以data-开头

  • 在DOM对象上一律以dataset对象方式获取

<body>
    <div data-id="1">1</div>
    <script>
       const div = document.querySelector('div')
       console.log(div.dataset.id)
    </script>
</body>

定时器-间歇函数

作用:能够使用定时器函数重复执行代码

定时器函数可以开启和关闭定时器

开启定时器

setInterval(函数,间隔时间)

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

间隔时间单位是毫秒

function test(){
    console.log('test')
}
let time = setInterval(test,1000)

关闭定时器

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

clearInterval(变量名)

注意: 1. 函数名字不需要加括号 2. 定时器返回的是一个id数字

function test(){
    console.log('test')
}
let time = setInterval(test,1000)
clearInterval(time)

练习:按钮60秒之后才可以使

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

练习:轮播图定时器版

每隔一秒钟切换一个图片

<!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>
        .slider{
            width:800px;
            height:420px;
            margin:0 auto;
            overflow: hidden;
        }
        .sliderBox{
            width:100%;
            height: 350px;
        }
        .sliderBox img{
            width:100%;
            height:100%;
        }
        .sliderFooter{
            width: 100%;
            height:70px;
            background-color: rgb(2, 93, 85);
            padding: 8px 8px 0 8px;
            position:relative;
        }
        .sliderFooter p{
            font-size: 20px;
            font-weight: 700;
            color:blueviolet;
            margin:0;
            margin-bottom: 10px;
        }
        .sliderFooter ul{
            margin:0;
            padding: 0;
            display: flex;
            list-style: none;
        }
        .sliderFooter .sliderBtn{
            position: absolute;
            right: 0;
            top:10px;

        }
        .sliderFooter .sliderBtn button{
            margin-right:15px;
            appearance: none;
            border:none;
            background: rgba(255,255,255,0.1);
            color:#fff;
            cursor: pointer;
        }
        .sliderFooter .sliderBtn button:hover{
            background: rgba(12, 117, 238, 0.5);
        }
        ul li{
            width: 8px;
            height: 8px;
            background-color: #fff;
            border-radius: 50%;
            margin-right: 5px;
            opacity: 0.4;
            cursor: pointer;
        }
        .sliderFooter ul li.active{
            opacity: 1;
            width: 10px;
            height: 10px;
        }
    </style>
</head>
<body>
    <div class="slider">
        <div class="sliderBox">
            <img src="./images/desktop_1.jpg" alt="">   
        </div>
        <div class="sliderFooter">
            <p>天好蓝</p>
            <ul>
                <li class="active"></li>
                <li></li>
                <li></li>
                <li></li>
            </ul>
            <div class="sliderBtn">
                <button><</button>
                <button>></button>
            </div>
        </div>
    </div>
    <script>
       const arr = [
       {url:'./images/desktop_1.jpg',title:'天好蓝',background:'rgb(2, 93, 85)'},
       {url:'./images/desktop_2.jpg',title:'建筑好美',background:'rgb(114, 155, 120)'},
       {url:'./images/desktop_3.jpg',title:'灯火通明',background:'rgb(134, 185, 110)'},
       {url:'./images/desktop_4.jpg',title:'海景好美',background:'rgb(104, 115, 190)'}
       ]
        let i = 0
        // 设置定时器
        setInterval(function(){
            i++
            // 图片播放到最后一张,则返回第一张
            if(i>=arr.length){
                i=0
            }
            console.log(i)
            // 获取img
            const img = document.querySelector('.sliderBox img')
            // 改变图片地址
            img.src = arr[i].url
            // 获取p
            const p = document.querySelector('.sliderFooter p')
            p.innerHTML = arr[i].title
            // 获取li的active
            const active = document.querySelector('.sliderFooter ul .active')
            // 清除前一个li的active类
            active.classList.remove('active')
            // 给当前li添加active类
            document.querySelector(`.sliderFooter ul li:nth-child(${i+1})`).classList.add('active')

        },2000)
        
    </script>
</body>
</html>

事件监听(绑定)

什么是事件?

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

什么是事件监听?

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

语法

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

特点:每次触发每次执行

事件监听三要素:

  • 事件源: 那个dom元素被事件触发了,要获取dom元素

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

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

<body>
    <button>按钮</button>
    <script>
       let btn = document.querySelector('button');
       btn.addEventListener('click',function(){
           console.log('点击了按钮')
       })
        
    </script>
</body>
练习:点击关闭广告

<!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: 100px;
            background-color: rgb(12, 154, 211);
            position: relative;
        }
        h1{
            text-align: center;
            line-height: 100px;
        }
        button{
            position: absolute;
            top: 0;
            right: 0;
        }
    </style>
</head>
<body>
    <div class="box">
        <h1>我是广告</h1>
        <button>X</button>
    </div>
    <script>
       let btn = document.querySelector('button');
       let box = document.querySelector('.box');
       btn.addEventListener('click',function(){
           box.style.display = 'none'
       })
        
    </script>
</body>
</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>
    <style>
        .box{
            width: 300px;
            height: 100px;
            background-color: rgb(12, 154, 211);
        }
        h1{
            text-align: center;
        }
        p{
            text-align: center;
        }
        span{
            color: red;
        }
        .btn{
            margin-top: 20px;
            width: 300px;
            display: flex;
            justify-content: space-between;
        }
        button{
            width: 50px;
        }
    </style>
</head>
<body>
    <div class="box">
        <h1>随机点名</h1>
        <p>名字是:<span></span></p>
    </div>
    <div class="btn">
        <button class="start">开始</button>
        <button class="end">结束</button>
    </div>
    <script>
        // 获取元素
        let start = document.querySelector('.start');
        let end = document.querySelector('.end');
        let span = document.querySelector('span');
        let arr = ['张三','李四','王五','赵六','钱七','孙八','周九','吴十','郑十一','王十二'];
        // 初始timer变量
        let timer = null;
        // 初始随机数
        let random = null;
        // 点击开始按钮
        start.addEventListener('click',function(){
            timer = setInterval(function(){
                let random = Math.floor(Math.random()*arr.length);
                span.innerHTML = arr[random];
            },100)
            
        })
        // 点击结束按钮
        end.addEventListener('click',function(){
            clearInterval(timer);
            // 结束了可以删掉抽中的元素
            arr.splice(random,1);
            console.log(arr);
            if(arr.length == 0){
                start.disabled = true;
                alert('已经抽完');
                clearInterval(timer);
            }
        })

    </script>
</body>
</html>

事件监听版本

  • DOM L0

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

  • DOM L2

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

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

发展史:

  • DOM L0 :是 DOM 的发展的第一个版本; L:level

  • DOM L1:DOM级别1 于1998年10月1日成为W3C推荐标准

  • DOM L2:使用addEventListener注册事件

  • DOM L3: DOM3级事件模块在DOM2级事件的基础上重新定义了这些事件,也添加了一些新事件类型

事件类型

鼠标事件

  • click 鼠标点击

  • mouseenter 监听鼠标是否移入 DOM 元素

  • mouseleave 鼠标离开

<!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: 100px;
            height: 100px;
            background-color: rgb(12, 154, 211);
        }
    </style>
</head>
<body>
    <div class="box"></div>
    
    <script>
        let box = document.querySelector('.box')
        box.addEventListener('mouseenter',function(){
            console.log('移入')
        })
        box.addEventListener('mouseleave',function(){
            console.log('移出')
        })

    </script>
</body>
</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>
    <style>
        .slider{
            width:800px;
            height:420px;
            margin:0 auto;
            overflow: hidden;
        }
        .sliderBox{
            width:100%;
            height: 350px;
        }
        .sliderBox img{
            width:100%;
            height:100%;
        }
        .sliderFooter{
            width: 100%;
            height:70px;
            background-color: rgb(2, 93, 85);
            padding: 8px 8px 0 8px;
            position:relative;
        }
        .sliderFooter p{
            font-size: 20px;
            font-weight: 700;
            color:blueviolet;
            margin:0;
            margin-bottom: 10px;
        }
        .sliderFooter ul{
            margin:0;
            padding: 0;
            display: flex;
            list-style: none;
        }
        .sliderFooter .sliderBtn{
            position: absolute;
            right: 0;
            top:10px;

        }
        .sliderFooter .sliderBtn button{
            margin-right:15px;
            appearance: none;
            border:none;
            background: rgba(255,255,255,0.1);
            color:#fff;
            cursor: pointer;
        }
        .sliderFooter .sliderBtn button:hover{
            background: rgba(12, 117, 238, 0.5);
        }
        ul li{
            width: 8px;
            height: 8px;
            background-color: #fff;
            border-radius: 50%;
            margin-right: 5px;
            opacity: 0.4;
            cursor: pointer;
        }
        .sliderFooter ul li.active{
            opacity: 1;
            width: 10px;
            height: 10px;
        }
    </style>
</head>
<body>
    <div class="slider">
        <div class="sliderBox">
            <img src="./images/desktop_1.jpg" alt="">   
        </div>
        <div class="sliderFooter">
            <p>天好蓝</p>
            <ul>
                <li class="active"></li>
                <li></li>
                <li></li>
                <li></li>
            </ul>
            <div class="sliderBtn">
                <button class="prev"><</button>
                <button class="next">></button>
            </div>
        </div>
    </div>
    <script>
       const arr = [
       {url:'./images/desktop_1.jpg',title:'天好蓝',background:'rgb(2, 93, 85)'},
       {url:'./images/desktop_2.jpg',title:'建筑好美',background:'rgb(114, 155, 120)'},
       {url:'./images/desktop_3.jpg',title:'灯火通明',background:'rgb(134, 185, 110)'},
       {url:'./images/desktop_4.jpg',title:'海景好美',background:'rgb(104, 115, 190)'}
       ]
        let i = 0
        let next = document.querySelector('.next')
        let prev = document.querySelector('.prev')
        // 右边按钮
        next.addEventListener('click',function(){
            i++
            if(i>=arr.length){
                i = 0
            }
            change()
        })
        // 左边按钮
        prev.addEventListener('click',function(){
            i--
            if(i<0){
                i = arr.length-1
            }
            change()
        })
        function change(){
            document.querySelector('.sliderBox img').src = arr[i].url
            document.querySelector('.sliderFooter p').innerText = arr[i].title
            document.querySelector('.sliderFooter').style.backgroundColor = arr[i].background
            // 先移除前一个active类名
            document.querySelector('.sliderFooter ul .active').classList.remove('active')
            // 再给当前li添加active类名
            document.querySelector(`.sliderFooter ul li:nth-child(${i+1})`).classList.add('active')
        }
        // 自动轮播
        let timer = setInterval(function(){
            // click一定要加()
            next.click()
        },1000)
        // 鼠标经过停止定时器
        let slider = document.querySelector('.slider')
        slider.addEventListener('mouseenter',function(){
            clearInterval(timer)
        })
        // 鼠标离开开始定时器
        slider.addEventListener('mouseleave',function(){
            //先停止在开启
            clearInterval(timer)
            timer = setInterval(function(){
                next.click()
            },1000)
        })
        
        
    </script>
</body>
</html>

焦点事件

  • focus:获得焦点

  • blur :失去焦点

练习:小米搜索框案例 ,当表单得到焦点,显示下拉菜单,失去焦点隐藏下拉菜单

<!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{
            width: 200px;
            height: 300px;
            margin:100px auto;
            position: relative;
        }
        input{
            outline: none;
            width: 100%;
            height: 30px;
            border: 1px solid #ede4e4;

        }
        ul{
            width: 100%;
            height: 270px;
            position: absolute;
            border: 1px solid #fa7807;
            top: 30px;
            left: 0;
            list-style: none;
            display: none;
        }
        .active{
            border: 1px solid #fa7807;
        }
        ul li{
            width: 100%;
            height: 30px;
            line-height: 30px;
            font-size: 15px;
        }
        li a{
            display: block;
            text-decoration: none;
            padding-left: 20px;
            color: #666565;
        }
        ul a:hover{
            background-color: #f5f5f5;
        }
    </style>
</head>
<body>
    <div class="box">
        <input placeholder="小米" type="search"/>
        <ul class="active">
            <li><a href="#">全部商品</a></li>
            <li><a href="#">小米手环</a></li>
            <li><a href="#">小米15</a></li>
            <li><a href="#">小米耳机</a></li>
            <li><a href="#">小米音响</a></li>
            <li><a href="#">小米充电宝</a></li>
            <li><a href="#">小米电脑</a></li>
            <li><a href="#">小米空调</a></li>
        </ul>
    </div>
    <script>
        // 获取元素
        let input = document.querySelector('[type="search"]');
        let ul = document.querySelector('ul');
        // 绑定焦点事件
        input.addEventListener('focus',function(){
            console.log('获取焦点');
            ul.style.display = 'block';
            input.classList.add('active');
        })
        // 绑定失去焦点事件
        input.addEventListener('blur',function(){
            console.log('失去焦点');
            ul.style.display = 'none';
            input.classList.remove('active');
        })
    </script>
</body>
</html>

键盘事件

  • Keydown 键盘按下触发

  • Keyup 键盘抬起触发

<body>
    <input />
    <script>
        // 获取元素
        let input = document.querySelector('input');
        // 键盘按下事件
        input.addEventListener('keydown',function(){
            console.log('键盘按下');
        })
        // 键盘抬起事件
        input.addEventListener('keyup',function(){
            console.log('键盘抬起');
        })
    </script>
</body>

表单输入触发

input 用户输入事件

<body>
    <input />
    <script>
        // 获取元素
        let input = document.querySelector('input');
        // 用户输入文本事件
        input.addEventListener('input', function () {
            console.log(input.value);
        })
    </script>
</body>

练习:统计表单字数

用户输入文字评论,可以计算用户输入的字数

<!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{
            width: 600px;
            margin: 50px auto;
            display: flex;
        }
        .wapper{
            position:relative;
            
        }
        input{
            width: 500px;
            height: 50px;
            border: 1px solid #ccc;
            background-color: #eee;
            outline: none;
            color: #242424;
            resize: none;
            border-radius: 5px;
            padding-left:10px ;
            line-height: 50px;
            flex: 1;
            opacity: 0.5;
        }
        input:focus{
            height:70px;
            border: 1px solid #0eb8e3;
            background-color: white;
        }
        button{
            width: 50px;
            height: 50px;
            background-color: #0eb8e3;
            border: none;
            outline: none;
            margin-left: 10px;
            border-radius: 5px;
            cursor: pointer;
        }
        .num{
            position: absolute;
            bottom:-20px;
            right:0px;
            display: none;
        }


    </style>
</head>
<body>
    <div class="box">
        <div class="wapper">
            <input maxlength="200" placeholder="发一条友善的评论"/>
            <div class="num"><span>0</span>/200</div>
        </div>
        <button>发布</button>
    </div>

    <script>
        // 获取元素
        let input = document.querySelector('input');
        let num = document.querySelector('.num');
        let span = document.querySelector('span');
        // 焦点
        input.addEventListener('focus', function () {
            num.style.display = 'block'
        })
        // 失焦
        input.addEventListener('blur', function () {
            num.style.display = 'none'
        })
        // 用户输入文本事件
        input.addEventListener('input', function () {
            span.innerHTML = input.value.length
        })
    </script>
</body>
</html>

事件对象

获取事件对象

任意事件类型被触发时与事件相关的信息会被以对象的形式记录下来,我们称这个对象为事件对象。

使用场景:

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

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

如何获取

事件回调函数的【第1个参数】即所谓的事件对象,通常习惯性的将这个对数命名为 eventeve

语法:
元素.addEventListener('click', function (e) {
     // 事件回调函数的第1个参数即所谓的事件对象
      console.log(e)
    })

事件对象常用属性

  1. ev.type 当前事件的类型

  2. ev.clientX/Y 光标相对浏览器窗口的位置

  3. ev.offsetX/Y 光标相于当前 DOM 元素的位置

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

注:在事件回调函数内部通过 window.event 同样可以获取事件对象。

<body>
    <input/>

    <script>
        let input = document.querySelector('input');
        input.addEventListener('keydown',function(e){
            console.log(e)
            if(e.key == 'Enter'){
                console.log('我按下了回车键')
            }
        })
    </script>
</body>

练习:评论回车发布

<!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{
            width: 600px;
            margin: 50px auto;
            display: flex;
        }
        .wapper{
            position:relative;
            
        }
        input{
            width: 500px;
            height: 50px;
            border: 1px solid #ccc;
            background-color: #eee;
            outline: none;
            color: #242424;
            resize: none;
            border-radius: 5px;
            padding-left:10px ;
            line-height: 50px;
            flex: 1;
            opacity: 0.5;
        }
        input:focus{
            height:70px;
            border: 1px solid #0eb8e3;
            background-color: white;
        }
        button{
            width: 50px;
            height: 50px;
            background-color: #0eb8e3;
            border: none;
            outline: none;
            margin-left: 10px;
            border-radius: 5px;
            cursor: pointer;
        }
        .num{
            position: absolute;
            bottom:-20px;
            right:0px;
            display: none;
        }
        .list{
            width: 600px;
            margin: 0px auto;
            border: 1px solid #ccc;
            padding: 10px;
            border-radius: 5px;
        }
        .list .name{
            color: #0eb8e3;
            font-size: 14px;
        }
        .list .text{
            font-size: 14px;
            color: #242424;
        }
        .list .time{
            font-size: 12px;
            color: #ccc;
        }


    </style>
</head>
<body>
    <div class="box">
        <div class="wapper">
            <input maxlength="200" placeholder="发一条友善的评论"/>
            <div class="num"><span>0</span>/200</div>
        </div>
        <button>发布</button>
    </div>
    <div class="list" style="display: none;">
            <p class="name">爱探险的朵拉</p>
            <p class="text">评论</p>
            <p class="time">xx年xx月xx日</p>
    </div>
    <script>
        // 获取元素
        let input = document.querySelector('input');
        let num = document.querySelector('.num');
        let span = document.querySelector('span');
        let list = document.querySelector('.list');
        // 评论内容
        let text = document.querySelector('.text');

        // 焦点
        input.addEventListener('focus', function () {
            num.style.display = 'block'
        })
        // 失焦
        input.addEventListener('blur', function () {
            num.style.display = 'none'
        })
        // 用户输入文本事件
        input.addEventListener('input', function () {
            span.innerHTML = input.value.length
        })
        // 按下回车键
        input.addEventListener('keydown', function (e) {
            if(e.key == 'Enter'){
                if(input.value.trim() !== ''){
                    list.style.display = 'block';
                    // 文本框内容写在评论中,并去除两端空格
                    text.innerHTML = input.value;
                    console.log(input.value)
                    input.value = '';
                    span.innerHTML = 0;
                }
            }
        })
    </script>
</body>
</html>

环境对象

能够分析判断函数运行在不同环境中 this 所指代的对象。

环境对象指的是函数内部特殊的变量 this ,它代表着当前函数运行时所处的环境。

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

谁调用,this就指向谁

<body>
    <button></button>
    <script>
        function test() {
            console.log(this)
        }
        test()// 相当于window.test()  this指向window
        let button = document.querySelector('button')
        button.addEventListener('click',function(){
            console.log(this) // this指向button
        })
    </script>
</body>

回调函数

如果将函数 A 做为参数传递给函数 B 时,我们称函数 A 为回调函数 简单理解: 当一个函数当做参数来传递给另外一个函数的时候,这个函数就是回调函数

function test() {
	console.log('我是回调函数')
}
// fn 作为参数传递给定时器,fn就是回调函数
setInterval(test, 1000)


let button = document.querySelector('button')
button.addEventListener('click',function(){
    console.log('我是回调函数')
})

结论:

  1. 回调函数本质还是函数,只不过把它当成参数使用

  2. 使用匿名函数做为回调函数比较常见

案例--tab切换

<!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,li{
            list-style: none;
        }
        a{
            text-decoration: none;

        }
        .tab{
            width: 600px;
            height: 350px;
            margin: 0 auto;
            border: 1px solid #ccc;
            padding: 10px 20px;
        }
        .tab-nav{
            height: 60px;
            width: 100%;
            display: flex;
            line-height: 60px;
            justify-content: space-between;
            margin-bottom: 20px;
        }
        h3{
            font-size: 30px;
            font-weight: 500;
        }
        .tab-nav ul{
            display: flex;
        }
        ul li{
            margin:0px 20px;
            font-size: 20px;
        }
        li a{
            border-bottom: 2px solid transparent;
            color: #333;
        }
        li a.active{
            border-bottom: 2px solid #f40;
            color: #f40;

        }
        .tab-content{
            width: 100%;
            height: 250px;

        }
        .tab-content .item{
            width: 100%;
            height: 100%;
            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>
        let a = document.querySelectorAll('.tab-nav ul li a');
        for(let i=0;i<a.length;i++){
            console.log(i);
            a[i].addEventListener('mouseenter',function(){
                document.querySelector('.tab-nav ul li a.active').classList.remove('active');
                this.classList.add('active');
                document.querySelector('.tab-content .item.active').classList.remove('active');
                document.querySelector(`.tab-content .item:nth-child(${i+1})`).classList.add('active');
            })
        }
    </script>
</body>
</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>
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      table {
        width: 500px;
        margin: 100px auto;
        border: 1px solid #adabab;
        border-collapse: collapse;
        text-align: center;
      }
      th {
        background-color: rgb(8, 150, 206);
        font: bold 20px "微软雅黑";
        color: #fff;
        height: 25px;
      }
      td {
        border: 1px solid #807f7f;
        padding: 10px;
      }
      #checkAll:checked,
      .ck:checked {
        width: 20px;
        height: 20px;
      }
    </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>
      // 获取大复选框
      let checkAll = document.querySelector("#checkAll");
      // 获取所有的小复选框
      let ck = document.querySelectorAll(".ck");
      // 点击大复选框
      checkAll.addEventListener("click", function () {
        // 让小复选框的选中状态和 大复选框的选中状态保持一致
        ck.forEach((item) => {
          item.checked = this.checked;
        });
      });
      // 点击小复选框
      ck.forEach((item) => {
        item.addEventListener("click", function () {
          // 打印小复选框总个数
          //console.log(ck.length);
          // 判断小复选框是否全部选中
          //console.log(document.querySelectorAll(".ck:checked").length);
          // 如果小复选框全部选中,则让大复选框选中
          if (document.querySelectorAll(".ck:checked").length == ck.length) {
            checkAll.checked = true;
          } else {
            checkAll.checked = false;
          }
        });
      });
    </script>
  </body>
</html>

事件流

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

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

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

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

事件捕获

  • 定义:从DOM的根元素开始去执行对应的事件 (从外到里)

  • 事件捕获需要写对应代码才能看到效果

代码:

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

说明:

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

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

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

事件冒泡

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

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

  • 事件冒泡是默认存在的

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

<!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>
      .father {
        width: 200px;
        height: 200px;
        background-color: red;
      }
      .son {
        width: 100px;
        height: 100px;
        background-color: blue;
      }
    </style>
  </head>
  <body>
    <div class="father">
      <div class="son"></div>
    </div>
    <script>
      let father = document.querySelector(".father");
      let son = document.querySelector(".son");
      document.addEventListener("click", function (e) {
        console.log("我是爷爷");
      });
      father.addEventListener("click", function (e) {
        alert("father");
      });
      son.addEventListener("click", function (e) {
        alert("son");
      });
    </script>
  </body>
</html>

阻止冒泡

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

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

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

语法:

事件对象.stopPropagation()

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

<!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>
      .father {
        width: 200px;
        height: 200px;
        background-color: red;
      }
      .son {
        width: 100px;
        height: 100px;
        background-color: blue;
      }
    </style>
  </head>
  <body>
    <div class="father">
      <div class="son"></div>
    </div>
    <script>
      let father = document.querySelector(".father");
      let son = document.querySelector(".son");
      father.addEventListener("click", function (e) {
        alert("father");
      });
      son.addEventListener("click", function (e) {
        // 阻止冒泡
        e.stopPropagation();
        alert("son");
      });
    </script>
  </body>
</html>

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

语法:

e.preventDefault()
<body>
<form action="https://www.baidu.com">
  <input type="text" name="username" value="张三" />
</form>
<script>
  let form = document.querySelector("form");
  form.addEventListener("submit", function (e) {
    e.preventDefault();
    console.log("阻止默认行为");
  });
</script>
</body>

解绑事件

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

let div = document.querySelector("div");
div.onclick = function () {
    console.log("div");
};
// 解绑
div.onclick = null;


let btn = document.querySelector("button");
btn.onclick = function () {
alert("点击了");
// 解绑
btn.onclick = null;
};
  • addEventListener方式,必须使用:removeEventListener(事件类型, 事件处理函数, [获取捕获或者冒泡阶段])

let btn = document.querySelector("button");
//btn.addEventListener("click", function () {
//  alert("点击了");
//});
// 解绑
//btn.removeEventListener("click", function () {});
// 会发现没法解绑,匿名函数无法解绑,所以需要给函数命名
function fn() {
alert("点击了");
}
btn.addEventListener("click", fn);
btn.removeEventListener("click", fn);

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

  • 鼠标经过事件

    • mouseover 和 mouseout 会有冒泡效果

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

不会冒泡

<!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>
      .father {
        width: 200px;
        height: 200px;
        background-color: red;
      }
      .son {
        width: 100px;
        height: 100px;
        background-color: blue;
      }
    </style>
  </head>
  <body>
    <div class="father">
      <div class="son"></div>
    </div>
    <script>
      let father = document.querySelector(".father");
      let son = document.querySelector(".son");
      father.addEventListener("mouseover", function () {
        console.log("父移入");
      });
      father.addEventListener("mouseout", function () {
        console.log("父移出");
      });
      son.addEventListener("mouseover", function () {
        console.log("子移入");
      });
      son.addEventListener("mouseout", function () {
        console.log("子移出");
      });
    </script>
  </body>
</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>
    <style>
      .father {
        width: 200px;
        height: 200px;
        background-color: red;
      }
      .son {
        width: 100px;
        height: 100px;
        background-color: blue;
      }
    </style>
  </head>
  <body>
    <div class="father">
      <div class="son"></div>
    </div>
    <script>
      let father = document.querySelector(".father");
      let son = document.querySelector(".son");
      father.addEventListener("mouseenter", function () {
        console.log("父移入");
      });
      father.addEventListener("mouseleave", function () {
        console.log("父移出");
      });
      son.addEventListener("mouseenter", function () {
        console.log("子移入");
      });
      son.addEventListener("mouseleave", function () {
        console.log("子移出");
      });
    </script>
  </body>
</html>

两种注册事件的区别

  • 传统on注册(L0)

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

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

    • 都是冒泡阶段执行的

  • 事件监听注册(L2)

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

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

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

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

    • 匿名函数无法被解绑

事件委托

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

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

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

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

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

<body>
<ul>
  <li>第1个</li>
  <li>第2个</li>
  <li>第3个</li>
  <li>第4个</li>
  <p>我不想变色</p>
</ul>
<script>
  let ul = document.querySelector("ul");
  // 用委托实现点击li标签,li标签变色
  ul.addEventListener("click", function (e) {
    //console.log(e);
    //console.log(e.target); // 获取点击的li标签
    // 这个操作会发现,点击p标签,p标签也会变色,因为p标签也是ul的子元素
    //e.target.style.color = "green";
    //我现在只想要点击li标签,li标签变色,点击p p标签不变色
    console.log(e.target.tagName); // 获取点击的标签名
    if (e.target.tagName === "LI") {
      e.target.style.color = "green";
    }
  });
</script>
</body>

事件委托版tab切换案例

<!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,
      li {
        list-style: none;
      }
      a {
        text-decoration: none;
      }
      .tab {
        width: 600px;
        height: 350px;
        margin: 0 auto;
        border: 1px solid #ccc;
        padding: 10px 20px;
      }
      .tab-nav {
        height: 60px;
        width: 100%;
        display: flex;
        line-height: 60px;
        justify-content: space-between;
        margin-bottom: 20px;
      }
      h3 {
        font-size: 30px;
        font-weight: 500;
      }
      .tab-nav ul {
        display: flex;
      }
      ul li {
        margin: 0px 20px;
        font-size: 20px;
      }
      li a {
        border-bottom: 2px solid transparent;
        color: #333;
      }
      li a.active {
        border-bottom: 2px solid #f40;
        color: #f40;
      }
      .tab-content {
        width: 100%;
        height: 250px;
      }
      .tab-content .item {
        width: 100%;
        height: 100%;
        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>
      // 事件委托
      let ul = document.querySelector(".tab-nav ul");

      // 添加事件
      ul.addEventListener("click", function (e) {
        console.log(e.target); // 获取事件源
        //console.log(e.target.tagName); // 获取事件源标签名
        // 只有点击a才能获得到效果
        if (e.target.tagName === "A") {
          // 先移除原来的active,当前元素添加active
          document
            .querySelector(".tab-nav a.active")
            .classList.remove("active");
          e.target.classList.add("active"); // 添加active
          // 获取当前点击的a的data-id
          let i = e.target.dataset.id;
          console.log(i);
          // 先移除原来的active,当前元素添加active
          document
            .querySelector(".tab-content .item.active")
            .classList.remove("active");
          // id打印下来是字符串,需要转成数字
          //document.querySelector(`.tab-content .item:nth-child(${parseInt(i) + 1})`).classList.add("active");
          // 方法二
          // 获取item
          let item = document.querySelectorAll(".tab-content .item");
          item[i].classList.add("active");
        }
      });
    </script>
  </body>
</html>

其他事件

页面加载事件

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

事件名:load

  • 监听页面所有资源加载完毕:

    • 给 window 添加 load 事件

    • window.addEventListener('load',function(){})

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

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script>
      // 页面加载事件,页面加载完成之后在执行回调函数
      window.addEventListener("load", function () {
        console.log("页面加载完成");
        const button = document.querySelector("button");
        // 点击事件
        button.addEventListener("click", function () {
          console.log("点击了按钮");
        });
      });
    </script>
  </head>
  <body>
    <button>点击</button>
  </body>
</html>

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

事件名:DOMContentLoaded

  • 监听页面DOM加载完毕:

    • 给 document 添加 DOMContentLoaded 事件

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <script>
      document.addEventListener("DOMContentLoaded", function () {
        const button = document.querySelector("button");
        button.addEventListener("click", function () {
          console.log("按钮被点击了");
        });
      });
    </script>
  </head>
  <body>
    <button>点击</button>
  </body>
</html>

元素滚动事件

滚动条在滚动的时候持续触发的事件

很多网页需要检测用户把页面滚动到某个区域后做一些处理, 比如固定导航栏,比如返回顶部

事件名:scroll

  • 监听整个页面滚动:

    • 给 window 或 document 添加 scroll 事件

  • 监听某个元素的内部滚动直接给某个元素加即可

<!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>
      body {
        height: 2000px;
      }
    </style>
  </head>
  <body>
    <script>
      window.addEventListener("scroll", function () {
        console.log("滚动");
      });
    </script>
  </body>
</html>
页面滚动事件-获取位置

scrollLeft和scrollTop (属性)

  • 获取被卷去的大小

  • 获取元素内容往左、往上滚出去看不到的距离

  • 这两个值是可读写

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

<!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>
      body {
        height: 600px;
      }
      div {
        display: none;
        width: 200px;
        height: 200px;
        background-color: rgb(184, 124, 240);
        overflow: scroll;
        font-size: 30px;
        margin-top: 300px;
      }
    </style>
  </head>
  <body>
    <div>
      hello world hello world hello world hello world hello world hello world
      hello world hello world hello world hello world hello world hello world
    </div>
    <script>
      // 获取页面滚动高度
      window.addEventListener("scroll", function () {
        // 获取HTML元素
        const html = document.documentElement;
        // 获取页面滚动高度
        const n = html.scrollTop;
        if (n >= 100) {
          document.querySelector("div").style.display = "block";
        } else {
          document.querySelector("div").style.display = "none";
        }
      });
      // 获取元素滚动高度
      //const div = document.querySelector("div");
      //div.addEventListener("scroll", function () {
      //  console.log(div.scrollTop);
      //});
    </script>
  </body>
</html>

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

document.documentElement   HTML 文档返回对象为HTML元素
滚动到指定的坐标

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

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

<!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>
      body {
        height: 600px;
      }
      div {
        width: 200px;
        height: 200px;
        background-color: rgb(184, 124, 240);
        overflow: scroll;
        font-size: 30px;
        margin-top: 300px;
        margin-left: 1100px;
      }
    </style>
  </head>
  <body>
    <div>
      hello world hello world hello world hello world hello world hello world
      hello world hello world hello world hello world hello world hello world
    </div>
    <script>
      // 让页面滚动到指定位置
      window.scrollTo(100, 300);
        // 没看到横轴移动效果,可打印横轴移动距离验证
      console.log("当前水平滚动位置:", window.pageXOffset);
    </script>
  </body>
</html>

页面尺寸事件

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

// 窗口尺寸发生变化事件
window.addEventListener('resize', function () {
  console.log('窗口尺寸发生变化')
})

检测屏幕宽度:

<!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: 100px;*/
      height: 100px;
      background-color: red;
      display:inline-block;
    }
  </style>
</head>
<body>
  <div>1111111</div>
  <script>
    // 获取元素宽高
    let div = document.querySelector('div')
    console.log(div.clientWidth, div.clientHeight)
      //获取屏幕可视宽高(不包含滚动条)
    console.log(document.documentElement.clientWidth, document.documentElement.clientHeight)
  </script>
</body>
</html>

元素尺寸于位置

获取宽高:

  • 获取元素的自身宽高、包含元素自身设置的宽高、padding、border

  • offsetWidthoffsetHeight

  • 获取出来的是数值,方便计算

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

获取位置:

  • 获取元素距离自己定位父级元素的左、上距离

  • offsetLeftoffsetTop 注意是只读属性

<!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;
    }
    div {
      width: 100px;
      height: 100px;
      background-color: rgb(10, 242, 242);
      margin:10px;
      padding: 10px;
      border: 10px solid #084db5;
      position: relative;
    }
    p{
      width: 50px;
      height: 50px;
      background-color: red;
      margin: 10px;
    }
  </style>
</head>
<body>
  <div><p></p></div>

  <script>
    let div = document.querySelector('div')
    // 获取元素距离父元素的距离
    console.log(div.offsetLeft, div.offsetTop)// 10 10 
    console.log(div.offsetWidth, div.offsetHeight)// 140 140 包含padding 和border
    let p = document.querySelector('p')
    console.log(p.offsetLeft, p.offsetTop)// 20 20 包含margin10+父元素的padding10
    console.log(p.offsetWidth, p.offsetHeight)// 50 50
  </script>
</body>
</html>
  • element.getBoundingClientRect()

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

属性 作用 说明
scrollLeft和scrollTop 被卷去的头部和左侧 配合页面滚动来用,可读写
clientWidth 和 clientHeight 获得元素宽度和高度 不包含border,margin,滚动条 用于js 获取元素大小,只读属性
offsetWidth和offsetHeight 获得元素宽度和高度 包含border、padding,滚动条等,只读
offsetLeft和offsetTop 获取元素距离自己定位父 级元素的左、上距离 获取元素位置的时候使用,只读属性

案例:简易电梯导航滑动

  • 页面滚动到对应位置,导航显示,否则隐藏模块

  • 点击返回顶部按钮,直接回到顶部

<!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;
    }
    .header {
      width: 100%;
      height: 50px;
      line-height: 50px;
      text-align: center;
      color: #fff;
      font:bold 20px/50px '微软雅黑';
      background-color: rgb(11, 144, 239);
      position: fixed;
    }
    .content{
      height: 3000px;
      width: 1000px;
      margin: 0 auto;
      background-color: #10bcbf;
      overflow: hidden;
    }
    .second{
      width: 200px;
      height: 200px;
      background-color: rgb(248, 149, 9);
      margin-top: 500px;
    }
    .backTop{
      display: none;
      width: 50px;
      height: 50px;
      position: fixed;
      bottom: 100px;
      left:70%;
      z-index: 100;
    }
    .backTop a {
            height: 50px;
            width: 50px;
            background: url(./images/bg2.png) 0 -600px no-repeat;
            opacity: 0.35;
            overflow: hidden;
            display: block;
            text-indent: -999em;
            cursor: pointer;
        }
  </style>
</head>
<body>
  <div class="header">我是头部导航栏</div>
  <div class="content">
    <div class="second">秒杀模块</div>
  </div>
  <div class="backTop">
    <img src="./images/close2.png" alt="">
    <a href="javascript:;"></a>
  </div>
  <script>
    let header = document.querySelector('.header')
      let backTop = document.querySelector('.backTop')
      let a = document.querySelector('.backTop a')

    // 页面滚动事件
    window.addEventListener('scroll',function(){
      // 当页面滚动到一定位置时,显示返回顶部按钮
      // 即页面滚动距离大于second模块距离页面顶部的距离时,显示返回顶部按钮
      if(document.documentElement.scrollTop > document.querySelector('.second').offsetTop){
        // 当滚动超过秒杀模块时,显示头部
        header.style.top = 0
        // 当滚动超过秒杀模块时,显示返回顶部按钮
        backTop.style.display = 'block'
      }else{
        // 在秒杀模块上方时,隐藏头部
        header.style.top = '-50px'
        // 当滚动在秒杀模块上方时,隐藏返回顶部按钮
        backTop.style.display = 'none'
      }
    })
    // 点击返回顶部按钮,回到页面顶部
    a.addEventListener('click',function(){
      //document.documentElement.scrollTop = 0
      window.scrollTo(0,0)

    })
  </script>
</body>
</html>

日期对象

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

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

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

  • 获得当前时间

    • // 实例化一个Date对象
      const date = new Date();
      console.log(date);
  • 获得指定时间

    • // 指定时间
          const date = new Date('2026-9-9 12:00:00');
          console.log(date);

日期对象方法

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

方法 作用 说明
getFullYear() 获得年份 获取四位年份
getMonth() 获得月份 取值为 0 ~ 11
getDate() 获取月份中的每一天 不同月份取值也不相同
getDay() 获取星期 取值为 0 ~ 6
getHours() 获取小时 取值为 0 ~ 23
getMinutes() 获取分钟 取值为 0 ~ 59
getSeconds() 获取秒 取值为 0 ~ 59

练习:

将当前时间以:YYYY-MM-DD HH:mm 的动态形式显示在页面

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=\, initial-scale=1.0">
  <title>Document</title>
  <style>
    div{
      width: 200px;
      height: 50px;
      line-height: 50px;
      background-color: rgb(11, 230, 222);
      font-size: 10px;
      text-align: center;
    }
  </style>
</head>
<body>
  <div></div>
  <script>
    const div = document.querySelector('div');
    function getDate(){
      const date = new Date();
      // 不够两位补0
      return `今天是:${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`
    }
    // 保证一秒的间隔不会留白
    div.innerHTML = getDate();

    setInterval(function(){
      div.innerHTML = getDate();
    },1000)
  </script>
</body>
</html>

时间的其他写法

// 时间的其他形式
const date = new Date();
console.log(date.toLocaleString());//2025/10/15 09:29:36
console.log(date.toLocaleDateString());//2025/10/15
console.log(date.toLocaleTimeString());//09:29:36

时间戳

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

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

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

  • 比如 将来时间戳 2000ms - 现在时间戳 1000ms = 1000ms

  • 1000ms 转换为就是 0小时0分1秒

三种方式获取时间戳:

  • 使用 getTime() 方法

    • 可以得到指定时间时间戳

  • +new Date()

    • 可以得到指定时间时间戳

  • 使用 Date.now()

    • 无需实例化,但只能得到当前时间戳,不能得到指定时间时间戳

// getTime() 方法
const date = new Date();
console.log(date.getTime())

const date = new Date('2025-10-12 12:12:12');
console.log(date.getTime())

// +new Date    
console.log(+new Date())
console.log(+new Date('2024-5-9 12:00:00'))

// Date.now()
console.log(Date.now())
// 用getDay()方法获取星期几
let arr = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
let date = new Date()
console.log(date.getDay())
console.log(arr[date.getDay()])

练习:倒计时效果

<!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;
      }
      .container {
        width: 300px;
        height: 300px;
        text-align: center;
        margin: 0 auto;
        background-color: rgba(242, 147, 246, 0.908);
        overflow: hidden;
      }
      .title {
        padding: 20px;
        font-size: 15px;
        color: white;
      }
      .content{
        font-size: 40px;
        color: #fff;
        margin-bottom: 20px;
      }
      .countdown{
        font-size: 30px;
        color: #fff;
        display: block;
        margin-bottom: 60px;
      }
      .countdown span{
        display: inline-block;
        width: 40px;
        height: 40px;
        line-height: 30px;
        background-color: #b64809;
      }
      .tips{
        margin-top: 30px;
        color: #fff;
      }
    </style>
  </head>
  <body>
    <div class="container">
      <p class="title"></p>
      <p class="content">考公倒计时</p>
      <p class="countdown">
        <span class="day">00</span>天<span class="hour">00</span>时<span class="minute">00</span>分<span class="second">00</span>秒
      </p>
      <p class="tips">2025-11-30 09:00:00考试</p>
    </div>
    <script>
      let title = document.querySelector(".title");
      const date = new Date();
      title.innerHTML = `今天是${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日`;
      setInterval(function(){
        let day = document.querySelector(".day");
        let hour = document.querySelector(".hour");
        let minute = document.querySelector(".minute");
        let second = document.querySelector(".second");
        let now = new Date();
        let end = new Date("2025/11/30 09:00:00");
        let time = end.getTime() - now.getTime();
        let dayNum = Math.floor(time / 1000 / 60 / 60 / 24);
        let hourNum = Math.floor((time / 1000 / 60 / 60) % 24);
        let minuteNum = Math.floor((time / 1000 / 60) % 60);
        let secondNum = Math.floor((time / 1000) % 60);
        day.innerHTML = dayNum<10 ? "0"+dayNum : dayNum;
        hour.innerHTML = hourNum <10 ? "0"+hourNum : hourNum;
        minute.innerHTML = minuteNum <10 ? "0"+minuteNum : minuteNum;
        second.innerHTML = secondNum <10 ? "0"+secondNum : secondNum;
      })
    </script>
  </body>
</html>

节点操作

DOM节点

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

  • 节点类型

    • 元素节点*

      • 所有的标签 比如 body、 div

      • html 是根节点

    • 属性节点

      • 所有的属性 比如 href

    • 文本节点

      • 所有的文本

查找节点

  • 1.父节点查找:

    • parentNode 属性

    • 返回最近一级的父节点 找不到返回为null

    • <!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="parent">
          <div class="child"></div>
        </div>
        <script>
          const child = document.querySelector('.child')
          console.log(child)
          console.log(child.parentNode)
        </script>
      </body>
      </html>

练习:点击x关闭广告,使用parentNode

​
<!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: 500px;
      height: 100px;
      background-color: rgb(198, 130, 240);
      position: relative;
      text-align: center;
      line-height: 100px;
    }
    .box1 {
      width: 20px;
      height: 20px;
      line-height: 20px;
      background-color: rgb(242, 114, 229);
      position: absolute;
      top: 0;
      right: 0;
    }

  </style>
</head>
<body>
  <div class="box">
    我是广告
    <div class="box1">X</div>
  </div>
  <script>
    let box1 = document.querySelector('.box1')
    box1.addEventListener('click',function(){
      this.parentNode.style.display = 'none'
    })
  </script>
</body>
</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>
  <style>
    .box {
      width: 500px;
      height: 100px;
      background-color: rgb(198, 130, 240);
      position: relative;
      text-align: center;
      line-height: 100px;
      margin:20px;
    }
    .box1 {
      width: 20px;
      height: 20px;
      line-height: 20px;
      background-color: rgb(242, 114, 229);
      position: absolute;
      top: 0;
      right: 0;
    }
    .aaa {
      width: 500px;
      height: 100px;
      background-color: rgb(9, 138, 230);
      position: relative;
      text-align: center;
      line-height: 100px;
      margin:20px;
    }
    .bbb {
      width: 500px;
      height: 100px;
      background-color: rgb(209, 233, 26);
      position: relative;
      text-align: center;
      line-height: 100px;
      margin:20px;
    }

  </style>
</head>
<body>
  <div class="box">
    我是广告
    <div class="box1">X</div>
  </div>
  <div class="aaa">
    我是广告
    <div class="box1">X</div>
  </div>
  <div class="bbb">
    我是广告
    <div class="box1">X</div>
  </div>
  <script>
    let box1 = document.querySelectorAll('.box1')
    for (let i = 0; i < box1.length; i++) {
      box1[i].addEventListener('click', function () {
        this.parentNode.style.display = 'none'
      })
    }
  </script>
</body>
</html>
  • 2.子节点

    • childNodes

      • 获得所有子节点、包括文本节点(空格、换行)、注释节点等

    • children 属性(重点)

      • 仅获得所有元素节点

      • 返回的还是一个伪数组

<body>
  <ul>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
  </ul>
  <script>
    let ul = document.querySelector('ul')
    // 仅获得所有元素节点,返回的是一个伪数组
    console.log(ul.children)
    // 获得所有节点,包括文本节点(空格、换行)、注释节点等
    console.log(ul.childNodes)
  </script>
</body>
  • 3.兄弟关系查找:

    • 下一个兄弟节点

      • nextElementSibling 属性

    • 上一个兄弟节点

      • previousElementSibling 属性

<body>
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
  </ul>
  <script>
    let li2 = document.querySelector('ul li:nth-child(2)');
    // 获取上一个兄弟节点
    console.log(li2.previousElementSibling);
    // 获取下一个兄弟节点
    console.log(li2.nextElementSibling);
  </script>
</body>

增加节点

创建节点

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

const div = document.createElement('div');
console.log(div);
追加节点
  • 要想在界面看到,还得插入到某个父元素中;插入到父元素的最后一个子元素:

// 插入到这个父元素的最后
父元素.appendChild(要插入的元素)
<!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>
  <ul></ul>
  <script>
    const ul = document.querySelector('ul')
    const li = document.createElement('li')
    li.textContent = 'hello'
    ul.appendChild(li)
  </script>
</body>
</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>
  <ul>
    <li>1</li>
  </ul>
  <script>
    const ul = document.querySelector('ul')
    const li = document.createElement('li')
    li.textContent = 'hello'
    // 插入到父元素的最后一个子元素
    //ul.appendChild(li)
    // 插入到父元素的第一个子元素
    ul.insertBefore(li, ul.firstChild)
  </script>
</body>
</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>
  <style>
    * {
    margin: 0;
    padding: 0;
    } 
    li {
    list-style: none;
    }
    a {
        text-decoration: none;
    }
    body {
    background-color: #f3f5f7;
    }
    .box {
    width: 1200px;
    margin: 30px auto;
  }
  .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: 10px;
  }
  .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 {
      font-size: 15px;
      color: #050505;
      font-weight: 400;
      margin:20px 20px 20px 25px;
  }
  .box-bd .info {
    margin: 0 20px 0 25px;
    font-size: 12px;
    color: #999;
  }
  .box-bd .info span {
      color: #ff7c2d;
  }
  </style>
</head>
<body>
  <div class="box">
        <div class="box-hd">
            <h3>精品推荐</h3>
            <a href="#">查看全部</a>
        </div>
        <div class="box-bd">
            <ul class="clearfix">
              
            </ul>
        </div>
    </div>
  <script>
    // 1. 重构  
        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: 'Android APP 实战项目演练',
                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
            },
        ]
        const ul = document.querySelector('.box-bd ul')
        // 1. 根据数据的个数,创建 对应的小li
        for (let i = 0; i < data.length; i++) {
            let li = document.createElement('li')
            li.innerHTML = `
                <a href="#">
                    <img src="${data[i].src}" alt="">
                    <h4>
                        ${data[i].title}
                    </h4>
                    <div class="info">
                        <span>高级</span> • <span>${data[i].num}</span>人在学习
                    </div>
                </a>
            `
            // ul追加li
            ul.appendChild(li)
        }
  </script>
</body>
</html>
克隆节点

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

// 克隆一个已有的元素节点
元素.cloneNode(布尔值)
  • cloneNode会克隆出一个跟原标签一样的元素,括号内传入布尔值

    • 若为true,则代表克隆时会包含后代节点一起克隆

    • 若为false,则代表克隆时不包含后代节点 (不包含内容)

    • 默认为false

<!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>
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
  </ul>
  <script>
    let ul = document.querySelector('ul');
    //let li1 = ul.children[0].cloneNode(true);
    //ul.appendChild(li1)
    ul.appendChild(ul.children[0].cloneNode(true))
  </script>
</body>
</html>

删除节点

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

父元素.removeChild(要删除的元素)

<!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>
  <ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
  </ul>
  <script>
    let ul = document.querySelector('ul');
    ul.removeChild(ul.children[1]);
  </script>
</body>
</html>
  • 如不存在父子关系则删除不成功

  • 删除节点和隐藏节点(display:none) 有区别的: 隐藏节点还是存在的,但是删除,则从html中删除节点

M端事件

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

  • 触屏事件 touch(也称触摸事件),Android 和 IOS 都有。

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

  • 常见的触屏事件如下

触摸touch事件 说明
touchstart 手指触摸到一个DOM元素时触发
touchmove 手指在一个DOM元素上滑动时触发
touchend 手指从一个DOM元素上移开时触发
<!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: 100px;
      height: 100px;
      background-color: red;
    }
  </style>
</head>
<body>
  <div></div>
  <script>
    // 只在移动端有效果,PC端无效
    let div = document.querySelector('div')
    div.addEventListener('touchstart',function(){
      console.log('开始触摸了')
    })
    div.addEventListener('touchend',function(){
      console.log('触摸结束了')
    })
    div.addEventListener('touchmove',function(){
      console.log('触摸移动了')
    })
  </script>
</body>
</html>

js插件-swiper插件

具体步骤可查看下面网站

swiper官网: https://www.swiper.com.cn/

在线演示:Swiper演示 - Swiper中文网

基本使用流程:Swiper使用方法 - Swiper中文网

查看api文档,配置自己的插件:中文api - Swiper中文网

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

<!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="../javascript/swiper/swiper-bundle.min.css" />
  <style>
    .box{
      width: 500px;
      height: 300px;
      background-color: rgb(10, 207, 251);
      margin: 30px auto;
      overflow: hidden;
      position: relative;
    }
    html,
    body {
      position: relative;
      height: 100%;
    }

    body {
      background: #eee;
      font-family: Helvetica Neue, Helvetica, Arial, sans-serif;
      font-size: 14px;
      color: #000;
      margin: 0;
      padding: 0;
    }

    .swiper {
      width: 500px;
      height: 300px;
    }

    .swiper-slide {
      text-align: center;
      font-size: 18px;
      background: #fff;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .swiper-slide img {
      display: block;
      width: 100%;
      height: 100%;
      object-fit: cover;
    }
    
  </style>
</head>
<body>
  <div class="box">
    <div class="swiper mySwiper">
    <div class="swiper-wrapper">
      <div class="swiper-slide">Slide 1</div>
      <div class="swiper-slide">Slide 2</div>
      <div class="swiper-slide">Slide 3</div>
      <div class="swiper-slide">Slide 4</div>
      <div class="swiper-slide">Slide 5</div>
      <div class="swiper-slide">Slide 6</div>
      <div class="swiper-slide">Slide 7</div>
      <div class="swiper-slide">Slide 8</div>
      <div class="swiper-slide">Slide 9</div>
    </div>
    <div class="swiper-pagination"></div>
  </div>
  </div>
  <script src="../javascript/swiper/swiper-bundle.min.js""></script>
  <script>
    var swiper = new Swiper(".mySwiper", {
      pagination: {
        el: ".swiper-pagination",
        
      },
      autoplay:true
    });
  </script>
</body>
</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>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
    a {
      text-decoration: none;
      color:#721c24;
    }
    h1{
      text-align: center;
      color:#333;
      margin: 20px 0;
    }
    .info {
      width: 900px;
      margin: 50px auto;
      text-align: center;
    }
    .info  input, .info select {
      width: 80px;
      height: 30px;
      outline: none;
      border-radius: 5px;
      border:1px solid #b8daff;
      padding-left: 5px;
      box-sizing: border-box;
      margin-right: 15px;
    }
    .info button {
      width: 60px;
      height: 30px;
      background-color: #004085;
      outline: none;
      border: 0;
      color: #fff;
      cursor: pointer;
      border-radius: 5px;
    }
    .info .age {
      width: 50px;
    }
    table {
      margin:0 auto;
      width: 800px;
      border-collapse: collapse;
      color:#004085;
      text-align: center;
    }
    th {
      padding: 10px;
      background: #cfe5ff;
      font-size: 20px;
      font-weight: 400;
    }
    td,th {
      border:1px solid #b8daff;
    }
    td {
      padding:10px;
      color:#666;
      font-size: 16px;
    }
    tbody tr {
      background: #fff;
    }
    tbody tr:hover {
      background: #e1ecf8;
    }
  </style>
</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="女" selected>女</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>
    </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>
        
    </tbody>
  </table>
  <script>
    // 获取元素
    let uname = document.querySelector('.uname');
    let age = document.querySelector('.age');
    let gender = document.querySelector('.gender');
    let salary = document.querySelector('.salary');
    let city = document.querySelector('.city');
    let tbody = document.querySelector('tbody');
    let info = document.querySelector('.info');
    // 自定义一个空数组
    let arr = [];
    info.addEventListener('submit', function(e) {
      // 阻止跳转
      e.preventDefault();
      // 进行表单验证  如果不通过,直接中断,不需要添加数据
      let names = document.querySelectorAll('[name]');
      for(let i = 0; i < names.length; i++) {
        if(names[i].value === '') {
          alert('请输入完整信息');
          return;
        }
      }
      // 获取表单数据
      let obj = {
        Id:arr.length + 1001,
        uname: uname.value,
        age: age.value,
        gender: gender.value,
        salary: salary.value,
        city: city.value
      }
      // 将表单数据添加到数组中
      arr.push(obj);
      console.log(arr);
      // 清空表单
      this.reset();
      // 调用渲染函数
      render();
    })
    // 渲染函数,增加和删除都需要渲染
    function render() {
      // 清空tbody,把最新数组渲染到页面上
      tbody.innerHTML = '';
​
      // 遍历arr数组
      for(let i = 0; i < arr.length; i++) {
        // 生成tr
        let tr = document.createElement('tr');
        tr.innerHTML = `
            <td>${arr[i].Id}</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:" data-id="${i}">删除</a>
            </td>
        `
        // 将tr添加到tbody中
        tbody.appendChild(tr);
      }
    }
    // 删除功能
    // 事件委托
    tbody.addEventListener('click', function(e) {
      // 判断点击的是否是a标签
      if(e.target.tagName === 'A') {
        // 获取当前a标签自定义属性的id
        let index = e.target.dataset.id;
        // 删除数组中对应的数据
        arr.splice(index, 1);
        // 调用渲染函数
        render();
      }
    })
    
  </script>
</body>
</html>

Window对象

BOM

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

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

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

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

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

定时器-延时函数

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

语法:

setTimeout(回调函数, 延迟时间)

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

间歇函数 setInterval : 每隔一段时间就执行一次, , 平时省略window

清除延时函数:

clearTimeout(timerId)

注意点

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

  2. 返回值是一个正整数,表示定时器的编号

<body>
  <script>
    // 定时器之延迟函数
​
    // 1. 开启延迟函数
    let timerId = setTimeout(function () {
      console.log('我只执行一次')
    }, 3000)
​
    // 1.1 延迟函数返回的还是一个正整数数字,表示延迟函数的编号
    console.log(timerId)
​
    // 1.2 延迟函数需要等待时间,所以下面的代码优先执行
​
    // 2. 关闭延迟函数
    clearTimeout(timerId)
​
  </script>
</body>

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

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

练习:3秒钟之后消失的广告

<!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>
        img{
            position: fixed;
            bottom: 0;
            left: 0;
        }
​
    </style>
</head>
<body>
    <img src="./images/course01.png" alt="">
    <script>
        let img = document.querySelector('img')
        setTimeout(function(){
            img.style.display = 'none'
        },3000)
​
    </script>
</body>
</html>

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 等 异步任务相关添加到任务队列中(任务队列也称为消息队列)。

  • 先执行执行栈中的同步任务。

  • 异步任务放入任务队列中。

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

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

location对象

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

  • 常用属性和方法:

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

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

    • hash 属性获取地址中的啥希值,符号 # 后面部分

    • reload 方法用来刷新当前页面,传入参数 true 时表示强制刷新

<body>
    <a href="https://www.baidu.com"><span>5</span>秒后跳转百度</a>
    <script>
        let a = document.querySelector('a')
        let span = document.querySelector('span')
        let num = 5
        let timer = setInterval(function(){
            num--
            span.innerHTML = num
            if(num == 0){
                clearInterval(timer)
                location.href = 'https://www.baidu.com'
                a.innerHTML = '跳转百度'
            }
        },1000)
​
    </script>
</body>
<body>
  <form>
    <input type="text" name="search"> <button>搜索</button>
  </form>
  <a href="#/music">音乐</a>
  <a href="#/download">下载</a>
​
  <button class="reload">刷新页面</button>
  <script>
    // location 对象  
    // 1. href属性 (重点) 得到完整地址,赋值则是跳转到新地址
    console.log(location.href)
    // location.href = 'http://www.baidu.com'
​
    // 2. search属性  得到 ? 后面的地址 
    console.log(location.search)  // ?search=笔记本
​
    // 3. hash属性  得到 # 后面的地址
    console.log(location.hash)
​
    // 4. reload 方法  刷新页面
    const btn = document.querySelector('.reload')
    btn.addEventListener('click', function () {
      // location.reload() // 页面刷新
      location.reload(true) // 强制页面刷新 ctrl+f5
    })
  </script>
</body>

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://baidu.com'
  }})();

histroy对象

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

使用场景

history对象一般在实际开发中比较少用,但是会在一些OA 办公系统中见到。

对象方法 作用
back() 可以后退功能
forward() 前进功能
go(参数) 前进后退功能,参数如果是1千金一和页面,如果是-1后退一个页面
<body>
  <button class="back">←后退</button>
  <button class="forward">前进→</button>
  <script>
    // histroy对象
​
    // 1.前进
    const forward = document.querySelector('.forward')
    forward.addEventListener('click', function () {
        location.href = 'http://www.baidu.com'
      // history.forward() 
      history.go(1)
    })
    // 2.后退
    const back = document.querySelector('.back')
    back.addEventListener('click', function () {
      // history.back()
      history.go(-1)
    })
  </script>
</body>

本地存储

本地存储介绍

以前我们页面写的数据一刷新页面就没有了,是不是?

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

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

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

    • 3、容量较大,sessionStorage和localStorage约 5M 左右 l 常见的使用场景: l https://todomvc.com/examples/vanilla-es6/ 页面刷新数据不丢失

本地存储分类- localStorage

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

  • 特性:

    • 可以多窗口(页面)共享(同一浏览器可以共享)

    • 以键值对的形式存储使用

  • 语法

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

    • 获取数据: localStorage.getItem(key)

    • 删除数据: localStorage.removeItem(key)

<body>
  <script>
    // 本地存储 - localstorage 存储的是字符串 
    // 1. 存储
    localStorage.setItem('age', 18)
​
    // 2. 获取
    console.log(localStorage.getItem('age'))
    console.log(typeof localStorage.getItem('age'))//string
​
    // 3. 删除
    localStorage.removeItem('age')
  </script>
</body>
  • 在浏览器中的检查中--application中查看

  • 本地存储只能存储字符串

本地存储分类- sessionStorage

特性:

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

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

  • 以键值对的形式存储使用

  • 用法跟localStorage 基本相同

存储复杂数据类型

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

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

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

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

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

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

  • 将JSON字符串转换成对象 取出 时候使用

<body>
  <script>
    let obj = {
        name: '张三',
        age: 18,
        sex: '男'
    }
    localStorage.setItem('info', JSON.stringify(obj))
    // 取
    // console.log(localStorage.getItem('info'))
    // 转换取
    console.log(JSON.parse(localStorage.getItem('info')))
  </script>
</body>

数组方法

数组中map方法 迭代数组

map 可以处理数据,并且返回新的数组

<body>
  <script>
  const arr = ['red', 'blue', 'green']
  // 1. 数组 map方法 处理数据并且 返回一个数组
   const newArr = arr.map(function (ele, index) {
    // console.log(ele)  // 数组元素
    // console.log(index) // 索引号
    return ele + '色'
    })
console.log(newArr)// [red色,...]
</script>
</body>

map 也称为映射。映射是个术语,指两个元素的集之间元素相互“对应”的关系。

map重点在于有返回值,forEach没有返回值(undefined)

数组join方法

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

语法:

<body>
  <script>
    const arr = ['red', 'blue', 'green']
​
    // 1. 数组 map方法 处理数据并且 返回一个数组
    const newArr = arr.map(function (ele, index) {
      // console.log(ele)  // 数组元素
      // console.log(index) // 索引号
      return ele + '色'
    })
    console.log(newArr)
​
    // 2. 数组join方法  把数组转换为字符串
    // 小括号为空则逗号分割
    console.log(newArr.join())  // red色,blue色,green色
    // 小括号是空字符串,则元素之间没有分隔符
    console.log(newArr.join(''))  //red色blue色green色
    console.log(newArr.join('|'))  //red色|blue色|green色
  </script>
</body>

案例--学生就业信息表

如果本地存储有数据,则返回 JSON.parse() 之后的对象 如果本地存储没有数据,则声明一个空的数组

遍历数组, td里面填写对应td数据, 并追加给 tbody

尽量减少dom操作,所以此处我们不在使用创建节点,追加节点方式

我们使用map方法遍历数组,直接返回 整个tr, 里面包含所有修改后的 tr 标签, 里面更换数据

把返回的结果, 通过 innerHTML 赋值给 tbody

  • 点击删除模块

(1) 采用事件委托形式,给 tbody 注册点击事件 (2) 点击链接,要删除的是对应数组里面的这个数据,而不是删除dom节点,如何找到这个数据? (3) 前面渲染数据的时候,动态给a链接添加 自定义属性 data-id=“0”,这样点击当前对象就知道索引号了 (4) 根据索引号,利用 splice 删除这条数据 (5) 写入本地存储, 记得一定要把数组 利用 JSON.stringify()存储为字符串 (6) 重新渲染

<!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;
    }
    a {
      text-decoration: none;
      color:#721c24;
    }
    h1{
      text-align: center;
      color:#333;
      margin: 20px 0;
    }
    .info {
      width: 900px;
      margin: 50px auto;
      text-align: center;
    }
    .info  input, .info select {
      width: 80px;
      height: 30px;
      outline: none;
      border-radius: 5px;
      border:1px solid #b8daff;
      padding-left: 5px;
      box-sizing: border-box;
      margin-right: 15px;
    }
    .info button {
      width: 60px;
      height: 30px;
      background-color: #004085;
      outline: none;
      border: 0;
      color: #fff;
      cursor: pointer;
      border-radius: 5px;
    }
    .info .age {
      width: 50px;
    }
    .title{
        text-align: right;
        margin:0 auto;
        width: 800px;
        color:#004085;
        background-color: aliceblue;
    }
    table {
      margin:10px auto;
      width: 800px;
      border-collapse: collapse;
      color:#004085;
      text-align: center;
    }
    th {
      padding: 10px;
      background: #cfe5ff;
      font-size: 20px;
      font-weight: 400;
    }
    td,th {
      border:1px solid #b8daff;
    }
    td {
      padding:10px;
      color:#666;
      font-size: 16px;
    }
    tbody tr {
      background: #fff;
    }
    tbody tr:hover {
      background: #e1ecf8;
    }
  </style>
</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="女" selected>女</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>
    </select>
    <button class="add">添加</button>
  </form>
​
  <h1>就业榜</h1>
  <div class="title">
    共有 <span class="count">0</span> 条数据
  </div>
  <table>
    <thead>
      <tr>
        <th>学号</th>
        <th>姓名</th>
        <th>年龄</th>
        <th>性别</th>
        <th>薪资</th>
        <th>就业城市</th>
        <th>录入时间</th>
        <th>操作</th>
      </tr>
    </thead>
    <tbody>
        
    </tbody>
  </table>
  <script>
    // 获取元素
    let uname = document.querySelector('.uname');
    let age = document.querySelector('.age');
    let gender = document.querySelector('.gender');
    let salary = document.querySelector('.salary');
    let city = document.querySelector('.city');
    let tbody = document.querySelector('tbody');
    let info = document.querySelector('.info');
    // 自定义一个空数组
    let arr = [];
    info.addEventListener('submit', function(e) {
      // 阻止跳转
      e.preventDefault();
      // 进行表单验证  如果不通过,直接中断,不需要添加数据
      let names = document.querySelectorAll('[name]');
      for(let i = 0; i < names.length; i++) {
        if(names[i].value === '') {
          alert('请输入完整信息');
          return;
        }
      }
      const currentTime = new Date().toISOString().replace('T', ' ').split('.')[0];
      // 获取表单数据
      let obj = {
        Id:arr.length + 1001,
        uname: uname.value,
        age: age.value,
        gender: gender.value,
        salary: salary.value,
        city: city.value,
        time:currentTime
      }
      // 将表单数据添加到数组中
      arr.push(obj);
      console.log(arr);
      localStorage.setItem('arr', JSON.stringify(arr));
      // 获取数据
      reArr = JSON.parse(localStorage.getItem('arr')) || [];
      console.log(reArr)
      // 清空重置表单
      this.reset();
      // 调用渲染函数
      render();
    })
    // 渲染函数,增加和删除都需要渲染
    function render() {
      // 清空tbody,把最新数组渲染到页面上
      tbody.innerHTML = '';
​
      // 遍历arr数组
      let trArr = reArr.map(function(ele,index){
        return `<tr>
                    <td>${ele.Id}</td>
                    <td>${ele.uname}</td>
                    <td>${ele.age}</td>
                    <td>${ele.gender}</td>
                    <td>${ele.salary}</td>
                    <td>${ele.city}</td>
                    <td>${ele.time}</td>
                    <td>
                    <a href="javascript:" data-id="${index}">删除</a>
                    </td>
                </tr>
        `
      })
        console.log(trArr)
        // 将数组转换为字符串 join
        let trStr = trArr.join('');
        // 将字符串添加到tbody中
        tbody.innerHTML = trStr;
        // 获取数量
        document.querySelector('.title span').innerHTML = reArr.length;
      }
    // 删除功能
    // 事件委托
    tbody.addEventListener('click', function(e) {
      // 判断点击的是否是a标签
      if(e.target.tagName === 'A') {
        // 获取当前a标签自定义属性的id
        let index = e.target.dataset.id;
        console.log(index);
        // 确定是否删除
        if(confirm('确定删除吗?')) {
            // 删除数组中对应的数据
            reArr.splice(index, 1);
            // 更新localStorage
            localStorage.setItem('arr', JSON.stringify(reArr));
            // 调用渲染函数
            render();
        }
        
​
      }
    })
    
  </script>
</body>
</html>

正则表达式

正则表达式(Regular Expression)是一种字符串匹配的模式(规则)

使用场景:

  • 例如验证表单:手机号表单要求用户只能输入11位的数字 (匹配)

  • 过滤掉页面内容中的一些敏感词(替换),或从字符串中获取我们想要的特定部分(提取)等

正则基本使用

  1. 定义规则

    const reg =  /表达式/
    • 其中/ /是正则表达式字面量

    • 正则表达式也是对象

  2. 使用正则

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

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

<body>
  <script>
    // 正则表达式的基本使用
    const str = 'web前端开发'
    // 1. 定义规则
    const reg = /web/
​
    // 2. 使用正则  test()
    console.log(reg.test(str))  // true  如果符合规则匹配上则返回true
    console.log(reg.test('java开发'))  // false  如果不符合规则匹配上则返回 false
  </script>
</body>

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

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

语法:reg.exec(str)

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

元字符

  1. 普通字符:

  • 大多数的字符仅能够描述它们本身,这些字符称作普通字符,例如所有的字母和数字。

  • 普通字符只能够匹配字符串中与它们相同的字符。

  • 比如,规定用户只能输入英文26个英文字母,普通字符的话 /[abcdefghijklmnopqrstuvwxyz]/

  1. 元字符(特殊字符)

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

  • 比如,规定用户只能输入英文26个英文字母,换成元字符写法: /[a-z]/

  • 分类:边界符、量词、字符类

边界符

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

边界符 说明
^ 表示匹配行首的文本(以谁开始)
$ 表示匹配行尾的文本(以谁结束)
<body>
  <script>
    // 元字符之边界符
    // 1. 匹配开头的位置 ^
    const reg = /^web/
    console.log(reg.test('web前端'))  // true
    console.log(reg.test('前端web'))  // false
    console.log(reg.test('前端web学习'))  // false
    console.log(reg.test('we'))  // false
​
    // 2. 匹配结束的位置 $
    const reg1 = /web$/
    console.log(reg1.test('web前端'))  //  false
    console.log(reg1.test('前端web'))  // true
    console.log(reg1.test('前端web学习'))  // false
    console.log(reg1.test('we'))  // false  
​
    // 3. 精确匹配 ^ $
    const reg2 = /^web$/
    console.log(reg2.test('web前端'))  //  false
    console.log(reg2.test('前端web'))  // false
    console.log(reg2.test('前端web学习'))  // false
    console.log(reg2.test('we'))  // false 
    console.log(reg2.test('web'))  // true
    console.log(reg2.test('webweb'))  // flase 
  </script>
</body>
量词

量词用来设定某个模式重复次数

量词 说明
* 重复零次或更多次
+ 重复一次或更多次
重复零次或一次
{n} 重复n次
{n,} 重复n次或更多次
{n,m} 重复n到m次

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

<body>
  <script>
    // 元字符之量词
    // 1. * 重复次数 >= 0 次
    const reg1 = /^w*$/
    console.log(reg1.test(''))  // true
    console.log(reg1.test('w'))  // true
    console.log(reg1.test('ww'))  // true
    console.log('-----------------------')
​
    // 2. + 重复次数 >= 1 次
    const reg2 = /^w+$/
    console.log(reg2.test(''))  // false
    console.log(reg2.test('w'))  // true
    console.log(reg2.test('ww'))  // true
    console.log('-----------------------')
​
    // 3. ? 重复次数  0 || 1 
    const reg3 = /^w?$/
    console.log(reg3.test(''))  // true
    console.log(reg3.test('w'))  // true
    console.log(reg3.test('ww'))  // false
    console.log('-----------------------')
​
​
    // 4. {n} 重复 n 次
    const reg4 = /^w{3}$/
    console.log(reg4.test(''))  // false
    console.log(reg4.test('w'))  // flase
    console.log(reg4.test('ww'))  // false
    console.log(reg4.test('www'))  // true
    console.log(reg4.test('wwww'))  // false
    console.log('-----------------------')
​
    // 5. {n,} 重复次数 >= n 
    const reg5 = /^w{2,}$/
    console.log(reg5.test(''))  // false
    console.log(reg5.test('w'))  // false
    console.log(reg5.test('ww'))  // true
    console.log(reg5.test('www'))  // true
    console.log('-----------------------')
​
    // 6. {n,m}   n =< 重复次数 <= m
    const reg6 = /^w{2,4}$/
    console.log(reg6.test('w'))  // false
    console.log(reg6.test('ww'))  // true
    console.log(reg6.test('www'))  // true
    console.log(reg6.test('wwww'))  // true
    console.log(reg6.test('wwwww'))  // false
​
    // 7. 注意事项: 逗号两侧千万不要加空格否则会匹配失败
​
  </script>
字符类

某些常见模式的简写方式,区分字母和数字

  • []里面加上 ^ 表示取反符号

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

    • 注意要写到中括号里面

  • 匹配除换行符之外的任何单个字符

    <body>
      <script>
        // 元字符之范围  []  
        // 1. [abc] 匹配包含的单个字符, 多选1
        const reg1 = /^[abc]$/
        console.log(reg1.test('a'))  // true
        console.log(reg1.test('b'))  // true
        console.log(reg1.test('c'))  // true
        console.log(reg1.test('d'))  // false
        console.log(reg1.test('ab'))  // false
    ​
        // 2. [a-z] 连字符 单个
        const reg2 = /^[a-z]$/
        console.log(reg2.test('a'))  // true
        console.log(reg2.test('p'))  // true
        console.log(reg2.test('0'))  // false
        console.log(reg2.test('A'))  // false
        // 想要包含小写字母,大写字母 ,数字
        const reg3 = /^[a-zA-Z0-9]$/
        console.log(reg3.test('B'))  // true
        console.log(reg3.test('b'))  // true
        console.log(reg3.test(9))  // true
        console.log(reg3.test(','))  // flase
    ​
        // 用户名可以输入英文字母,数字,可以加下划线,要求 6~16位
        const reg4 = /^[a-zA-Z0-9_]{6,16}$/
        console.log(reg4.test('abcd1'))  // false 
        console.log(reg4.test('abcd12'))  // true
        console.log(reg4.test('ABcd12'))  // true
        console.log(reg4.test('ABcd12_'))  // true
    ​
        // 3. [^a-z] 取反符
        const reg5 = /^[^a-z]$/
        console.log(reg5.test('a'))  // false 
        console.log(reg5.test('A'))  // true
        console.log(reg5.test(8))  // true
    ​
      </script>
    </body>
  • 预定义:指的是 某些常见模式的简写方式。

字符类 说明
\d 匹配0-9之间的任一数字,相当于[0-9]
\D 匹配所有0-9以外的字符,相当于0-9
\w 匹配任意的字母,数字和下划线,相当于[A-Za-z0-9]
\W 除所有字母,数字和下划线以外的字符,相当于A-Za-z0-9
\s 匹配空格(包括换行符、制表符、空格等)相当于[\t\r\n\v\f]
\S 匹配非空格的字符,相当于\t\r\n\v\f

案例

用户名要求用户英文字母,数字组成,并且用户名长度为 6~16位

<!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>
        span {
            display: inline-block;
            width: 250px;
            height: 30px;
            vertical-align: middle;
            line-height: 30px;
            padding-left: 15px;
        }
​
        .error {
            color: red;
            background: url(./images/error1.png) no-repeat left center;
        }
​
        .right {
            color: green;
            background: url(./images/right.png) no-repeat left center;
        }
    </style>
</head>
​
<body>
    <input type="text">
    <span></span>
    <script>
        let reg = /^[a-zA-Z0-9]{6,16}$/;
        const input = document.querySelector('input');
        const span = document.querySelector('span');
        input.addEventListener('blur', function () {
            if( reg.test(this.value)){
                span.className = 'right';
                span.innerHTML = '输入正确';
            }else{
                span.className = 'error';
                span.innerHTML = '输入错误';
            }
        })
    </script>
​
</body>
​
</html>

替换和修饰符

replace 替换方法,可以完成字符的替换

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

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

<body>
  <script>
    // 替换和修饰符
    const str = '欢迎大家学习前端,相信大家一定能学好前端,都成为前端大神'
    // 1. 替换  replace  需求:把前端替换为 web
    // 1.1 replace 返回值是替换完毕的字符串
    // const strEnd = str.replace(/前端/, 'web') 只能替换一个
​
    // 2. 修饰符 g 全部替换
    const strEnd = str.replace(/前端/g, 'web')
    console.log(strEnd) 
  </script>
</body>

案例--注册验证input输入格式

<!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;
            list-style: none;
        }
​
        input {
            outline: none;
        }
​
        button {
            outline: none;
        }
​
        body {
            background-color: #3d9bcc;
        }
​
        #box {
            width: 415px;
            height: 350px;
            background-color: #ffffff;
            margin: auto;
            top: 0;
            bottom: 0;
            left: 0;
            right: 0;
            border-radius: 10px;
            position: absolute;
        }
​
        #box h2 {
            height: 105px;
            text-align: center;
            line-height: 105px;
            font-size: 25px;
        }
​
        #box>div {
            position: relative;
            text-align: center;
​
        }
​
        #box>div input {
            width: 365px;
            height: 45px;
            border: 1px solid #efeef1;
            border-radius: 10px;
            margin-bottom: 20px;
            padding-left: 10px;
        }
​
        #box>div p {
            position: absolute;
            overflow: hidden;
            top: 40px;
            left: 20px;
            color: red;
            font-size: 12px;
            height: 0px;
            overflow: hidden;
            transition: height .10s;
        }
​
        #box button {
            width: 375px;
            height: 45px;
            display: block;
            color: #002037;
            font-size: 15px;
            font-weight: bold;
            background-color: #2c7eb9;
            border-radius: 10px;
            border: 1px solid #e5faff;
            margin: 15px auto;
            cursor: pointer;
        }
​
        #box button:hover {
            background-color: rgb(107, 152, 249);
        }
​
        #box p {
            text-align: center;
            margin-top: 10px;
        }
    </style>
</head>
​
<body>
    <div id="box">
        <h2>会员账号注册</h2>
        <div>
            <input type="text" placeholder="账号">
            <p></p>
        </div>
        <div>
            <input type="text" placeholder="密码">
            <p></p>
        </div>
        <button>注册</button>
    </div>
    <script>
        let input = document.querySelectorAll('#box>div input');
        let p = document.querySelectorAll('#box>div p');
        //用户名
        let user = /^[\w\u4e00-\u9fa5]{2,8}$/;
        //密码
        let pass = /^(?=.*[0-9])(?=.*[a-zA-Z])[\w@#^\*\(\)";:,.]{4,12}$/;
​
        //优化
        input[0].onchange = function () {
            handler(0, '用户名格式错误');
        }
        input[1].onchange = function () {
            handler(1, '密码格式错误');
        }
        function handler(index, str) {
            let value = input[index].value.trim();//去除空格
            let flag = index == 0 ? user.test(value) : pass.test(value);
            if (!flag) {
                //p[index].innerHTML = index == 0 ? '用户名格式错误' : '密码格式错误';
                p[index].innerHTML = str;
                p[index].style.height = '20px';
                setTimeout(function () {
                    p[index].style.height = '0px';
                    p[index].innerHTML = '';
                }, 2000)
            }
            return flag;
        }
​
​
    </script>
</body>
​
</html>

change 事件

给input注册 change 事件,值被修改并且失去焦点后触发

判断是否有类

元素.classList.add('类名')//添加类名
元素.classList.remove('类名')//删除类名
元素.classList.toggle('类名')//切换类名
元素.classList.contains('类名')//判断是否包含某个类名 

元素.classList.contains() 看看有没有包含某个类,如果有则返回true,么有则返回false