Index acquisition problem of callback function in asynchronous event

Today, here is a demo about clicking li to get the corresponding index. There are detailed explanations and a guide to avoid pits. Please eat with peace of mind. Today is also a day full of dry goods.

The source of inspiration is a question in the H5 exam, which is probably the following requirements:

The picture is a little unclear, everyone will see it

Let's do a similar demo today, 手把手 \color{FF9640} {hand in hand} teach you how to get the value of the clicked li, mother no longer have to worry that I can't get the value~

1. Ideas

The reason is actually very simple, that is, there are many li in each ul, and each li corresponds to a picture or a div, the number and order of all li and pictures are the same, we can get the current click through js If li is the first number, you can get the picture or div corresponding to li through this index

Then changing the li and image styles is a matter of memorizing the code to change the css style (change the class or style after getting it)

S t e p 1 \color{#FF7400} {Step 1}

We set a class called current, remember that it is unique , whichever li is clicked, give this unique class to whom.

S t e p 2 \color{#FF7400} {Step 2}

Since it is a unique class, you must give it a solemn authorization ceremony before giving it to li, ahem, wrong!!!

先把所有的li的class样式先清空,避免有其他的li也私藏了current这个class的情况,所有搜身清空完毕后,再把独一无二的class=“current”给到点击的li,这样就完成了li的点击样式改变。

S t e p 3 \color{#FF7400} {Step 3}

img的样式直接用display:block显示/none隐藏 来控制就好,将获取到的index拿去匹配所有的imgs,就可以适合li的小妾啦,适合li的图片了

S t e p 4 \color{#FF7400} {Step 4}

li和img的样式都用到的是排他的思想,用多了自然就熟悉了,感觉很难,理解了原理和逻辑就可以手到擒来啦

2.代码

今天使用的工具依然是VSCodeQQ截图20220102225155.png和preview on live server这个插件 点 这儿 \color{#FFB300} {这儿} 查看详情

1.html

<div class="container">
        <ul>
            <li class="current">熊猫1号</li>
            <li>熊猫2号</li>
            <li>熊猫3号</li>
            <li>小狗1号</li>
        </ul>
        <div class="imgs">
            <img src="./images/panda1.jpg" alt="1" style="display: block;">
            <img src="./images/panda2.jpg" alt="2">
            <img src="./images/panda3.jpg" alt="3">
            <img src="./images/dog3.jpg" alt="4">
        </div>
    </div>
复制代码

2.css

样式的话,我们给container设置合适的宽度,再利用父相子绝的技巧(父元素相对定位,子元素绝对定位),图片也使用绝对定位,就不会拍列起来了,重叠的放置在同一个位置。

再用到一个盒子垂直居中的方法,将盒子定位在body的中央,由于默认的body标签的宽是浏览器一样宽,但是高度是由内容撑开的,我们就暂且给body手动设置一个高度。

不要忘记设置一个独一无二的名为current的class(命名为啥都OK,天知地知你知我知),这个里设置好当li被点击到要变成的样式,这里我改变了背景颜色和字体颜色

        * {
            margin: 0;
            padding: 0;
            list-style: none;
        }
        
        body {
            height: 800px;
            /*垂直水平居中方法2.*/
            display: flex;
            justify-content: center;
            align-items: center;
        }
        
        .container {
            width: 300px;
            height: 200px;
            /*父相子绝,使子元素不超出父元素,而导致溢出*/
            position: relative;
            /*整个盒子垂直居中显示*/
            /*方法1.*/
            /* left: 50%;
            top: 50%;
            margin-left: -150px;
            margin-top: -100px; */
        }
        
        .container ul {
            width: 30%;
            height: 100%;
            position: absolute;
        }
        
        li {
            height: 50px;
            line-height: 50px;
            text-align: center;
            background-color: rgba(49, 146, 92, 0.795);
            /*透明度,整个元素都透明*/
            opacity: 0.8;
        }
        
        .current {
            background-color: rgb(31, 109, 64);
            color: white;
            cursor: pointer;
        }
        
        img {
            /*先将所有的图片隐藏起来,在行内元素设置单独的图片为block 显示一张图片*/
            display: none;
            width: 100%;
            height: 100%;
        }
        
        .container .imgs {
            position: absolute;
            height: 100%;
            width: 70%;
            left: 30%;
        }
复制代码

3.JS

基础设置,获取元素

 var lis = document.querySelectorAll("li"); //获取到所有的小li,这个方法获取的到的是一个伪数组
 var imgs = document.querySelectorAll("img"); //获取到所有的图片
 
 //lis和imgs获取到的是伪数组,可以使用数组的一些方法,但不是真正的数组
复制代码

1.难点

逻辑很简单,就是获取点击到的li对应的index,但是由于在js中,事件是异步的,并且计算机执行的速度非常快,所有会导致不能直接在事件的回调函数直接中获取到li的index,做个演示

var lis = document.querySelectorAll("li"); //获取到所有的小li
        var imgs = document.querySelectorAll("img"); //获取到所有的图片
        //lis和imgs获取到的是伪数组,可以使用数组的一些方法,但不是真正的数组

        //1.循环遍历,并且判断是哪个li被点击了
        //2.先将所有的li和img的样式清空,再进行点击所对应的元素的样式设置(排他思想)
        for (var i = 0; i < lis.length; i++) {
            lis[i].onclick = function(i) {
                //这样是没有办法获得点击li的对应的index,因为事件是异步的,获取到的i已经是循环完毕后的i了,也就是此时的i已经变成了4
                //不能获取到对应的i,就不能改变相应的样式
                console.log(i); //4,4,4,4
            }
        }
复制代码

不管我点击哪个li,输出的i都为4

QQ截图20220327165326.png

方法1.给每一个li设置专有的index

for (var i = 0; i < lis.length; i++) {
            lis[i].index = i;

            //或者使用setAttribute()也可以
            
            //lis[i].setAttribute("index",i)
            //用lis[i].getAttribute(index)获取值

            lis[i].onclick = function(i) {
                console.log(this.index);
                for (var i = 0; i < lis.length; i++) {
                    lis[i].className = ""; //清空li样式
                    imgs[i].style.display = "none"; //清空img
                }
                
                //给点击的li和对应的img设置样式
                this.className = "current";
                imgs[this.index].style.display = "block"
            }
        }
复制代码

方法2.使用jquery

先引入jquery

  $("ul li").click(function() {
            //获取到点击的li的index
            var index = $(this).index();

            //先给点击到的li的class样式设置为current
            //再将点击到的li的其他兄弟节点的current的class演示移除(排他思想)
            //siblings()获取所有的兄弟节点
            $(this).addClass("current");
            $(this).siblings().removeClass("current");

            //先给点击到的li对应的图片设置为显示
            //再将点击到的li的其他兄弟节点的图片设置为隐藏
            //siblings()获取所有的兄弟节点  eq()匹配为index的元素
            $(".imgs img").eq(index).css("display", "block")
            $(".imgs img").eq(index).siblings().css("display", "none")
        })
复制代码

方法3.使用ES6的 let(块级作用域)

 for (let i = 0; i < lis.length; i++) {
            lis[i].onclick = function() {
                console.log(i);
                for (let i = 0; i < lis.length; i++) {
                    lis[i].className = ""; //清空li样式
                    imgs[i].style.display = "none"; //清空img
                }
                //给点击的li和对应的img设置样式
                this.className = "current";
                imgs[i].style.display = "block"
            }
        }
复制代码

2.进阶的方法

方法4.使用立即执行函数()()形成闭包函数

for (var i = 0; i < lis.length; i++) {
            lis[i].onclick = (
            
            //closure闭包开始
                function(index) {
                    return function() {
                       // console.log(index);
                        for (var i = 0; i < lis.length; i++) {
                            lis[i].className = ""; //清空li样式
                            imgs[i].style.display = "none"; //清空img
                        }
                        //给点击的li和对应的img设置样式
                        this.className = "current";
                        imgs[index].style.display = "block"
                    }
                }
            )(i)
            
        }
复制代码

方法5.使用bind()改变this指向每一条li,并传参i

for (let i = 0; i < lis.length; i++) {
            lis[i].onclick = function(index) {
                //console.log(index);
                for (let i = 0; i < lis.length; i++) {
                    lis[i].className = ""; //清空li样式
                    imgs[i].style.display = "none"; //清空img
                }
                //给点击的li和对应的img设置样式
                this.className = "current";
                imgs[index].style.display = "block"
            }.bind(lis[i], i)
        }
复制代码

方法6.forEach

数组的forEach方法也可以获取index的值,但是这里获取到的lis和imgs是伪数组,所有不适用

3.完整代码

我用的是let来获取index,Jquery文件和图片需要自己引入,并且注意路劲是否正确。

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

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            list-style: none;
        }
        
        body {
            height: 800px;
            /*垂直水平居中
            方法2.*/
            display: flex;
            justify-content: center;
            align-items: center;
        }
        
        .container {
            width: 300px;
            height: 200px;
            /*父相子绝,使子元素不超出父元素,而导致溢出*/
            position: relative;
            /*整个盒子垂直居中显示*/
            /*方法1.*/
            /* left: 50%;
            top: 50%;
            margin-left: -150px;
            margin-top: -100px; */
        }
        
        .container ul {
            width: 30%;
            height: 100%;
            position: absolute;
        }
        
        li {
            height: 50px;
            line-height: 50px;
            text-align: center;
            background-color: rgba(49, 146, 92, 0.795);
            /*透明度,整个元素都透明*/
            opacity: 0.8;
        }
        
        .current {
            background-color: rgb(31, 109, 64);
            color: white;
            cursor: pointer;
        }
        
        img {
            /*先将所有的图片隐藏起来,在行内元素设置单独的图片为block 显示一张图片*/
            display: none;
            width: 100%;
            height: 100%;
        }
        
        .container .imgs {
            position: absolute;
            height: 100%;
            width: 70%;
            left: 30%;
        }
    </style>
</head>

<body>
    <div class="container">
        <ul>
            <li class="current">熊猫1号</li>
            <li>熊猫2号</li>
            <li>熊猫3号</li>
            <li>小狗1号</li>
        </ul>
        <div class="imgs">
            <img src="./images/panda1.jpg" alt="1" style="display: block;">
            <img src="./images/panda2.jpg" alt="2">
            <img src="./images/panda3.jpg" alt="3">
            <img src="./images/dog3.jpg" alt="4">
        </div>
    </div>
</body>
<script>
    window.onload = function() {
        //1.使用循环
        var lis = document.querySelectorAll("li"); //获取到所有的小li
        var imgs = document.querySelectorAll("img"); //获取到所有的图片
        //lis和imgs获取到的是伪数组,可以使用数组的一些方法,但不是真正的数组

        //1.循环遍历,并且判断是哪个li被点击了
        //2.先将所有的li和img的样式清空,再进行点击所对应的元素的样式设置(排他思想)



        //方法2. 使用ES6的let,let是块级作用域,可以直接获取i
        for (let i = 0; i < lis.length; i++) {
            lis[i].onclick = function() {
                console.log(i);
                for (let i = 0; i < lis.length; i++) {
                    lis[i].className = ""; //清空li样式
                    imgs[i].style.display = "none"; //清空img
                }
                //给点击的li和对应的img设置样式
                this.className = "current";
                imgs[i].style.display = "block"
            }
        }



    }
</script>
//记得引入jquery
//1.采用本地文件
<script src="./Jquery.js"></script>

//2.CDN

</html>
复制代码

小M.祝大家学习顺利,考试顺利,工作顺利!欢迎评论区和私信

转载:欢迎转载,但未经作者同意,必须保留此段声明;

小白纯分享,欢迎大佬纠错

Guess you like

Origin juejin.im/post/7079703850503897096