JavaScriptAPI-DOM

152 阅读16分钟

1.const let 是块级作用域 var没有块级作用域,var只有函数和全局作用域
2.const let 不存在变量声明的提前,var有声明的提前,所以const和let在声明变量或者常量 之前,是没办法获取到的,称为暂时性死区temporal dead zone
3.const let是ES6提出的,var是ES5
4.const 声明的是常量,常量不能被修改,let和var声明的是变量,可以被修改
5.const在声明时必须赋值,而let和var不需要
6.let 和const不能重复声明同一个值:如 let a=1 ; let a =2 这样是不被允许的,但var可以,最后一个var声明的值会覆盖之前的 如:var b =1 ;var b =2 console.log(b) 结果为2


API:通过JavaScript获取到标签元素,然后会自动转换为Object类型,也就是DOM对象,JS通过操作DOM对象,对网页进行功能操作。

本质就是通过JS去操作html和浏览器,也可以细分为DOM、BOM

DOM

DOM:文档对象模型,用来呈现以及与任意HTML或XML文档交互的API

简而言之是:浏览器为我们提供的一套专门用来操作网页内容的功能。

DOM树

图片.png

DOM对象

DOM对象:浏览器根据html标签自动生成的一个对象

let btn = document.querySelector('button')

这样就可以拿到button了,并且是一个对象形式。对象就意味着他有方法、属性值。

所有标签的属性都可以在这个对象上面找到,如果对这个对象的属性进行修改,会自动映射到该标签上

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

DOM的顶层对象是document对象,他提供的方法和属性都是用来访问和操作网页内容的:document.write(),网页所有内容都在document中

获取DOM对象

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

querySelector

1.1 选择匹配的第一个元素(可能会有很多相同的标签元素,但是我只获取第一个出现的)
document.querySelector('css选择器')

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

<div>测试1</div>
<div>测试2</div>
<script>
    //获取 测试1 元素
    let div = document.querySelector('div')
</script>

1.2 除了能够获取标签元素外,还可以根据类来获取

<div>测试1</div>
<div class = ".two">测试2</div>
<script>
    let two = document.querySelector('.two')
</script>

1.3 通过css来获取

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
<script>
    let li3 = document.querySelector('ul li:last-child')
</script>

querySelectorAll

选择匹配多个元素
document.querySelectorAll('css选择器')

querySelector返回值是一个对象,querySelectorAll返回的是NodeList对象集合

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
let lis = document.querySelectorAll('ul li')

注:querySelector()可以获取到元素后,对他进行修改操作,而querySelectorAll()不可以直接修改,只能通过遍历的方式,一次给里面的元素做修改

document.querySelectorAll('css选择器')得到的是一个伪数组,他有长度、有索引号,但是没有pop()和push()这种数组方法。要想得到里面的每一个对象,需要通过for来获取

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
<script>
    let lis = document.querySelectorAll('ul li')
    for(let i=0;i<lis.lengths;i++){
        console.log(lis[i])
    }
    </script>

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

其他获取DOM元素(了解) 根据id获取一个元素
document.getElementById('nav')
根据标签获取一类元素,获取页面所有的div
document.getElementByTagName('div')
根据类名获取元素,获取页面所有类名为w的
'document.getElementByClassName('w')'

图片.png

设置修改DOM内容

document.write()方法

只能将文本内容追加到</body>前面的位置,文本中包含的标签会被解析。

document.write('Hello');
document.write('<h3>你好!</h3>')

对象.innerText属性

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

对象.innerText:标签对象中的文字

<div>pink</div>

<script>

let div = document.querySelector('div')
div.innerText = "<strong>不要pink色</strong>"

</script>

直接输出<strong>不要pink色</strong>

对象.innerHTML属性

和innerText用法几乎一样,不同的是:innerHTML可以解析标签

直接输出加粗后的不要pink色

图片.png

案例

图片.png

<style>
    div{
        display:inline-block;
        width:150px;
        height:30px;
        border:1px solid pink;
        //相对于该元素所在行的基线的垂直对齐
        vertical-align:middle;
        text-align:center;
        line-height:30px;
    }
</style>
<body>
    抽中的选手:<div></div>
    <script>
    //随机数函数
        function getRandom(min,max){
            return Math.floor(Math.random()*(max-min+1))+min
        }
    //声明一个数组
        let arr = ['张飞','关羽','赵云']
    //生成一个随机数,并作为数组的下标
        let random = getRandom(0,arr.length-1)
        
        let div = document.querySelector('div')
        div.innerHTML = arr[random];
        
    //然后从数组中删除已经抽到过的名字
    //从哪开始删,删几个
    arr.splice(random,1)
    
    </script>
</body>

设置修改DOM元素属性

常见的属性比如:src、href、title等,通过src更换图片:

对象.属性 = 值

//获取元素
let pic = document.querySelector('img')
//操作元素
pic.src = './images/02.jpg'
pic.title = '这是一个图片'

案例

图片.png

图片.png

<img src = "./images/01.png">

<script>
1.获取图片元素
let img = document.querySelector('img')

2.随机得到图片序号
function getRandom(min,max){
    return Math.floor(Math.random()* (max-min+1))+min
}

let num = getRandom(1,6)

3.完成src属性的赋值
img.src = `./images/${num}.png`

</script>

修改样式属性

还可以通过JS设置、修改标签元素的样式属性,常见有:轮播图小圆点自动更换颜色,点击按钮可以滚动图片等

通过style属性操作CSS、操作类名来控制CSS、通过classList操作类来控制CSS

图片.png

通过style操作CSS

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

图片.png

一定记得要加单位!!!

<head>
    <style>
        div{
            width:300px;
            height:300px;
            background-color:pink;
        }
    </style>
</head>
<body>
    <div></div>
    <script>
        let div = document.querySelector('div');
        div.style.backgroundColor = 'blue';
        div.style.width = '400px';
        div.style.marginTap = '100px';
    </script>
</body>

注意:JavaScript中没有-这种连接符,而是采用驼峰命名法:CSS中background-color在JS中为:backgroundColor

我们修改后的样式,会变成行内形式

图片.png

修改随机背景

因为background-image是body的唯一标签,因此不能向上方那样书写,而是:document.body.style,直接通过document拿到body,不再需要querySelector

<style>
    body{
        background-iamge:url(./images/...png)
    }
</style>

<script>
    function getRandom(min,max){
        return Math.floor(Math.random()*(max-min+1))+min;
    }

    let num = getRandom(1,6)
    document.body.style.backgroundImage = `url(./images/${num}.png)`
</script>

设置修改元素样式属性

如果修改的样式比较多,通过修改style属性会比较繁琐,我们可以通过修改css类名的形式

元素.className = 'active'

div{
    
}
.active{
    background-color:pink;
    margin-left:100px;
}
<div></div>

<script>
let box = document.querySelector('div')

box.style.width = '500px'
box.className = 'active'
</script>

如果想要给多个类就会存在问题:

box.className = 'active1'
box.className = 'active2'

只会存在active2,因为这是赋值操作,会覆盖前面的一次赋值。因此className会存在缺陷。

修改:

box.className = 'active1 active2'

querySelector也可以获取到类

<div class ="one"></div>
<div id ="two"></div>

let box1 = document.querySelector('one')
let box2 = document.querySelector('#two')

classList

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

追加一个类:不会覆盖之前的类 元素.classList.add('类名')

删除一个类: 元素.classList.remove('类名')

切换一个类:如果我本身存在类,那么使用toggle就会将这个类删除,如果我没有这个类,那么就会追加这个类 元素.classList.toggle('类名')

.active1{

}
.active2{

}
<div class = "one"></div>

let box = document.querySelector('div')

box.classList.add('active1')
box.classList.add('active2')

box.classList.remove('active1')

box.classList.toggle('active1')

设置修改表单元素属性

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

图片.png

表单.value = '用户名'
表单.type = 'password'

<input type = "text" value = "请输入">
<script>
    let input = document.querySelector('input')
    input.value = '华为'
    input.type = 'password'
</script>

表单属性中,添加就有效果、移除就没有效果。一律使用布尔值表示,true:添加了该属性;false:移除了该属性

比如:disabled、checked、selected

<button disabled>按钮</button>
let btn = querySelector('button')
btn.disabled = 'false' 启用

禁用状态,不给你选(短信验证码,获取一次后,60秒内不能再次获取)

<input type = "checkbox' name = "" id = "" class = "agree">

let input = document.querySelector('.agree')
input.checked = true

定时器-间歇函数

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

图片.png

开启定时器:setInterval(函数,间隔时间)

作用:每隔一段时间调用这个函数,间隔时间的单位是毫秒

<script>
    setInterval(function(){
        每个一秒钟执行这个函数
    },1000)
    
---------------两种写法---------------
    function show(){
        函数
    }
    不能写小括号,写了小括号表示调用
    setInterval(show,1000)
</script>

函数名字不需要加小括号!!!

let timer = setInterval(show,1000)定时器返回的是定时器的序号,如果只有一个定时器,返回的就是1,如果有两个定时器,那么两个定时器分别返回1、2

关闭定时器

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

let timer = setInterval(show,1000)
clearInterval(timer);

清除定时器在定时器里面完成操作

图片.png

