惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
About on SuperTechFans
Y
Y Combinator Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Microsoft Security Blog
Microsoft Security Blog
aimingoo的专栏
aimingoo的专栏
I
InfoQ
C
Check Point Blog
IT之家
IT之家
MyScale Blog
MyScale Blog
Apple Machine Learning Research
Apple Machine Learning Research
Vercel News
Vercel News
Last Week in AI
Last Week in AI
GbyAI
GbyAI
P
Proofpoint News Feed
量子位
Stack Overflow Blog
Stack Overflow Blog
Microsoft Azure Blog
Microsoft Azure Blog
月光博客
月光博客
阮一峰的网络日志
阮一峰的网络日志
人人都是产品经理
人人都是产品经理
B
Blog
T
The Blog of Author Tim Ferriss
H
Help Net Security
云风的 BLOG
云风的 BLOG

博客园 - dosboy

Python趣味入门14:类的继承 Python趣味入门13:类中的各种变量 Python趣味入门12:初遇类与实例 Python趣味入门11:函数作为参数以及匿名函数 Python趣味入门10:推倒繁琐化烦为简的推导式 Python趣味入门9:函数是你走过的套路,详解函数、调用、参数及返回值 买我的《Python青少年趣味编程》给寒假爱编程的小朋友一点温暖。 Python趣味入门8:集合变量-列表、集合、元组、字典 Python趣味入门7:循环与条件的爱恨情仇 Python趣味入门6:能计数的循环语句for Python趣味入门5:循环语句while Python趣味入门4:选择往往是最重要的-条件语句 Python趣味入门3:变量、字串输入与输出 MacOs安装python3的mysqlclient组件支持django运行 Python入门的三大问题和三大谎言 Python趣味入门02: 妥妥地安装配置Python(Windows版) 轻松搞懂Python递归函数的原理与应用 Python趣味入门01:你真的了解Python么? Python魔术世界 1 如何使用Visual Studio在WIN10中一键安装Python3入门编程环境并测试Django
如何使用Javascript开发sliding-nav带滑动条效果的导航插件?
dosboy · 2021-03-28 · via 博客园 - dosboy

本文介绍如何使用纯Javascript来开发一款简单的JS插件,本插件可以实现鼠标悬停在导航上时,下方的滑动条自动从当前菜单滑动到所选菜单当中去。

本项目的源代码寄宿于GitHub,记得点小星星哦:

https://github.com/dosboy0716/sliding-nav

一、前言

效果如下图:

二、使用方法

本插件只需要如下的三步,就可以在您的项目中使用:

1、在</body>标记结束前,引用sliding-nav.js文件

2、在需要滑动条的菜单容器上加类名 sliding-nav,当前项使用类名:active

3、使用属性来定定外观:sn-color="颜色" sn-radius="圆度" sn-height="高度"

<script src="/path/to/sliding-nav.js"></script>
<ul class="nav sliding-nav" sn-color="#F00" sn-radius="0px" sn-height="3px">
    <li class="active">菜单项1</li>
    <li>菜单项2</li>
    <li>菜单项3</li>
<ul>

三、开发过程

1. 模型示例

导航菜单一般使用上图的层次型结构,外层容器使用<ul> 标记,菜单项使用<li>标记,假设如果要显示黄色小横条,如何定位很重要。

经过分析,虽然在视觉上小横条位于UL之内,为了不破坏原来导航的样式,小黄条必须使用absolute的绝对定位,并且初始位置与ul标记相同。

因此,我们把小横条插入<ul>标记的前面,如上面的小灰点,它就是小横条的初始位置即(left=0,top=0)的位置。

那么我们如何让小条看起来在菜单项的正下方呢?

  • 把小条的top属性赋值为菜单项的高度(即offsetHeight属性),
  • 把小条的left属性赋值为菜单项的左边距(即offsetLeft属性)

实现上面的功能可以使用如下的代码:

