原生JS实现导航下拉菜单效果

 更新时间:2020年11月25日 10:33:16   作者:QxQstar  
这篇文章主要介绍了JS实现导航下拉菜单效果,用原生JS实现的一个导航下拉菜单,下拉菜单的宽度与浏览器视口的宽度一样,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

这个导航下拉菜单需要实现的功能是:下拉菜单的宽度与浏览器视口的宽度一样宽;一级导航只有两项,当鼠标移到一级导航上的导航项时,相应的二级导航出现。在本案例中通过改变二级导航的高度来实现二级导航的显示和消失。为了便于理解我画了一个图,如下:

在这个案例主要用到的知识有:设置定时器,清除定时器,mouseout和mouseover事件,另外还有css中position相关知识。本案例分为两部分讲解。第一部分html和css,第二部分js。

一. html和css

将导航这个导航条包裹在一个div中,这个div的position值为relative,高度为50px(导航条的高度为50px),宽度为100%,将最外层的div的position属性设置为relative是因为二级导航要根据这个div来定位。这个导航条的结构是二级嵌套无序列表。每一个一级导航项li都嵌套了它对应的无序列表。需要将嵌套的无序列表移除文档流。所以嵌套的无序列表的position值为absolute,top:50px(导航条的高度)。left:0;right:0;通过设置这些值可以使嵌套的无序列表宽度为浏览器视口的宽度。通过将li的display值设置inline-block并且将外层div的text-align设置为center使得导航项居中显示

注:在这个案例中一定要将嵌套的无序列表的position的值设置为absolute,使它移除文档流。

html和css代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>下拉菜单</title>
 <link rel="stylesheet" type="text/css" href="index.css" rel="external nofollow" >
</head>
<body>
 <div class='header'>
  <ul class='outer' id='outer'>
   <li class='outerList' id='outerList1'><a href='#' id='link1' class='link'>产品<span></span></a>
    <ul class='inter' id='inter1'>
     <li>
      <a href='#'>
       <img src='img/01fea55541ed73000001714a430253.jpg'>
       <strong>纳斯</strong>
      </a>
     </li>
     <li>
      <a href='#'>
       <img src='img/thumb_image3.jpg'>
       <strong>纯色</strong>
      </a>
     </li>
     <li>
      <a href='#'>
       <img src='img/白胡子.jpg'>
       <strong>保温杯</strong>
      </a>
     </li>
     <li>
      <a href='#'>
       <img src='img/宠物.jpg'>
       <strong>设计周边</strong>
      </a>
     </li>
    </ul>
   </li>
   <li class='outerList' id='outerList2'><a href='#' id='link2' class='link'>服务<span></span></a>
    <ul class='inter' id = 'inter2'>
     <li>
      <a href='#'>
       <img src='img/狮子座.jpg'>
       <strong>售后服务</strong>
      </a>
     </li>
     <li>
      <a href='#'>
       <img src='img/莲花禅.jpg'>
       <strong>设计师</strong>
      </a>
     </li>
    </ul>
   </li>
  </ul>
 </div>
 <script type="text/javascript" src='index.js'></script>
</body>
</html>

css代码如下: 

*{
 padding: 0;
 margin: 0;
}
.header{
 position: relative;
 width: 100%;
 height: 50px;
 background-color: #000000;
 text-align: center;
 z-index: 2;
}
.header .outer li{
 display: inline-block;
 list-style: none;
}
.outerList{
 height: 50px;
 line-height: 50px;

}
.outerList a{
 display: block;
 padding: 0 15px;
 color: #fff;
 text-decoration: none;
}
.outerList:hover a{
 color: #EDECEC;

}
.outerList .link span{
 display: block;
 height: 0;
 width: 100%;
 position: relative;
 top: -10px;
 left: 0;
 background-color: #fff;

}
.outerList:hover .link span{
 height: 1px;
}
.outerList .inter{
 position: absolute;
 left: 0;
 height: 0;
 overflow: hidden;
 top: 50px;
 right: 0;
 background-color:rgba(0,0,0,0.5);
}
.outerList .inter li{
 margin-top: 30px;
}
.outerList .inter strong{
 display:block;
 height: 25px;
 line-height: 25px;
 text-align: center;
}

二. js部分

在js部分涉及到的知识主要有:设置定时器,清除定时器,mouseout和mouseover事件。

mouseout事件当鼠标从一个元素上移入另一个元素的上时,会在失去鼠标的那个元素上触发mouseout事件。获得鼠标的那个元素可能是失去鼠标的元素的父元素或子元素,获得鼠标的那个元素也可能位于失去鼠标元素的外部。当在一级导航项上触发mouseout事件时,我们需要判断获得鼠标的元素是不是一级导航项的子孙元素。当一个元素触发了mouseout事件时,去鼠标的元素为目标元素(target),获得鼠标的元素为相关元素(relatedTarget)。所以需要判断相关元素是否为一级导航项的子孙元素,如果是子孙元素,则相应的导航项的二级导航项高度不变。如果不是子孙元素,则相应的二级导航项消失。判断是否为子孙元素的代码如下:

var flag1 = false,flag2 = false;

if(relatedTarget !== null){
  var parented = relatedTarget.parentNode;
  do{
   if(parented === outerList1 || relatedTarget === outerList1){
    flag1 = true;
    break;
   }else if(parented === outerList2 || relatedTarget === outerList2){
    flag2 = true;
    break;
   }else{
    parented = parented.parentNode;
   }
  }while(parented !== null);
 }

注:通过判断flag1和flag2的值来确定是否该把二级菜单的高度变为0,如果flag1的值为false则让outerList1对应的二级菜单消失,如果flag2为false则将outerList2对应的二级菜单消失。

mouseover事件当鼠标移入一个元素内部时,获得鼠标的元素上触发这个事件,获得鼠标的元素可能位于失去鼠标的外部,也可能位于失去鼠标元素的内部。获得鼠标的元素是目标元素,失去鼠标的元素为相关元素。在这个案例中我们只需要判断mouseover的目标元素,但是对于mouseout事件我们需要判断相关元素。

注:在支持DOM的浏览器中,mouseout和mouseover的相关元素都保存在事件对象(event)的relatedTagrget属性中,但是在IE浏览器中,对于mouseout事件而言,相关事件保持在事件对象(event)的toElement属性中,对于mouseover事件而言,相关事件保存在事件对象(event)的fromElement属性中。

设置定时器和清除定时器在这个案例中嵌套无序列表的消失和出现是通过改变它的高度实现的,它的高度是逐渐变化,所以我使用的setTimeout这个定时器,为了能够清除定时器还要将定时器标识保存在一个变量中。清除定时器的目的是为了防止当快速移动鼠标时嵌套无序列表的高度抖动(即:一个定时器里的回调函数让高度增加,另一个定时器的回调函数让高度减小)。

js代码如下: 