倒计时-案例

需求:按钮60秒之后才可以使用

图片.png

<textarea name="" id="" cols="30" rows="10">

        用户协议,测试

    </textarea>

    <br>

    <button class = "btn" disabled>已阅读用户协议(6)</button>

    <script>

        let btn = document.querySelector('.btn')

        let i = 6;

        let timer = setInterval(function(){

        i--

        btn.innerHTML = `已阅读用户协议(${i})`

            if(i == 0){

                btn.disabled = false;

                btn.innerHTML = '我同意该协议'

                clearInterval(timer);

            }

        },1000)

    </script>

焦点图-案例

图片.png

图片.png

<div>

        <img src="./uploads/banner_1.jpg" alt="">

        <div class = "text">

            第一张

        </div>

    </div>

    <script>

        let data = [

            {

                imgSrc:'./uploads/banner_1.jpg',

                title:'第一张'

            },

            {

                imgSrc:'./uploads/banner_2.jpg',

                title:'第二张'

            },

            {

                imgSrc:'./uploads/banner_3.jpg',

                title:'第三张'

            },

            {

                imgSrc:'./uploads/banner_4.jpg',

                title:'第四张'

            }

        ]

        let img = document.querySelector('img');

        let i = 0

        let text = document.querySelector('.text')

        setInterval(function(){

            i++;

            img.src = data[i].imgSrc;

            text.innerHTML = data[i].title

            console.log(i);

            if(i==data.length-1){
            // 避免i++后,i的取值为1的情况
            // 这样i++后,i的取值是0
                i = -1

            }

        },3000)

    </script>

事件

事件:编程时,系统内发生的动作或者发生的事情。比如:用户在网页上单击了一个按钮、鼠标经过、鼠标离开

事件监听:一旦有事件触发,就立即调用一个函数做出响应,也叫:注册事件。

语法:元素.addEventListener('事件',要执行的函数)

事件监听的三要素:
事件源:哪个DOM元素被事件触发了,要获取DOM元素
事件:用什么方式触发,比如鼠标单击click、鼠标经过mouseover等
事件调用的函数:要做什么事

事件类型要加引号,函数是点击之后再去执行,每次点击都会执行一次!

let btn = document.querySelector('button')

btn.addEventListener('click',function(){
    alert('被点击了')
})

图片.png

图片.png

这里是获取erweima而不是img,因为我点了x后,所有的全部隐藏

    * {
        margin: 0;
        padding: 0;
    }

    .erweima {
        position: relative;
        width: 160px;
        height: 160px;
        margin: 100px auto;
        border: 1px solid #ccc;
    }

    .erweima i {
        position: absolute;
        left: -13px;
        top: 0;
        width: 10px;
        height: 10px;
        border: 1px solid #ccc;
        font-size: 12px;
        line-height: 10px;
        color: #ccc;
        font-style: normal;
        
        cursor:pointer设定鼠标的形状为一只伸出食指的手,
        这也是绝大多数浏览器里面鼠标停留在网页链接上方时候的样式
        另外可以选择其他鼠标指针样式,
        常用的有default 箭头,crosshair 十字,progress 箭头和沙漏等等

        cursor: pointer;
    }
<div class = "erweima">
    <img src = "./images/code.png">
    <i class = "close_btn">x</i>
</div>
<script>
    let i = document.querySelector('.close_btn')
    let erweima = document.querySelector('.erweima')
    i.addEventListener('click',function(){
        erweima.style.display = 'none'
    })
</script>

案例-随机点名

图片.png

<body>

    <div>

        <textarea rows="2" cols="10"></textarea>

        <button>点击随机点名</button>

    </div>

    <script>

        let names = ['刘备', '关羽', '张飞', '赵云']

        let text = document.querySelector('textarea')

        let btn = document.querySelector('button')

        function getRandom(min, max) {

            return Math.floor(Math.random() * (max - min + 1)) + min

        }

        btn.addEventListener('click', function () {

            let random = getRandom(0, names.length - 1)

            text.value = names[random]

            // 将抽到的元素删除

            names.splice(random, 1)

            if (names.length === 0) {

                btn.disabled = true

                alert('已经抽完啦')

            }

        })

    </script>

</body>

随机点名-混合

<body>

    <div class="box">

        <h3 style="text-align: center;">随机问答</h3>

        问题是:<span>马超</span>

        <br>

        <button class="start">开始</button>

        <button class="end">结束</button>

    </div>

    <script>

        let qs = document.querySelector('span')

        let start = document.querySelector('.start')

        let end = document.querySelector('.end')

        let arr = ['马超', '黄忠', '赵云', '关羽', '张飞']

        //设置成全局变量,不然关闭时找不到

        let btnStart = 0;

        let num;

        function getRandom(min, max) {

            return Math.floor(Math.random() * (max - min + 1)) + min

        }

        start.addEventListener('click', function () {

            btnStart = setInterval(function () {

                num = getRandom(0, arr.length - 1)

                console.log(arr[num]);

                qs.innerHTML = arr[num]

                if (arr.length === 1) {

                    start.disabled = end.disabled = true

                }

            }, 50)

        })

        end.addEventListener('click', function () {

            clearInterval(btnStart)

            // 删除已经抽到的,再抽到同一个,你是不是有点过分了

            arr.splice(num, 1)

        })

    </script>

</body>

事件拓宽

图片.png

图片.png

鼠标事件:mouseenter鼠标经过、mouseleave鼠标离开;

焦点事件:focus获得焦点、blur失去焦点;

键盘触发:Keydown键盘按下触发、Keyup键盘抬起触发;

文本事件:input用户输入事件

案例-小米搜索框

不点击搜索框没有光标,点击搜索框,边框变色、下拉框出现。

图片.png

focusblus

<body>

    <div class="mi">

        <input type="search" placeholder="小米笔记本">

        <ul class="result-list">

            <li><a href="#">全部商品</a></li>

            <li><a href="#">小米11</a></li>

            <li><a href="#">小米10S</a></li>

            <li><a href="#">小米笔记本</a></li>

            <li><a href="#">小米手机</a></li>

            <li><a href="#">黑鲨4</a></li>

            <li><a href="#">空调</a></li>

        </ul>

    </div>

    <script>

        let input = document.querySelector('input[type=search]')

        let result_list = document.querySelector('.result-list')

        // 光标获取事件

        input.addEventListener('focus', function () {

            // 文本框变色

            input.classList.add('search')

            // 显示下拉菜单

            result_list.style.display = 'block'

        })

        // 光标失去事件

        input.addEventListener('blur', function () {

            // 文本框去色

            input.classList.remove('search')

            // 隐藏下拉菜单

            result_list.style.display = 'none'

        })

    </script>

</body>

微博输入-案例

图片.png

value是有长度的:input.value.length

1.获取元素、文本域
let area = document.querySelector('#area')
let useCount = document.querySelector('.useCount')

2.绑定input输入事件 用户输入事件input
area.addEventListener('input',function(){
    得到字符的长度
    useCount.innerHTML = area.value.length
})

全选文本框-案例

图片.png

<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 checkbox = document.querySelectorAll('.ck')

        // 三个按钮在伪数组里,我们需要遍历,挨着取出来,依次给值

        checkAll.addEventListener('click', function () {

            for (let i = 0; i < checkbox.length; i++) {

                checkbox[i].checked = checkAll.checked

            }

        })

        // 当全选按钮处于选中状态,则可以改为取消

    </script>

</body>

底下的小按钮,也可以影响上面的大按钮。

分析:
1.遍历下面所有的checkbox,添加点击事件
2.在事件内部,遍历所有的checkbox状态,只要有一个为false,就会将全选状态设置为false,把文字改为全选,并且直接return退出循环
3.在循环结束,将全选的状态直接设置为true

// 小按钮的做法:同时给多个元素绑定相同事件
        for (let i = 0; i < checkbox.length; i++) {
            // 给每一个小按钮绑定事件
            checkbox[i].addEventListener('click', function () {
                for (let j = 0; j < checkbox.length; j++) {
                    // 看看是不是有人没有选中
                    if (checkbox[j].checked === false) {
                        checkAll.checked = false
                        // 退出函数
                        return
                    }
                }
                // 如果代码能够走出循环,那么说明全部都被选中了
                checkAll.checked = true
            })
        }

购物车案例

图片.png

表单里面的value永远都是字符串类型的!!

<input type = "text" id = "total" value = "1" readonly>
<input type = "button" value = "+" id = "add">
<input type = "button" value = "-" id = "reduce" disabled>

add.addEventListener('click',function(){
//i++会隐式转换,这里自动转换为number类型
    total.value++
    reduce.disabled = false
})

reduce.addEventListener('click',function(){
    total.value--
    if(total.value<=1){
        reduce.disabled = true
    }
})

<=也存在隐式转换,==也存在;但是===没有

高阶函数-回调函数

高阶函数:函数的高级应用,JavaScript中函数可以被当成【值】来对待,基于这个特性,来实现函数的高级应用。

【值】就是JavaScript中的数据,比如数值、字符串、布尔、对象等

函数表达式:
let fn = function(){}、把函数当成一个数值来赋值给fn

回调函数:
如果将函数A、作为参数、传递给函数B,我们就说:函数A是回调函数。常见的比如:

function fn(){
    console.log('我是回调函数...')
}
//fn传递给了setInterval,fn就是回调函数
setInterval(fn,1000)

setInterval(function(){},1000)

box.addEventListener('click',function(){})

function fun(){}

box.addEventListener('click',fun)

环境对象this

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

这里this(环境对象,就是个对象)指向的是window对象

function fn(){
    console.log(this)
}

为什么指向的是window?因为当我们调用fn()时,完整写法是:window.fn(),window可以略写。

let btn = document.querySelector('button')

btn.addEventListener('click',function(){
    console.log(this)
})

这里返回的是btn(button按钮),因为是button按钮触发的。

函数的调用方式不同,this的指向对象也不同
【谁调用,this就是谁】是判断this执行的粗略规则

排他思想

当前元素为A状态,其他元素为B状态(Tab栏)

使用:
1.干掉所有人:使用for循环
2.复活他自己:通过this或者下标找到自己或者对应的元素

点谁,谁变颜色

<style>
    .pink{
        background:pink
    }
</style>
<button>1</button>
<button>2</button>
<button>3</button>
<button>4</button>
<button>5</button>

<script>
    let btns = document.querySelectorAll('button')
    //给每一个按钮都注册点击事件
    for(let i = 0; i<btns.length;i++){
        btns[i].addEventListener('click',function(){

            //先干掉所有人
            for(let j = 0;j<btns.length;j++){
                btns[j].classList.remove('pink')
            }
            //复活我自己本身
            this.classList.add('.pink')
        })
    }
</script>

逻辑:点谁,先通过循环把所有人先清除掉,然后具体点谁、谁在变色。每次一上来先清除。

但是上方的写法浪费资源,我们通过改进,只需要找出那个唯一的pink类,进行删除即可

for(let i = 0;i<btns.length;i++){
    btns[i].addEventListener('click',function(){
        //我只需要找出那个唯一的pink类,删除
        let pink = document.querySelector('.pink')
        pink.classList.remove('pink')
        
        //我的
        this.classList.add('pink')
    })
}

案例-tab栏

 <div class="wrapper">

    <ul class="tab">

      <li class="tab-item active">国际大牌<span></span></li>

      <li class="tab-item">国妆名牌<span></span></li>

      <li class="tab-item">清洁用品<span></span></li>

      <li class="tab-item">男士精品</li>

    </ul>

    <div class="products">

      <div class="main active">

        <a href="###"><img src="imgs/guojidapai.jpg" alt="" /></a>

      </div>

      <div class="main">

        <a href="###"><img src="imgs/guozhuangmingpin.jpg" alt="" /></a>

      </div>

      <div class="main">

        <a href="###"><img src="imgs/qingjieyongpin.jpg" alt="" /></a>

      </div>

      <div class="main">

        <a href="###"><img src="imgs/nanshijingpin.jpg" alt="" /></a>

      </div>

    </div>

  </div>


  <script>

    // 拿到li

    let lis = document.querySelectorAll('.tab .tab-item')

    let divs = document.querySelectorAll('.products .main')

    for (let i = 0; i < lis.length; i++) {

      lis[i].addEventListener('click', function () {

        // 找到以前的active,并移除;querySelector里面可以写类
        //remove和add里面不可以写类符号

        document.querySelector('.tab .active').classList.remove('active')

        // add()里不要加类符号,添加当前点击的active

        this.classList.add('active')

        // 底部显示模块
document.querySelector('.products.active').classList.remove('active')

        // 让div对应序号的加上active

        divs[i].classList.add('active')

      })

    }

  </script>

DOM节点

图片.png

图片.png

查找节点+二维码案例

关闭二维码案例:点击关闭按钮,关闭的是二维码的盒子,还需要获取erweima盒子

关闭按钮和二维码是父子关系,所以我们可以点击关闭按钮后,直接关闭二维码(父亲),不用再获取二维码的元素了。

节点关系:父节点子节点兄弟节点

父节点查找

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

子元素.parentNode

<div class = "father">
    <div class = "son">儿子</div>
</div>

let son = document.querySelector('.son')
//省略获取父级的步骤
son.parentNode.style.display = 'none'
<body>

    <div class="erweima">

        <img src="./images/code.png" alt="">

        <i class="close_btn">x</i>

    </div>

    <script>

        let i = document.querySelector('.close_btn')

        i.addEventListener('click', function () {

            i.parentNode.style.display = 'none'

        })

    </script>

</body>

关闭多个二维码:

<div class = "erweima">
    <span class = "close"></span>
</div>
<div class = "erweima">
    <span class = "close"></span>
</div>
<div class = "erweima">
    <span class = "close"></span>
</div>
<div class = "erweima">
    <span class = "close"></span>
</div>

let close_btn = document.querySelectorAll('.close')

绑定多个点击事件
for(let i = 0;i<close_btn.length;i++){
    close_btn[i].addEventListener('click',function(){
        this.parentNode.style.visibility = 'hidden'
    })
}

display:'none':隐藏了,下面的div会往上顶替位置

visibility:'hidden':避免顶替位置的情况

子节点和兄弟节点

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

children(重点):仅获得所有元素节点(不包括空格换行)、返回的还是一个伪数组(只有数组长度和下标,没有数组push方法)

<button></button>
<ul>
    <li>孩子</li>
    <li>孩子</li>
    <li>孩子</li>
    <li>孩子</li>
    <li>孩子</li>
</ul>

let btn = document.querySelector('button')

let ul = document.querySelector('ul')
btn.addEventListener('click',function(){
      console.log(ul.children)//返回的是存储li的伪数组
      
      for(let i = 0;i<ul.children.length;i++){
          ul.children[i].style.color='red'
      }
})

下一个兄弟节点:nextElementSibling属性
上一个兄弟节点:previousElementSibling属性

<button>按钮</button>
<ul>
    <li>1</li>
    <li class = "test">2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>

<script>
    let btn = document.querySelector('button')
    let two = document.querySelector('.test')
    
    btn.addEventListener('click',function(){
        //two.style.color = 'red'
        //让第三个变色
          two.nextElementSibling.style.color = 'red'
        //上一个变色
          two.previousElementSibling.style.color = 'red'
    })
</script>

追加节点

在很多情况下,我们要在页面中增加元素,比如:点击发布按钮,可以新增一条信息。

document.createElement('标签名'):不用写标签,但是要写标签名、比如:divspan

let div = document.createElement('div')
div.className = 'current'
let li = document.createElement('li')

还需要将写好的元素,插入到父元素的【最后一个子元素】:父元素.appendChild(要插入的元素)

let ul = document.querySelector('ul')
let li = document.createElement('li')

li.innerHTML = '我是li'
//变量不能加引号
ul.appendChild(li)

插入到父元素的【某个子元素前面】:父元素.insertBefore(要插入的元素,在哪个元素的前面)

<ul>
<li>我是大毛<li>

<li>我是二毛<li>
</ul>

let ul = document.querySelector('ul')
let li = document.createElement('li')

li.innerHTML = '我是插入'
追加节点(要插入的元素,在哪个元素前面)
ul.inserBefore(li,ul.children[1])

学成在线-案例

1.准备好空的ul结构
2.根据数据的个数,创建一个新的空的li
3.li里面添加内容、img、标题等
4.追加给ul

图片.png

let ul = document.querySelector('ul')
//根据数据的个数,决定li的个数
for (let i = 0;i<data.length;i++){
    //创建li
    let li = document.createElement('li')
    
    //设计好li
    li.innerHTML = 
    `
            <img src=${data[i].src} alt="">
            <h4>
                ${data[i].title}
            </h4>
            <div class="info">
                <span>高级</span> • <span> ${data[i].num}</span>人在学习
            </div>
    `
    
    //追加给ul
    ul.appendChild(li)
}

克隆和删除节点

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

元素.cloneNode(布尔值)

cloneNode会克隆出一个跟原标签一样的元素,括号内传入布尔值:
若为true:克隆时,会包含后代节点一起克隆
若为falese:克隆时,不包括后代节点
默认是false

<ul>
    <li>1</li>
    <li>2</li>
</ul>

let ul = document.querySelector('ul')
let newUI = ul.cloneNode()//默认输出的就是<ul>
let newUI = ul.cloneNode(true)//<ul>包含<li>

document.body.appendChild(newUI)

若一个页面中,某个节点已经不再需要,可以删除它。在JavaScript原生DOM操作中,要【删除元素必须通过父元素删除】。
父元素.removeChild(要删除的元素)

总结:不是自己删除,而是通过爸爸删除儿子

<button></button>
<ul>
    <li>我是内容</li>
</ul>

let btn = document.querySelector('button')

let ul = document.querySelector('ul')

btn.addEventListener('click',funcion(){
    父元素.removeChild(子元素)
    ul.removeChild(ul.children[0])
})

如果不存在父子关系、则删除不成功;
删除节点和隐藏节点有区别的:隐藏节点是还存在的,但是删除,则从html中删除节点。

时间对象

时间对象:表示时间的对象,可以得到当前的系统时间。

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

获得当前时间:let date = new Date(),输出date返回的就是当前系统的时间。

获得指定时间:let date = new Date('1949-10-01 18:30:00');年月日用【分隔号】隔开、时分秒用【冒号】隔开