function init() {

    var navs = document.getElementsByClassName('sliding-nav');

    for (var i = 0; i < navs.length; i++) {


        //创建一个DIV与当前导航竖向对齐
        var indi = document.createElement("div");
        indi.id = "slna-indicator"

        indi.style.borderRadius = navs[i].getAttribute("sn-radius") || "0px"
        indi.style.height = navs[i].getAttribute("sn-height") || "3px"
        indi.style.backgroundColor = navs[i].getAttribute("sn-color") || "#F00"

        indi.style.position = "absolute"
        indi.style.transition = "0.5s"

        //查找当前子菜单项,如果有类名active或者是selected就视为当前项,如果没有使用第1项
        var selected = navs[i].getElementsByClassName('active')
        if (selected.length == 0) {
            selected = navs[i].getElementsByClassName('selected')
        }
        if (selected.length == 0) {
            selected = navs[i].children
        }

        if (selected.length == 0) {
            throw Error('Sorry, Navigation bar has no item at all!');
        }

        selected = selected[0];

        indi.style.width = selected.offsetWidth + "px";
        indi.style.top = selected.offsetHeight + "px";
        indi.style.left = selected.offsetLeft + "px";
        navs[i].parentElement.insertBefore(indi, navs[i]);

        //未完成,下面插入代码以绑定事件




    }

}

如上的代码构建了初始化函数init(),此函数:

查找所有含有类名sliding-nav的标记,并且按照上面的方法,在前面插入div标记充当“指示条”,并且查找“活动”的菜单项,找到后通过这个菜单项的各个属性给“指示条”定位。

2、事件与动画

我们把"指示条"div 标记transition属性设置成了0.5s,那么只要在事件里直接设置该div的如下:

  • left属性就可以实现"指示条"的移动
  • width属性就可以设置"指示条"的宽度

所以可以在如上的代码末尾,插入如下的代码实现事件与动画:

for (var j = 0; j < navs[i].children.length; j++) {

            hover(navs[i].children[j], function(e, elem) {

                indi.style.width = elem.offsetWidth + "px";
                indi.style.left = elem.offsetLeft + "px";

            });

            //移出导航就恢复默认
            hover(navs[i], null, function(e, elem) {
                indi.style.width = selected.offsetWidth + "px";
                indi.style.left = selected.offsetLeft + "px";
            });

        }

其中代码,用到了自定义函数hover,该函数类似于实现hover事件,JS原生只有mouseover和mouseout事件。

hover(绑定DOM元素,移入事件函数,移出事件函数)

函数作用是给DOM元素绑定鼠标移入和鼠标移出事件,具体实现的过程,可以看作者原代码。

四、所有原代码

