JavaScript 实现轮播图

42 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第13天,点击查看活动详情

  • 我们通过两种方式来实现轮播图,这两种方式其实就是我们使用框架的时候,路由的底层实现,通过 historyhash 这两种方式来实现, 这两种方式都可以实现刷新页面保存状态

  • 让我们先了解以下,这两种方式都是怎么实现的

一、 使用 history 实现历史记录

  1. 使用 history.pushState(参数1,参数2,参数3) 存储对应参数, 参数1是要存的数据,是字符串格式,参数2和参数3是字符串格式,对应的名字
  2. history.pushState() 事件绑定给需要拿到历史记录的事件中, 将对应的数据存入第一个参数
  3. history.state 得到pushState中存储的第一个参数
  4. 使用window 监听 'popstate' 事件, 当点击上一页或下一页的时候触发这个事件
  5. 事件触发以后会得到 histort.state, 就是每一次历史记录, 也就是对应事件触发的时候存储的数据

二、 使用 hash 实现历史记录

  1. 使用window 监听 'hashchange' 事件, 当点击上一页下一页的时候,触发这个事件
  2. 触发事件以后,能够拿到上一页对应的锚点信息,使用锚点后面的信息处理页面,如轮播图
  3. 使用location.hash.slice(1) 拿到对应锚点的 id 值, 第一位是#,第二位是数字
  • 下面咱们来看一下这两种方式,都分别有什么优缺点? 这也是面试的时候比较常问的问题了,面试官一般会问: 实现路由的原理了解吗? 了解, 我们也不要等着面试官继续问,都是什么请说一下,这种被动提问的方式是面试官最讨厌的。 我们可以直接说出来这两种方式, 实现原理就是我上面写的了, 每个分别是侦听了什么事件, 这个点答上来其实就可以了,当然,流程能全部答上来也是更好的,然后再把这二者的区别说一下就ok了。

二者的区别

  1. hash 就是指 url 后面的 # 号以及后面的字符,history没有带#,外观上比hash 模式好看些
  2. hash 能兼容到 IE8 , history 只能兼容到 IE10
  3. hash 值的改变不会导致浏览器向服务器发出请求,因为hash只出现在 URL 中,history模式的 URL 要和 后端一致,刷新会从新请求(前进和后退不会),后端没有及时响应 或没有对应路径路由的处理 会报 404, 所以一般后端要处理匹配不到的时候 应该重定向到 index 页面
  4. pushState()设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中,而hash设置的新值必须与原来不一样才会触发动作将记录添加到栈中
  5. pushState()可额外设置title属性供后续使用
  • 下面是我们实现轮播图的一个示例,我会先粘贴 css 和 html 代码, js代码 单独来, 因为是两个逻辑

先上 css 和 html

    <style>
        body{
            margin: 0;
        }
        #crousel{
            width: 100vw;
            height: 45vw;
            position: relative;
        }
        #crousel>img{
            width: 100%;
            height: 100%;
            display: block;
            position: absolute;
            opacity: 0;
            transition:  all 0.5s;
            
        }
        #crousel>ul{
            position: absolute;
            list-style: none;
            margin: 0;
            padding: 0;
            top: 45vw;
        }
        #crousel>ul>li{
            width: 20%;
            float: left;
        }
        #crousel>ul img{
            width: 100%;
            border:2px solid transparent;
            box-sizing: border-box;
        }
       
    </style>
    
    <body>
    <div id="crousel">
        <img class="showImg" src="./img/a.jpg">
        <img class="showImg" src="./img/b.jpg">
        <img class="showImg" src="./img/c.jpg">
        <ul>
            <li><a href="#0"><img class="icon" src="./img/a.jpg"></a></li>
            <li><a href="#1"><img class="icon" src="./img/b.jpg"></a></li>
            <li><a href="#2"><img  class="icon" src="./img/c.jpg"></a></li>
        </ul>
    </div>
    </body>
  • 以上我使用了三种图片,这个图片大家可以在自己本地随便找三张 替换掉就可以了

下面上 hash js 的实现

<script>
        var icons,showImgs;
        var prevList={};

        init();
        function init(){
            var index=Number(location.hash.slice(1)) || 0;
            icons=Array.from(document.getElementsByClassName("icon"));
            showImgs=Array.from(document.getElementsByClassName("showImg"));
            changePrev(index,"icon","borderColor");
            changePrev(index,"showImg","opacity");
            window.addEventListener("hashchange",hashChangehandler);
        }

        function hashChangehandler(e){
            var index=Number(location.hash.slice(1));
            changePrev(index,"icon","borderColor");
            changePrev(index,"showImg","opacity");
        }
        // 这两个逻辑其实差不多,上面的逻辑都是用来操作历史的, 大家可能对这一步会有一些疑问,prevList
        // 这个其实就是有一个暂存区,用来存储我们当前选中的信息的,每一次历史记录的切换,或者点击事件,都 
        // 会执行这里,当前展示的图片样式进行处理
        function changePrev(index,name,styleName){
            if(prevList[name]){
                prevList[name].style[styleName]=styleName==="borderColor" ? "transparent" : 0;
            }
            prevList[name]= name==="icon" ? icons[index] : showImgs[index];
            prevList[name].style[styleName]=styleName==="borderColor" ? "red" : "1";
        }

    </script>

下面上 history js 的实现

 <script>
        var icons,showImgs;
        var prevList={ };

        init();
        function init(){
            var index=history.state || 0;
            icons=Array.from(document.getElementsByClassName("icon"));
            showImgs=Array.from(document.getElementsByClassName("showImg"));
            icons.forEach(function(item){
                item.addEventListener("click",clickHandler);
            });
            history.replaceState(index,"showimg");
            changePrev(index,"icon","borderColor");
            changePrev(index,"showImg","opacity");
            // popstate只有点击历史回退和前进时,才会触发popstate事件
           window.addEventListener("popstate",popstateHandler);
        }

        function popstateHandler(e){
            changePrev(history.state,"icon","borderColor");
            changePrev(history.state,"showImg","opacity");
        }
       
        function clickHandler(e){
            var index=icons.indexOf(this);
            history.pushState(index,"showimg");
            changePrev(index,"icon","borderColor");
            changePrev(index,"showImg","opacity");
        }


        function changePrev(index,name,styleName){
            if(prevList[name]){
                prevList[name].style[styleName]=styleName==="borderColor" ? "transparent" : 0;
            }
            prevList[name]=name==="icon" ? icons[index] : showImgs[index];
            prevList[name].style[styleName]=styleName==="borderColor" ? "red" : "1";
        }


    </script>