通过上方实例化得到的时间,存在格式上的问题,需要我们自己手动修改。

getFullYear():获得4位的年份
getMonth():获得月份(取值0-11)
getDate():获取月份中的每一天,不同月份的取值也不相同
getDay():获取星期,取值0-6
getHours():获取小时,取值0-23
getMinutes():获取分钟,取值0-59
getSeconds():获取秒,取值0-59

全部依靠当前系统的时间

let date = new Date();
date.getFullYear();
date.getMonth()+1
date.getDate()
date.getDay()+1
date.getHours()+1
date.getMinutes()+1
date.getSeconds()+1

案例

按照YYYY-MM-DD HH:mm形式显示在页面

<div></div>

1.实例化
let date = new Date();{}

2.getDay()返回的是一个数字,可以巧妙运用返回的数字作为数组的下标来书写
let arr = ['周日','周一','周二','周三','周四','周五','周六']
setInterval(function(){
    let year = date.getFullYear();
    let month = date.getMonth()+1;
    let date1 = date.getDate();
    let hour = date.getHours()+1;
    let minutes = date.getMinutes()+1;
    let sc = date.getSeconds()+1;
    let div = document.querySelector('div')
    div.innerHTML = 
    `现在时间:
    ${year}-${month}-${date1}${hour}:${minutes}:${sc}${arr[day]}`
},1000)

时间戳及案例

图片.png

时间戳是总的毫秒数,是独一无二的;
计算倒计时:将来的时间有一个时间戳、现在的时间有一个时间戳,他们俩相减,然后转换日期形式即可。

三种方式获取时间戳:
1.let date = new Date()->date.getTime()\

2.(推荐)简写形式:+new Date()

3.这个只能得到当前的时间戳:Date.now()

返回指定日期的时间戳
console.log(+new Date('2022-6-2zong4 12:00:00'))

转换公式:
时间戳:首先转换为秒数再计算:(未来的-现在的时间戳)/1000

天数:parseInt(总秒数/60/60/24)
小时:parseInt(总秒数/60/60%24)
分数:parseInt(总秒数/60%60)
秒数:parseInt(总秒数%60)\

毕业倒计时效果:

<div class="countdown">

    <p class="next">今天是2021828日</p>

    <p class="title">下班倒计时</p>

    <p class="clock">

      <span id="hour">00</span>

      <i>:</i>

      <span id="minutes">25</span>

      <i>:</i>

      <span id="scond">20</span>

    </p>

    <p class="tips">

      现在是18:30:00

    </p>

  </div>

  <script>

    let hour = document.querySelector('#hour')

    let minutes = document.querySelector('#minutes')

    let scond = document.querySelector('#scond')

    time()

    setInterval(time, 1000)

    function time() {

      // 1. 得到现在的时间戳

      let now = +new Date()

      // 2. 得到指定时间的时间戳

      let last = +new Date('2021-8-29 18:30:00')

      // 3. (计算剩余的毫秒数) / 1000 === 剩余的秒数

      let count = (last - now) / 1000

      // console.log(count)

      // 4. 转换为时分秒

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

      let h = parseInt(count / 60 / 60 % 24)

      h = h < 10 ? '0' + h : h

      // m = parseInt(总秒数 / 60 % 60);     //   计算分数

      let m = parseInt(count / 60 % 60)

      m = m < 10 ? '0' + m : m

      // s = parseInt(总秒数 % 60); //   计算当前秒数

      let s = parseInt(count % 60);

      s = s < 10 ? '0' + s : s

      // console.log(h, m, s)

      hour.innerHTML = h

      minutes.innerHTML = m

      scond.innerHTML = s

    }

  </script>

微博发布案例

图片.png

避免用户在输入的时候,在首尾不小心输入空格,我们可以采用trim()方法。具体使用:'字符串'.trim()

new Date().toLocaleString():生成本地时间
格式为:2022/6/24 下午1:04:12

<div class="w">

    <!-- 操作的界面 -->

    <div class="controls">

      <img src="./images/9.6/tip.png" alt="" /><br />

      <!-- maxlength 可以用来限制表单输入的内容长度 -->

      <textarea placeholder="说点什么吧..." id="area" cols="30" 
         rows="10" maxlength="200"></textarea>

      <div>

        <span class="useCount" id="useCount">0</span>

        <span>/</span>

        <span>200</span>

        <button id="send">发布</button>

      </div>

    </div>

    <!-- 微博内容列表 -->

    <div class="contentList">

      <ul id="list"></ul>

    </div>

  </div>

  <!-- 添加了hidden属性元素会直接隐藏掉 -->

  <li hidden>

    <div class="info">

      <img class="userpic" src="./images/9.6/03.jpg" />

      <span class="username">死数据:百里守约</span>

      <p class="send-time">死数据:发布于 2020年12月05日 00:07:54</p>

    </div>

    <div class="content">死数据:111</div>

    <span class="the_del">X</span>

  </li>

  <script>

    // maxlength 是一个表单属性, 作用是给表单设置一个最大长度

    // 模拟数据

    let dataArr = [

      { uname: '司马懿', imgSrc: './images/9.5/01.jpg' },

      { uname: '女娲', imgSrc: './images/9.5/02.jpg' },

      { uname: '百里守约', imgSrc: './images/9.5/03.jpg' },

      { uname: '亚瑟', imgSrc: './images/9.5/04.jpg' },

      { uname: '虞姬', imgSrc: './images/9.5/05.jpg' },

      { uname: '张良', imgSrc: './images/9.5/06.jpg' },

      { uname: '安其拉', imgSrc: './images/9.5/07.jpg' },

      { uname: '李白', imgSrc: './images/9.5/08.jpg' },

      { uname: '阿珂', imgSrc: './images/9.5/09.jpg' },

      { uname: '墨子', imgSrc: './images/9.5/10.jpg' },

      { uname: '鲁班', imgSrc: './images/9.5/11.jpg' },

      { uname: '嬴政', imgSrc: './images/9.5/12.jpg' },

      { uname: '孙膑', imgSrc: './images/9.5/13.jpg' },

      { uname: '周瑜', imgSrc: './images/9.5/14.jpg' },

      { uname: '老夫子', imgSrc: './images/9.5/15.jpg' },

      { uname: '狄仁杰', imgSrc: './images/9.5/16.jpg' },

      { uname: '扁鹊', imgSrc: './images/9.5/17.jpg' },

      { uname: '马可波罗', imgSrc: './images/9.5/18.jpg' },

      { uname: '露娜', imgSrc: './images/9.5/19.jpg' },

      { uname: '孙悟空', imgSrc: './images/9.5/20.jpg' },

      { uname: '黄忠', imgSrc: './images/9.5/21.jpg' },

      { uname: '百里玄策', imgSrc: './images/9.5/22.jpg' },

    ]

    // 注册input事件

    let text = document.querySelector('textarea')

    let useCount = document.querySelector('#useCount')

    text.addEventListener('input', function () {

      useCount.innerHTML = text.textLength

    })

    let ul = document.querySelector('#list')

    // 按钮事件

    let send = document.querySelector('#send')

    send.addEventListener('click', function () {

      // 输入内容不能为空,并且防止用户输入开头结尾多余的空格

      if (text.value.trim() === '') {

        // 输入空格时,弹出输入内容不能空并且要让字数不变、光标不变。

        text.value = ''

        useCount.innerHTML = 0

        return alert('内容不能为空哦')

      }

      // 随机数

      function getRandom(min, max) {

        return Math.floor(Math.random() * (max - min + 1)) + min

      }

      // 能够走到这里,说明输入的没问题

      let random = getRandom(0, dataArr.length - 1)
      
      let li = document.createElement('li')

      li.innerHTML = `
      <div class="info">

      <img class="userpic" src=${dataArr[random].imgSrc}>

      <span class="username">${dataArr[random].uname}</span>

      <p class="send-time">${new Date().toLocaleDateString}</p>

      </div>

      <div class="content">${text.value}</div>

      <span class="the_del">X</span>
      `
      
      // 在生成数据后,插入数据前,让该数据具有自己的删除功能

      // 用li获取,因为一开始没有li,是后续craete出来的,用document会报错

      let the_del = li.querySelector('.the_del')

      the_del.addEventListener('click', function () {

        ul.removeChild(li)

      })

      ul.insertBefore(li, ul.children[0])

      // 每次输入完,将内容重置为空,字符数字也为空

      text.value = ''

      useCount.innerHTML = 0

    })

重绘和回流(面试常问)

图片.png

浏览器通过解析(Parser)HTML,生成DOM树(DOM Tree)
同时解析(Parser)CSS,生成样式规则(Style Rules)
根据DOM树和样式规则,生成渲染树(Render Tree)
进行布局Layout(回流/重排):根据生成的渲染树,得到节点的几何信息(位置、大小)
进行绘制Painting(重绘):根据计算和获取的信息进行整个页面的绘制
Display:展示在页面上

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

重绘:由于节点(元素)的样式的改变,并不影响它在文档流中的位置和文档布局(比如修改文字颜色、背景颜色,不影响布局的),称为重绘

重绘不一定影响回流,但是回流一定会引起重绘

图片.png

案例-购物车

