异步事件中回调函数的index获取问题

今天来一个关于点击li获取对应的index的demo,内有详解和避坑指南,请安心食用,今日也是干货满满的一天

灵感来源是H5考试的一道题目,大概是这样的要求:

图片有点不清晰 大家将就看

今天就来做一个类似的demo, 手把手 \color{FF9640} {手把手} 教会你怎么获取点击的li的值,妈妈再也不用担心我获取不到值啦~

1.思路

道理其实很简单,就是每个ul里有很多li,每个li又对应了一张图片或者一个div,所有每个li和图片的数量和顺序是相同的,我们可以通过js获取到当前点击的li是第几个的话,就可以通过这个index来获取到li所对应的图片或者div

那么改变li和图片样式的,就是死记硬背的改变css样式的代码的事了(获取后改变class或者style)

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

我们设置一个class叫current,记住它是独一无二的,哪个li被点击了,就把这个独一无二的class给谁。

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

既然是独一无二的class,在给li之前肯定要先给它来一个庄重的授权仪式,咳咳,说错了!!!

先把所有的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.祝大家学习顺利,考试顺利,工作顺利!欢迎评论区和私信

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

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

猜你喜欢

转载自juejin.im/post/7079703850503897096