var untilEvent = {
 addEvent:function(element,type,hander){
  if(element.addEventListener){
   element.addEventListener(type,hander,false);
  }else if(element.attachEvent){
   element.attachEvent('on'+type,hander);
  }else{
   element['on'+type] = hander;
  }
 },
 getEvent:function(event){
  return event?event:window.event;
 },
 getTarget:function(event){
  return event.target||event.srcElement;
 },
 getRelated:function(event){
  if(event.relatedTarget){
   //兼容DOM的浏览器将相关元素保持在relatedTarget属性中
   return event.relatedTarget;
  }else if(event.toElement){
   //在IE浏览器中mouseout事件的相关元素保存在toElement属性中
   return event.toElement;
  }else if(event.fromElement){
   //在IE浏览器中mouseover事件的相关元素保持在fromElement属性中
   return event.fromElement;
  }else{
   return null;
  }
 }

};
//下面这四个元素用于表示四个定时器的标识,最开始我只使用两个定时器,当快速移动时
//动画会乱。
var timeDec1,timeAdd1,timeAdd2,timeDec2;//定时器标识
function getOuter(){
 var outer = document.getElementById('outer');
 untilEvent.addEvent(outer,'mouseover',callBackOver);
 untilEvent.addEvent(outer,'mouseout',callBackOut);
}
//mouseout事件:当鼠标从一个元素移入另一个元素时在鼠标离开的那个元素
//上触发,获得鼠标的元素可能在失去鼠标元素的外部也可能在失去鼠标元素的
//内部.所以需要判断mouseout事件的相关元素是否为外部li(即id为outerList或id为outerList2)元素
//的子孙元素,如果是子孙元素,则内部无序列表无须收起。
function callBackOut(event){
 var event = untilEvent.getEvent(event);
 var relatedTarget = untilEvent.getRelated(event);
 var outerList1 = document.getElementById('outerList1');
 var inter1 = document.getElementById('inter1');
 var outerList2 = document.getElementById('outerList2');
 var inter2 = document.getElementById('inter2');
 var flag1 = false,flag2 = false;
 if(relatedTarget !== null){
  var parented = relatedTarget.parentNode;
  do{
   if(parented === outerList1 || relatedTarget === outerList1){
    flag1 = true;
    break;
   }else if(parented === outerList2 || relatedTarget === outerList2){
    flag2 = true;
    break;
   }else{
    parented = parented.parentNode;
   }
  }while(parented !== null);
 }
 if(!flag1){
  var str1 = 'flag1';
  changeHeightDec(inter1,timeAdd1,str1);
 }
 if(!flag2){
  var str2 = 'flag2';
  changeHeightDec(inter2,timeAdd2,str2);
 }
}
function changeHeightDec(element,timer,flag){
 var offHeight = 70;
 var inverTimer = 10;
 clearTimeout(timer);
 change();
 function change(){
  var height = parseInt(element.style.height);
  if(!height)height = 0;
  if(height > 0){
   if(height - offHeight > 0){
   element.style.height = height - offHeight +'px';
   }else{
    element.style.height = 0+'px';
   }
   if(flag === 'flag1'){
    timeDec1= setTimeout(change,inverTimer);
   }else{
    timeDec2 = setTimeout(change,inverTimer);
   }
  }
 }
}
function callBackOver(event){
 var event = untilEvent.getEvent(event);
 var target = untilEvent.getTarget(event);
 var inter1 = document.getElementById('inter1');
 var inter2 = document.getElementById('inter2');
 if(target.id == 'outerList1' || target.id == "link1"){
  var str1 = "flag1";
  changeHeight(inter1,timeDec1,str1);
 }
 if(target.id == 'outerList2' || target.id == 'link2'){
  var str2 = "flag2";
  changeHeight(inter2,timeDec2,str2);
 }
}
function changeHeight(element,timer,flag){
 var totalHeight = 160;
 var inverHeight = 10;
 var inverTimer = 10;
 clearTimeout(timer);
 //当鼠标移入时清除让内部ul长度减小的定时器,保证鼠标移入后
 //内部ul长度立即增加
 change();
 function change(){
  var height = parseInt(element.style.height);
  if(!height) height = 0;
  if(height < totalHeight){
   if(height + inverHeight > totalHeight){
    element.style.height = totalHeight + "px";
   }else{
    element.style.height = height + inverHeight +'px';
   }
   if(flag === 'flag1'){
    timeAdd1 = setTimeout(change,inverTimer);
    }else{
     timeAdd2 = setTimeout(change,inverTimer);
    }
  }
 }
}
untilEvent.addEvent(window,'load',getOuter);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • 原生js实现ajax请求和JSONP跨域请求操作示例

    原生js实现ajax请求和JSONP跨域请求操作示例

    这篇文章主要介绍了原生js实现ajax请求和JSONP跨域请求操作,结合实例形式分析了基于原生js实现的ajax请求和JSONP跨域请求相关操作技巧与使用注意事项,需要的朋友可以参考下
    2020-03-03
  • Bootstrap的popover(弹出框)在append后弹不出(失效)

    Bootstrap的popover(弹出框)在append后弹不出(失效)

    这篇文章主要介绍了Bootstrap的popover(弹出框)在append后弹不出,失效的解决方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-02-02
  • 几个高效,简洁的字符处理函数

    几个高效,简洁的字符处理函数

    几个高效,简洁的字符处理函数...
    2007-04-04
  • 微信小程序返回箭头跳转到指定页面实例解析

    微信小程序返回箭头跳转到指定页面实例解析

    这篇文章主要介绍了微信小程序返回箭头跳转到指定页面实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2019-10-10
  • js类的静态属性和实例属性的理解

    js类的静态属性和实例属性的理解

    看到论坛的帖子,然后对静态属性和实例属性有个新的认识,做下记录。
    2009-10-10
  • javascript css styleFloat和cssFloat

    javascript css styleFloat和cssFloat

    在写js操作css的过程中发现float属性在IE和firefox下对应的js脚本是不一样的,IE下对应得是 styleFloat,firefox,chorme,safari下对应的是cssFloat,可用in运算符去检测style是否包含此属性。
    2010-03-03
  • JavaScript中的类(Class)详细介绍

    JavaScript中的类(Class)详细介绍

    这篇文章主要介绍了JavaScript中的类(Class)详细介绍,本文讲解了JavaScript中类的定义、JavaScript中的Constructor(构造函数)、类名等内容,需要的朋友可以参考下
    2014-12-12
  • javascript中的this详解

    javascript中的this详解

    avaScript 中的 this 关键字,深入浅出的分析其在不同情况下的含义,形成这种情况的原因以及 Dojo 等 JavaScript 工具中提供的绑定 this 的方法。可以这样说,正确掌握了 JavaScript 中的 this 关键字,才算迈入了 JavaScript 这门语言的门槛。
    2014-12-12
  • JavaScript实现打砖块游戏

    JavaScript实现打砖块游戏

    这篇文章主要为大家详细介绍了JavaScript实现打砖块游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-02-02
  • JavaScript将当前时间转换成UTC标准时间的方法

    JavaScript将当前时间转换成UTC标准时间的方法

    这篇文章主要介绍了JavaScript将当前时间转换成UTC标准时间的方法,涉及javascript中Date及toUTCString方法的使用技巧,需要的朋友可以参考下
    2015-04-04

最新评论