'123abc~!@'通过parseInt('123abc!@#')可以得到其中的数值

<body>

  <div class="car">

    <table>

      <thead>

        <tr>

          <th><input type="checkbox" id="all" />全选</th>

          <th>商品</th>

          <th>单价</th>

          <th>商品数量</th>

          <th>小计</th>

          <th>操作</th>

        </tr>

      </thead>

      <tbody id="carBody">

        <tr>

          <td>

            <input class="s_ck" type="checkbox" readonly />

          </td>

          <td>

            <img src="./images/01.jpg" />

            <p>牛奶</p>

          </td>

          <td class="price">5¥</td>

          <td>

            <div class="count-c clearfix">

              <button class="reduce" disabled>-</button>

              <input type="text" value="1" />

              <button class="add">+</button>

            </div>

          </td>

          <td class="total">5¥</td>

          <td>

            <a href="javascript:" class="del">删除</a>

          </td>

        </tr>

        <tr>

          <td>

            <input class="s_ck" type="checkbox" />

          </td>

          <td>

            <img src="./images/01.jpg" />

            <p>牛奶</p>

          </td>

          <td class="price">10¥</td>

          <td>

            <div class="count-c clearfix">

              <button class="reduce" disabled>-</button>

              <input type="text" value="1" />

              <button class="add">+</button>

            </div>

          </td>

          <td class="total">20¥</td>

          <td>

            <a href="javascript:" class="del">删除</a>

          </td>

        </tr>

        <tr>

          <td>

            <input class="s_ck" type="checkbox" />

          </td>

          <td>

            <img src="./images/01.jpg" />

            <p>牛奶</p>

          </td>

          <td class="price">20¥</td>

          <td>

            <div class="count-c clearfix">

              <button class="reduce" disabled>-</button>

              <input type="text" value="1" />

              <button class="add">+</button>

            </div>

          </td>

          <td class="total">40¥</td>

          <td>

            <a href="javascript:" class="del">删除</a>

          </td>

        </tr>

        <tr>

          <td>

            <input class="s_ck" type="checkbox" />

          </td>

          <td>

            <img src="./images/01.jpg" />

            <p>牛奶</p>

          </td>

          <td class="price">35¥</td>

          <td>

            <div class="count-c clearfix">

              <button class="reduce" disabled>-</button>

              <input type="text" value="1" />

              <button class="add">+</button>

            </div>

          </td>

          <td class="total">70¥</td>

          <td>

            <a href="javascript:" class="del">删除</a>

          </td>

        </tr>

      </tbody>

    </table>

    <div class="controls clearfix">

      <a href="javascript:" class="del-all">删除所选商品</a>

      <a href="javascript:" class="clear">清理购物车</a>

      <a href="javascript:" class="pay">去结算</a>

      <p>

        已经选中<span id="totalCount">0</span>件商品;总价:<span id="totalPrice" class="total-price">0¥</span>

      </p>

    </div>

  </div>

  <script>

    // + - 删除是相同的,一一对应的 我们完全可以用一个for来遍历绑定事件

    // +

    let adds = document.querySelectorAll('.add')

    // -

    let reduces = document.querySelectorAll('.reduce')

    // del

    let dels = document.querySelectorAll('.del')

    // 输入框input

    let inputs = document.querySelectorAll('.count-c input')

\


    // 单价 price  5

    let prices = document.querySelectorAll('.price')

    // 小计 total  5 * 2 = 10

    let totals = document.querySelectorAll('.total')

    // 总价元素获取

    let totalResult = document.querySelector('.total-price')

    // 总的数量获取

    let totalCount = document.querySelector('#totalCount')

    // tbody 获取过来

    let carBody = document.querySelector('#carBody')

    for (let i = 0; i < adds.length; i++) {

      // 总价和单价是一样的

      totals[i].innerText = prices[i].innerText

      //1. 加号的操作

      adds[i].addEventListener('click', function () {

        // 点击了谁,就让对应的输入框自增就行了

        inputs[i].value++

        // 减号要启用

        reduces[i].disabled = false

        // prices[i].innerText  得到的是 5¥     parseInt('5¥')  === 5

        console.log(parseInt(prices[i].innerText))

        // 计算小计模块

        // totals[i].innerText =  单价 * 数量  

        // totals[i].innerText = 20

        totals[i].innerText = parseInt(prices[i].innerText) * inputs[i].value + '¥'

        // 计算现在的总额 调用

        result()

      })

\


      //2. 减号的操作

      reduces[i].addEventListener('click', function () {

        // 点击了谁,就让对应的输入框自增就行了

        inputs[i].value--

        // prices[i].innerText  得到的是 5¥     parseInt('5¥')  === 5

        // console.log(parseInt(prices[i].innerText))

        // 判断如果表单里面的值 小于等于1 则,禁用按钮

        if (inputs[i].value <= 1) {

          this.disabled = true

        }

        // 计算小计模块

        // totals[i].innerText =  单价 * 数量  

        // totals[i].innerText = 20

        totals[i].innerText = parseInt(prices[i].innerText) * inputs[i].value + '¥'

\


        // 计算现在的总额 调用

        result()

      })

\
\


      // 3. 删除操作

\


      dels[i].addEventListener('click', function () {

        // 父元素.removeChild(子元素)  

        // 我们要删除的是那个元素   tr  他的爸爸是 tbody

        // 删除的是当前元素爸爸的爸爸  就是 tr 就是当前的tr

        carBody.removeChild(this.parentNode.parentNode)

        // 调用总计模块

        result()

      })

    }


    // div  span   ul  li  标签  有文字内容  怎么得到或则设置文字内容呢  元素.innerText   元素.innerHTML

    // 表单  input 单选 复选    textarea  select  怎么得到或则设置值   表单的value

    // 特殊的  button 是通过inner来设置

    // 以前数组求和的方式 累加

    //  计算总价 result 函数  把所有的小计 totals 加起来的结果

    function result() {

      // 小计 total  5 * 2 = 10

      let totals = document.querySelectorAll('.total')

      // 输入框input

      let inputs = document.querySelectorAll('.count-c input')

      let sum = 0

      let num = 0

      for (let i = 0; i < totals.length; i++) {

        // sum = sum + 小计的数字  10¥

        sum = sum + parseInt(totals[i].innerText)

        num = num + parseInt(inputs[i].value)

      }

      // console.log(sum)

      totalResult.innerText = sum + '¥'

      // console.log(num)

      totalCount.innerText = num

    }

    result()

  </script>

\


</body>

事件对象

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

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

元素.addEventListener('click',function(e){
    
})
<button></button>

let btn = document.querySelector('button')

btn.addEventListener('click',function(e){
    console.log(e)
})

图片.png

事件对象常用属性

type:得到当前的事件类型(是click、还是mouseenter)

clientX/clientY:获取光标相对于浏览器可见窗口左上角的位置(如果浏览器缩小的话,他的值和pageX就会不同)

pageX/pageY:跟文档坐标有关系

offsetX/offsetY:获取光标相对于当前DOM元素左上角的位置;(在一个div中,鼠标离当前盒子的X和Y坐标)

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

案例-鼠标跟随

一张图片一直跟着鼠标移动

其实就是图片(left和top值)知道鼠标的坐标就行

<style>

        img {

            position: absolute;

            top: 0;

            left: 0;

        }

    </style>

</head>

\


<body>

    <img src="./images/tianshi.gif" alt="">

    <script>

        let img = document.querySelector('img')

        document.addEventListener('mousemove', function (e) {

            // 不断得到当前的鼠标坐标

            // console.log(e.pageX)

            // console.log(e.pageY)

            // 把坐标给图片

            // img.style.left = '100px'

            img.style.left = e.pageX - 50 + 'px'

            img.style.top = e.pageY - 40 + 'px'

        })

    </script>

</body>

e.paegX后面必须要加px单位!!!

案例-回车发布微博

按下键盘:keydown或者keyup事件都可以

textarea.addEventListener('keyup',function(e){
    //已废弃
    console.log(e.keyCode())
    
    if(e.key === 'Enter'){
        //按了回车点,自动触发已经写好的发布按钮
        send.click()
    }
})

e.keyCode属性已经被删除了,取而代之的是:e.key

在开发中,使用键盘事件,我们更推荐使用keyup,而不是keydown,因为keydown后面的逻辑可能没有执行到,因为keydown执行的逻辑过早。

事件流

事件流:事件完整执行过程中的流动路径

图片.png

捕获阶段:Document--》Element html--》Element body--》Element div

冒泡阶段:Element div--》Element body--》Element html--》Document

冒泡:当元素的事件被触发时,同样的事件(比如都是单击事件)将会在该元素的所有祖先元素中、依次被触发,这个过程就是事件的冒泡

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

<div class = "yeye">
    <div class = "father">
        <div class = "son"></div>
    </div>
</div>
let fa = document.querySelector('.father')

let son = document.querySelector('.son')
fa.addEventListener('click',function(){
    alert('我是父亲')
})
son.addEventListener('click',function(){
    alert('我是儿子')
})
yeye.addEventListener('dblclick',function(){
    alert('我是爷爷')
})