本文实现的所有原代码如下,希望读者提出更加优化的建议,我们一起打造更加唯美的前端体验。

  1 /*
  2 
  3 Usage
  4 1. Include file sliding-nav.js before tag</body> in a HTML file.
  5 
  6 <script src="/path/to/sliding-nav.js"></script>
  7 
  8 2. Use class name sliding-nav in a navigation bar element,and use .active for a selected menu item.
  9 3. Use following attributes to change its color,radius and height:sn-color, sn-radius,sn-height. (if no these attributes, default settings following will be used)
 10 
 11 <ul class="nav sliding-nav" sn-color="#F00" sn-radius="0px" sn-height="3px">
 12     <li class="active">menu-item 1</li>
 13     <li>menu-item 2</li>
 14     <li>menu-item 3</li>
 15 <ul>     
 16 
 17 使用方法
 18 1、在</body>标记结束前,引用sliding-nav.js文件
 19 2、在需要滑动条的菜单容器上加类名 sliding-nav,当前项使用类名:active
 20 3、使用属性来定定外观:sn-color="颜色" sn-radius="圆度" sn-height="高度"
 21 
 22 
 23 <script src="/path/to/sliding-nav.js"></script>
 24 <ul class="nav sliding-nav" sn-color="#F00" sn-radius="0px" sn-height="3px">
 25     <li class="active">菜单项1</li>
 26     <li>菜单项2</li>
 27     <li>菜单项3</li>
 28 <ul>
 29     
 30 Sliding Navigation Bar By yan,ZHANG 
 31 Mailto: 26959368@qq.com
 32 2020.02.06
 33 */
 34 
 35 
 36 window.onload = function() {
 37     init();
 38 };
 39 
 40 function bind(elem, ev, callback) {
 41     if (document.all) {
 42         elem.attachEvent("on" + ev, callback);
 43     } else {
 44         elem.addEventListener(ev, callback, false);
 45     }
 46 }
 47 
 48 function unbind(elem, ev, callback) {
 49     if (typeof(callback) == "function") {
 50         if (document.all) {
 51             elem.detachEvent("on" + ev, callback);
 52         } else {
 53             elem.removeEventListener(ev, callback, false);
 54         }
 55     } else {
 56         if (document.all) {
 57             elem.detachEvent("on" + ev);
 58         } else {
 59             elem.removeEventListener(ev, false);
 60         }
 61     }
 62 }
 63 
 64 function hover(elem, overCallback, outCallback) { //实现hover事件
 65     var isHover = false; //判断是否悬浮在上方
 66     var preOvTime = new Date().getTime(); //上次悬浮时间
 67     function over(e) {
 68         var curOvTime = new Date().getTime();
 69         isHover = true; //处于over状态
 70         if (curOvTime - preOvTime > 10) { //时间间隔超过10毫秒,认为鼠标完成了mouseout事件
 71             overCallback && overCallback(e, elem);
 72         }
 73         preOvTime = curOvTime;
 74     }
 75 
 76     function out(e) {
 77         var curOvTime = new Date().getTime();
 78         preOvTime = curOvTime;
 79         isHover = false;
 80         setTimeout(function() {
 81             if (!isHover) {
 82                 outCallback && outCallback(e, elem);
 83             }
 84         }, 10);
 85     }
 86     bind(elem, "mouseover", over);
 87     bind(elem, "mouseout", out);
 88 };
 89 
 90 
 91 
 92 
 93 function init() {
 94 
 95     var navs = document.getElementsByClassName('sliding-nav');
 96 
 97     for (var i = 0; i < navs.length; i++) {
 98 
 99 
100         //创建一个DIV与当前导航竖向对齐
101         var indi = document.createElement("div");
102         indi.id = "slna-indicator"
103 
104         indi.style.borderRadius = navs[i].getAttribute("sn-radius") || "0px"
105         indi.style.height = navs[i].getAttribute("sn-height") || "3px"
106         indi.style.backgroundColor = navs[i].getAttribute("sn-color") || "#F00"
107 
108         indi.style.position = "absolute"
109         indi.style.transition = "0.5s"
110 
111         //查找当前子菜单项,如果有类名active或者是selected就视为当前项,如果没有使用第1项
112         var selected = navs[i].getElementsByClassName('active')
113         if (selected.length == 0) {
114             selected = navs[i].getElementsByClassName('selected')
115         }
116         if (selected.length == 0) {
117             selected = navs[i].children
118         }
119 
120         if (selected.length == 0) {
121             throw Error('Sorry, Navigation bar has no item at all!');
122         }
123 
124         selected = selected[0];
125 
126         indi.style.width = selected.offsetWidth + "px";
127         indi.style.top = selected.offsetHeight + "px";
128         indi.style.left = selected.offsetLeft + "px";
129         navs[i].parentElement.insertBefore(indi, navs[i]);
130 
131         for (var j = 0; j < navs[i].children.length; j++) {
132 
133             hover(navs[i].children[j], function(e, elem) {
134 
135                 indi.style.width = elem.offsetWidth + "px";
136                 indi.style.left = elem.offsetLeft + "px";
137 
138             });
139 
140             //移出导航就恢复默认
141             hover(navs[i], null, function(e, elem) {
142                 indi.style.width = selected.offsetWidth + "px";
143                 indi.style.left = selected.offsetLeft + "px";
144             });
145 
146         }
147 
148 
149 
150 
151     }
152 
153 }