此时点击父亲,弹出我是父亲,但是点击儿子,不仅会弹出我是儿子,还会弹出我是父亲、但是并不会弹出我是爷爷,因为爷爷的事件是鼠标双击事件!!!

因为在事件中,儿子会冒泡,冒泡到父级,然后冒泡到body--》html--》document

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

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

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

若是用L0事件(onclick、老写法)监听,则只有冒泡阶段,没有捕获阶段

阻止事件流动

因为默认就有冒泡模式的存在,所以容易导致事件影响到父级元素,若想把事件就限制在当前元素内,就需要阻止事件的流动

事件对象.stopPropagation()、事件对象是function(e)中的参数e也就是event

我希望我点了儿子,只有儿子弹出窗口

fa.addEventListener('click',function(){
    alert('我是父亲')
})
son.addEventListener('click',function(e){
    alert('我是儿子')
    //阻止冒泡
    e.stopPropagation()
    
})
yeye.addEventListener('dblclick',function(){
    alert('我是爷爷')
})

鼠标经过事件:mouseovermouseout会有冒泡效果

鼠标经过事件:mouseentermouseleave没有冒泡效果(推荐)

阻止默认行为:e.preventDefault()

a标签,默认点击后就可以跳转,但是有些情况,我们是不希望他跳转的,就可以使用阻止默认行为

<a href = "www.baidu.com">跳转百度</a>

let a = document.querySelector('a')
a.addEventListener('click',function(e){
    e.preventDefault();
})

此时就算点击a标签,他也不会跳转!!!

注册事件

使用addEventListener()事件监听注册:后面注册的事件【不会覆盖】前面注册的事件(同一个事件)、并且可以通过第三个参数去确定是在冒泡或者捕获阶段执行

addEventListener('click',function(e){},false)

必须使用removeEventListener()进行解绑,但是此时【不可以使用匿名函数】!!!

function (){

}
btn.addEventListener('click',add)

//移除事件,并且不可以是匿名函数
btn.removeEventListener('click',add)

事件委托

事件委托:利用【事件流】来快速完成

事件委托是给父级添加事件,而不是子级

<ul>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>

不需要给每个小li注册事件了,而是把事件委托给li的父级

事件委托是给父级添加事件,而不是子级
let ul = document.querySelector('ul')
ul.addEventListener('click',function(e){
    alert('我点击了')
    console.log(e.target)
    //哪个被点击了,哪个变色
    e.target.style.color = 'red'
})

e.target:获得真正触发事件的元素

原理:去具体的某一个li上找点击事件,结果li上面并没有点击事件,然后事件就会默认【冒泡】往上找,就会找到ul的点击事件

案例-学生信息表

图片.png

图片.png

不管是添加还是删除,都是往数组里面进行操作!

自动生成的id:arr[arr.length-1].stuId + 1:每次新的id都是上一次最后一个id的值+1、从而实现自增

appendChild():追加,以前存在的数据,还会连通新的数据一样、一起加入数组中

想要避免这个问题,就需要在渲染之前,先让以前的数据为空,再渲染数据:tbody.innerHTML = ''

每次录入完(渲染完)要将数据清空,下拉列表的值也清空

uname.value = age.value = salary.value =''

gender.value = '男'

city.value = '北京'

事件委托:最好是有多个相同的操作使用:比如多个删除操作,我们如果用以前的方法,需要每个tr都获取到,现在通过事件委托,通过tbody就可以了,而录入操作只有一个button,所以不需要使用事件委托

图片.png

<body>

  <h1>新增学员</h1>

  <div class="info">

    姓名:<input type="text" class="uname">

    年龄:<input type="text" class="age">

    性别: <select name="gender" id="" class="gender">

      <option value="男"></option>

      <option value="女"></option>

    </select>

    薪资:<input type="text" class="salary">

    就业城市:<select name="city" id="" class="city">

      <option value="北京">北京</option>

      <option value="上海">上海</option>

      <option value="广州">广州</option>

      <option value="深圳">深圳</option>

      <option value="曹县">曹县</option>

\


    </select>

    <button class="add">录入</button>

  </div>

\


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

    //  1. 准备好数据后端的数据

    let arr = [

      { stuId: 1001, uname: '欧阳霸天', age: 19, gender: '男', salary: '20000', city: '上海' },

      { stuId: 1002, uname: '令狐霸天', age: 29, gender: '男', salary: '30000', city: '北京' },

      { stuId: 1003, uname: '诸葛霸天', age: 39, gender: '男', salary: '2000', city: '北京' },

    ]

\


    // 获取父元素 tbody

    let tbody = document.querySelector('tbody')

    // 添加数据按钮

    // 获取录入按钮

    let add = document.querySelector('.add')

    // 获取各个表单的元素

    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')

    // 渲染函数  把数组里面的数据渲染到页面中

    function render() {

      // 先干掉以前的数据  让tbody 里面原来的tr 都没有

      tbody.innerHTML = ''

      // 在渲染新的数据

      // 根据数据的条数来渲染增加 tr  

      for (let i = 0; i < arr.length; i++) {

        // 1.创建tr  

        let tr = document.createElement('tr')

        // 2.tr 里面放内容

        tr.innerHTML = `

            <td>${arr[i].stuId}</td>

            <td>${arr[i].uname}</td>

            <td>${arr[i].age}</td>

            <td>${arr[i].gender}</td>

            <td>${arr[i].salary}</td>

            <td>${arr[i].city}</td>

            <td>

              <a href="javascript:" id="${i}">删除</a>

            </td>

            `

        // 3.把tr追加给 tobdy  父元素.appendChild(子元素)

        tbody.appendChild(tr)
      }

    }

    // 页面加载就调用函数

    render()

\


    add.addEventListener('click', function () {

      // alert(11)

      // 获得表单里面的值   之后追加给 数组 arr  用 push方法

      arr.push({

        // 得到数组最后一条数据的学号 1003    + 1

        stuId: arr[arr.length - 1].stuId + 1,

        uname: uname.value,

        age: age.value,

        gender: gender.value,

        salary: salary.value,

        city: city.value

      })

      // console.log(arr)

      // 重新渲染我们的函数

      render()

      // 复原所有的表单数据

      uname.value = age.value = salary.value = ''

      gender.value = '男'

      city.value = '北京'

    })

\
\


    // 删除操作, 删除的也是数组里面的数据 , 但是我们用事件委托

    tbody.addEventListener('click', function (e) {

      // alert(11)

      // 我们只能点击了链接 a ,才会执行删除操作

      // 那我们怎么知道你点击了a呢?

      // 俺们只能点击了链接才能做删除操作

      // console.dir(e.target.tagName)

      if (e.target.tagName === 'A') {

        // alert('你点击了链接')

        // 删除操作  删除 数组里面的数据  arr.splice(从哪里开始删,1)

        // 我要得到a的id 需要

        // console.log(e.target.id)

        arr.splice(e.target.id, 1)

\


        // 重新渲染我们的函数

        render()

      }

    })

网页特效篇

能用css做特效就用css做,但是有些需要通过JavaScript来触发改变!!!

手风琴效果

demo08.gif

鼠标经过的时候会展示,又叫折叠卡片;鼠标进入:小图片宽度发生变化,鼠标离开:又恢复

//li默认的宽度是240像素
//当我们鼠标经过,当前的li宽度变大800px,其余的li变为100px
//当我们鼠标离开,所有的li全部复原(宽度240px)

let lis = document.querySelectorAll('li')

//绑定鼠标经过、离开事件
for(let i  = 0;i<lis.length;i++){
    lis[i].addEventListener('mouseenter',function(){
        //排他思想:干掉所有人,复活我自己
        for(let j = 0;j<lis.length;j++){
            lis[j].style.width = 100px
        }
        //复活
        this.style.width = '800px'
    })
    lis[i].addEventListener('mouseleave',function(){
    //所有的全部复原
    for(let j = 0;j<lis.length;j++){
           lis[j].style.width = '240px'
       }
    })
}

排他思想:只有自己这样做。。别人都是那样的。。

滚动和加载事件

当滚动某个长页面的时候,一直往下滚,此时右边的状态栏会出现一个回到顶部的功能栏,点击回到顶部就可以回到顶部

这个效果是通过页面滚动事件来实现的,所以我们要学习。

滚动事件:滚动时就会触发(滚动1px就可以触发)、比如:固定导航栏、返回顶部

事件名:scroll

window.addEventListener('scroll',function(){
    //执行的操作
})

给window或document、某个盒子里.添加scroll事件

加载事件:加载外部资源(图片、外联CSS)加载完毕时,触发的事件

有些时候需要等到页面资源加载完,处理一些事情

事件名:load

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

window.addEventListener('load',function(){
    console.log('当我输出时,说明所有资源已经全部加载完毕')
})

这句话还可以写在<head>标签的script标签的里面:

<head>
    <style></style>
    <script>
        window.addEventListener('load',function(){
            console.log(div)
        })
    </scrpit>
</head>

<body>
    <div>
        ....
    </div>
</body>

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

事件名:DOMContentLoaded

监听页面DOM加载完毕

document.addEventListener('DOMContentLoaded',function(){
    
})

scroll家族:元素大小和位置

我们想要页面滚动一段距离,比如100px,就让某些元素显示和隐藏,那我们怎么知道,页面滚动了100像素呢?

可以使用scroll来检测页面滚动的举例

获取宽高

获取元素的内容总宽高(不包含滚动条)返回值不带单位:scrollWidthscrollHeight

div{
    width:150px;
    height:150px;
    overflow:auto;//超出的部分用滚动条
}
<div>
    ...很多很多内容,已经超出了盒子的大小
</div>
<script>
    
    div.addEventListener('scroll',function(){
    //内容有多大,就检测有多大,和盒子的大小无关、不带px单位
    div.scrollWidth
    //返回的是内容的宽度,但是会被滚动条覆盖一点宽度
    
    div.scrollHeight //返回的是内容的高度
    })
</scrpit>

获取位置

被卷去的头部和左侧(重点)

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

被卷去的头部:浏览器页面可能一般大,但是内容会很大,超出了浏览器的页面显示范围,需要使用滚动条查看内容的下面部分,这个时候就需要滚动条往下,但是内容实际上是往上走的,往上走的这一部分就是被卷去的头部。

图片.png

//先做页面滚动事件,再得到页面滚动的距离
div.addEventListener('scroll',function(){
    console.log(this.scrollTop)
})
页面

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

document.documentElement:就是<html>标签的滚动,也就是所有的标签的滚动多少。

window.addEventListener('scroll',function(){
    let num = document.documentElement.scrollTop
    
    console.log(num)
})

也可以赋值修改(不带单位):document.documentElement.scrollTop = 500

图片.png

返回顶部案例

当滚动到某个位置时,将隐藏的回到顶部显示出来,然后点击后在将scrollTop设置为0就可以了。

<div class = "content"></div>
<div class = "backtop">
    <img>
    <a>
</div>

0.获取元素
let backtop = document.querySelector('.backtop')
1.页面滚动事件
window.addEventListener('scroll',function(){
    2.检测滚动的距离
    let num = document.documentElement.scrollTop
    if(num>=500){
        //显示回到顶部的元素
        backtop.style.display = 'block'
    }else{
        backtop.style.display = 'none'
    }
})

3.进行判断显示和隐藏(根据a标签链接返回)
backtop.children[1].addEventListener('click',function(){
    //返回顶部
    document.documentElement.scrollTop = 0
})

offset家族

刚刚我们的设计存在问题,对于scrollTop我们是写死的,我们应该让js自动获取页面中的位置。

什么时候用offset家族呢?
当我们知道盒子在页面中的距离就可以使用

获取宽高:
获取元素的自身宽高、包含元素自身设置的宽高、padding、border;盒子是多大,他就是多大,受到padding、border的影响
offsetWidth、offsetHeight

scrollWidth和scrollHeight只和内容有关系、不受盒子大小、padding的影响。而offsetWidth和offsetHeight和盒子有关系,受到padding和border的影响

图片.png

获取位置(offsetLeft、offsetTop):获取元素距离自己定位父级的左、上距离

如果没有父级,那么就获取浏览器距离自己的位置,如果有父级,那么就获取距离父级的左、上距离

也就是说:当某个div没有直接父级,那么他的父级就是html,当这个div和html的距离存在一定等值时,就会触发某些事件

但是:offsetLeftoffsetTop是只读属性,不可以给值

图片.png

京东固定导航栏案例

当页面滚动到某个位置时,导航栏就会出现把并且固定在上方,但是往回移动的时候,又会消失

offsetTop:距离顶部有多远,如果卷出的头部距离>我秒杀的offsetTop,说明已经往下划过了秒杀盒子

<style>

        * {

            margin: 0;

            padding: 0;

            box-sizing: border-box;

        }

\


        .content {

            overflow: hidden;

            width: 1000px;

            height: 3000px;

            background-color: pink;

            margin: 0 auto;

        }

\


        .backtop {

            display: none;

            width: 50px;

            left: 50%;

            margin: 0 0 0 505px;

            position: fixed;

            bottom: 60px;

            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;

        }

\


        .header {

            position: fixed;

            top: -80px;

            left: 0;

            width: 100%;

            height: 80px;

            background-color: purple;

            text-align: center;

            color: #fff;

            line-height: 80px;

            font-size: 30px;

            transition: all .3s;

        }

\


        .sk {

            width: 300px;

            height: 300px;

            background-color: skyblue;

            margin-top: 600px;

        }

    </style>

<div class="header">我是顶部导航栏</div>

    <div class="content">

        <div class="sk">秒杀模块</div>

    </div>

    <div class="backtop">

        <img src="./images/close2.png" alt="">

        <a href="javascript:;"></a>

    </div>
    
        let sk = document.querySelector('.sk')

        let header = document.querySelector('.header')
1.页面滚动事件
        window.addEventListener('scroll',function(){
2.检测滚动的距离>=秒杀模块的offsetTop值(秒杀模块位置)则滑入
    如果我页面往下滚动,卷出的头部距离>我秒杀的offsetTop
    if (document.documentElement.scrollTop >= sk.offsetTop) {

                // alert('改吃饭了')
                //默认header导航栏是-80px是看不见的,当滚动到秒杀时,top:0就显示
                header.style.top = '0'

            } else {

                header.style.top = '-80px'

            }
})

通过offsetTop就不需要再手写并且写死值了,更加的灵活

电梯导航案例

一个大的内容模块,分为4部分,旁边有导航栏,点击导航栏可以快速切到对应的内容

分析:
点击当前小导航,当前添加active,其余移除active
得到对应内容的offsetTop值
让页面的scrollTop走到对应内容的offsetTop

图片.png

侧边栏典型的排他思想,点我、只让我自己高亮;

<div class="aside">

        <div class="item active">男装/女装</div>

        <div class="item">儿童服装/游乐园</div>

        <div class="item">电子产品</div>

        <div class="item">电影/美食</div>

    </div>


    <div class="content">

        <div class="neirong content1">男装/女装</div>

        <div class="neirong content2">儿童服装/游乐园</div>

        <div class="neirong content3">电子产品</div>

        <div class="neirong content4">电影/美食</div>

    </div>


let item = document.querySelectorAll('.item')

内容的盒子获取
let neirong = document.querySelectorAll('.neirong')

for(let i =0;i<item.length;i++){
    item[i].addEventListener('click',function(){
            //找到active,移除类(直接找到类不需要双重循环了)
            document.querySelector('.aside .active').classList.remove('active')
        //给自己添加
        this.classList.add('active')
        
        
        //拿到每个盒子距离浏览器顶部的距离:offsetTop
        //让页面滚动到对应的地方
        document.documentElement.scrollTop = neirong[i].offsetTop
    })
}

client家族

获取宽高

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

scrollHeight:内容超出了盒子,但是我获取的是内容的高度

offsetWidth:盒子多大,我就多大(受到边框、边距的影响)

clientWidthclientHeight(可见部分、不包含边框、边距)

可见部分是什么意思?当我把浏览器缩小时,能看到的页面部分也变小了,这就是可见部分

窗口改变事件

resize事件:会在窗口尺寸发生改变时,触发

window.addEventListener('resize',function(){
    document.documentElement.clientWidth;//当前可视屏幕宽度
})
window.addEventListener('resize',function(){
    let w = document.documentElement.clientWidth
    if(w<1920){
        document.body.style.backgroundColor = 'pink'
    }
})
三大家族总结

clientLeft、clientTop:只读属性,获取左边框和上边框的宽度(用的极少)

图片.png

重点掌握offset和scroll

轮播图综合案例

图片.png

图片.png

图片.png

注解:

图片.png

上面最大的图片每个都是一个li,但是他们全部都是叠加在一起的,不同的是:通过opacity来设置淡入淡出的效果来实现具体某一张图片的显示

图片.png

底下的小图,点到谁,谁就高亮、并且还有一个小下划线.

1.小图标鼠标经过事件,鼠标经过小图片、当前高亮,其余兄弟变淡,添加类

let lis = document.querySelectorAll('.indicator li')
//给多个li绑定事件
for(let i = 0;i<lis.length;i++){
    lis[i].addEventListener('mouseenter',function(){
        //找到上一个具有active类的、并移除
        document.querySelector('.indicator .active').classList.remove('.active')
        //鼠标经过谁,谁就加上
        this.classList.add('.active')
    })
}

2.大图片跟随小图片变化,可以使用opacity效果,可以利用CSS淡入淡出效果,还是添加类

目的是让图片对应的li隐藏,li都隐藏了,那图片一定也隐藏(因为img有链接,最好不要隐藏),让li加一个active类,里面有opciaty属性,为1就是显示,0就是透明

一定要放到鼠标经过事件里面

let piclis = document.querySelectorAll('.slides ul li')

//给多个li绑定事件
for(let i = 0;i<lis.length;i++){
    lis[i].addEventListener('mouseenter',function(){
        //找到上一个具有active类的、并移除
        document.querySelector('.indicator .active').classList.remove('.active')
        //鼠标经过谁,谁就加上
        this.classList.add('.active')
        
        
        
        --------------------------------
        //选出唯一的那个active,删除类
        document.querySelector('.slides .active').classList.remove('active')
        piclis[i].classList.add('active')
    })
}

3.图片变化,文字也跟着变化

let text = document.querySelector('.extra h3')

let piclis = document.querySelectorAll('.slides ul li')

//给多个li绑定事件
for(let i = 0;i<lis.length;i++){
    lis[i].addEventListener('mouseenter',function(){
        //找到上一个具有active类的、并移除
        document.querySelector('.indicator .active').classList.remove('.active')
        //鼠标经过谁,谁就加上
        this.classList.add('.active')

        //选出唯一的那个active,删除类
        document.querySelector('.slides .active').classList.remove('active')
        piclis[i].classList.add('active')
    
    -------------------------------------
    text.innerHTML = `这是第${i+1}张图片的详细描述信息`
    })
}

4.点击按钮、控制图片(大图小图)的转换

需要一个变化量index不断自增、然后播放下一张,如果到了最后一张,必须要还原为第一张

索引号=索引号%数组长度(放到播放前面)

let next = document.querySelector('.next')

let index = 0
next.addEventListener('click',function(){
    //点一次按钮,我的index就加一次
    index++
    
    index = index % lis.length
    //小图片--先移除其他人存在的高亮
    document.querySelector('.indicator .active').classList.remove('active')
    //复活我自己
    lis[index].classList.add('active')
    
    //大图片--和小图片一样的逻辑
    document.querySelector('.slides ul .active').classLisst.remove('active')
    piclis[index].classList.add('active')
    
    text.innerHTML = `第${index+1}张图的描述信息`
})

但是上方存在bug,点击右侧按钮可以实现播放下一张、但是鼠标经过前面的,播放就会乱序。

因为index不在鼠标移入事件中,所以鼠标事件需要知道index的值

解决方法:让索引号重新赋值为:当前鼠标经过的索引号。index是全局变量,她没有定义在任何语句块中

index = i

5.写左侧按钮

let prev = document.querySelector('.prev')

prev.addEventListener('click',function(){
    index--
    index = (lis.length+index) % lis.length
    document.querySelector('.indicator .active').classList.remove('active')
    lis[index].classList.add('active')
    document.querySelector('.slides ul .active').classList.remove('active')
    
    piclis[index].classList.add('active')
    text.innerHTML = `第${index+1}张图的描述信息`
})

6.自动播放图片模块(实际上定时器播放就相当于点击了一次播放按钮)

let timer = setInterval(function(){
//自动调用右侧按钮的点击事件
    next.click()
},1000)

7.鼠标经过:停止定时器(清除)、鼠标离开:开启定时器

let main = document.querySelector('.main')

main.addEventListener('mouseenter',function(){
    clearInterval(timer)
})
main.addEventListener('mouseleave',function(){
    timer = setInterval(function(){
//自动调用右侧按钮的点击事件
    next.click()
},1000)
})
完整代码:
<div class="main">

    <div class="slides">

      <ul>

        <li class="active"><a href="#"><img src="./assets/b_01.jpg" alt="第1张图的描述信息"></a></li>

        <li><a href="#"><img src="./assets/b_02.jpg" alt="第2张图的描述信息"></a></li>

        <li><a href="#"><img src="./assets/b_03.jpg" alt="第3张图的描述信息"></a></li>

        <li><a href="#"><img src="./assets/b_04.jpg" alt="第4张图的描述信息"></a></li>

        <li><a href="#"><img src="./assets/b_05.jpg" alt="第5张图的描述信息"></a></li>

        <li><a href="#"><img src="./assets/b_06.jpg" alt="第6张图的描述信息"></a></li>

        <li><a href="#"><img src="./assets/b_07.jpg" alt="第7张图的描述信息"></a></li>

        <li><a href="#"><img src="./assets/b_08.jpg" alt="第8张图的描述信息"></a></li>

        <li><a href="#"><img src="./assets/b_09.jpg" alt="第9张图的描述信息"></a></li>

        <li><a href="#"><img src="./assets/b_10.jpg" alt="第9张图的描述信息"></a></li>

      </ul>

      <div class="extra">

        <h3>第1张图的描述信息</h3>

        <a class="prev" href="javascript:;"></a>

        <a class="next" href="javascript:;"></a>

      </div>

    </div>

    <div class="indicator">

      <ul>

        <li class="active">

          <img src="assets/s_01.jpg">

          <span class="mask"></span>

          <span class="border"></span>

        </li>

        <li>

          <img src="assets/s_02.jpg">

          <span class="mask"></span>

          <span class="border"></span>

        </li>

        <li>

          <img src="assets/s_03.jpg">

          <span class="mask"></span>

          <span class="border"></span>

        </li>

        <li>

          <img src="assets/s_04.jpg">

          <span class="mask"></span>

          <span class="border"></span>

        </li>

        <li>

          <img src="assets/s_05.jpg">

          <span class="mask"></span>

          <span class="border"></span>

        </li>

        <li>

          <img src="assets/s_06.jpg">

          <span class="mask"></span>

          <span class="border"></span>

        </li>

        <li>

          <img src="assets/s_07.jpg">

          <span class="mask"></span>

          <span class="border"></span>

        </li>

        <li>

          <img src="assets/s_08.jpg">

          <span class="mask"></span>

          <span class="border"></span>

        </li>

        <li>

          <img src="assets/s_09.jpg">

          <span class="mask"></span>

          <span class="border"></span>

        </li>

        <li>

          <img src="assets/s_10.jpg">

          <span class="mask"></span>

          <span class="border"></span>

        </li>

      </ul>

    </div>

  </div>

// 轮播图开始啦

    // 需求①:小图标鼠标经过事件

    //   鼠标经过小图片,当前高亮,其余兄弟变淡  添加类

    let lis = document.querySelectorAll('.indicator li')

    let piclis = document.querySelectorAll('.slides ul li')

    let text = document.querySelector('.extra h3')

    let next = document.querySelector('.next')

    let prev = document.querySelector('.prev')

    let main = document.querySelector('.main')



    // 给多个小li绑定事件

    for (let i = 0; i < lis.length; i++) {

      lis[i].addEventListener('mouseenter', function () {

        // 选出唯一的那个active ,删除类

        document.querySelector('.indicator .active').classList.remove('active')

        // 鼠标经过谁,谁加上active 这个类

        this.classList.add('active')



        // 需求② :大图片跟随变化  一定要放到鼠标经过事件里面

        // 对应的大图片跟着显示,如果想要过渡效果,
        可以使用opacity效果,可以利用CSS淡入      淡出的效果,还是添加类

        // 选出唯一的那个active ,删除类

        document.querySelector('.slides ul .active').classList.remove('active')

        // 对应序号的那个 li,谁加上active 这个类

        piclis[i].classList.add('active')

        text.innerHTML = `第${i + 1}张图的描述信息`



        // 需求④:解决一个BUG

        // 点击右侧按钮可以实现播放下一张,但是鼠标经过前面的,播放就会乱序

        // 解决方案:  让变化量 index 重新赋值为 当前鼠标经过的索引号

        // 鼠标经过了那个小li 他的索引号就是 i

        // 右侧按钮是通过 index 来了控制播放的

        index = i

      })

    }





    // 需求③:右侧按钮播放效果

    //   点击右侧按钮,可以自动播放下一张图片

    //   需要一个变化量  index 不断自增

    //   然后播放下一张图片

    //   如果到了最后一张,必须要还原为第1张图片

    //   教你一招: 索引号 = 索引号 % 数组长度 (放到播放前面)

    let index = 0  // 全局变量  信号量 控制器 为了给 右侧按钮和左侧按钮同时使用

    next.addEventListener('click', function () {

      index++

      // 选出 index 小图片 做操作

      // console.log(index)

      // if (index === lis.length) {

      //   index = 0

      // }

      index = index % lis.length

      common()



    })



    // 需求⑤:左侧按钮播放效果

    //   点击左侧按钮,可以自动播放上一张图片

    //   需要一个变化量  index 不断自减

    //   然后播放上一张图片

    //   如果到了第一张,必须要从最后一张播放

    //   教你一招: 索引号 = (数组长度 + 索引号) % 数组长度

    prev.addEventListener('click', function () {

      index--

      // 选出 index 小图片 做操作

      // console.log(index)

      if (index < 0) {

        index = lis.length - 1

      }

      // index = (lis.length + index) % lis.length

      common()



    })



    // 需求⑥:

    //   因为左侧按钮和右侧按钮里面有大量相同的操作,可以抽取封装一个函数 common

    function common() {

      document.querySelector('.indicator .active').classList.remove('active')

      lis[index].classList.add('active')

      // 选出 index 大图片 做操作

      document.querySelector('.slides ul .active').classList.remove('active')

      piclis[index].classList.add('active')

      text.innerHTML = `第${index + 1}张图的描述信息`

    }







    // 需求⑦:开启定时器

    //   其实定时器自动播放,就相当于点击了右侧按钮,此时只需要, next.click()

    let timer = setInterval(function () {

      // 自动调用右侧按钮的点击事件

      next.click()

    }, 1000)

    // 需求⑧:

    //   鼠标经过停止定时器 (清除定时器)



    main.addEventListener('mouseenter', function () {

      clearInterval(timer)

    })

    //   鼠标离开开启定时器 (开启定时器)

    main.addEventListener('mouseleave', function () {

      timer = setInterval(function () {

        // 自动调用右侧按钮的点击事件

        next.click()

      }, 1000)

    })