原生JS實現(xiàn)導(dǎo)航下拉菜單效果
這個導(dǎo)航下拉菜單需要實現(xiàn)的功能是:下拉菜單的寬度與瀏覽器視口的寬度一樣寬;一級導(dǎo)航只有兩項,當鼠標移到一級導(dǎo)航上的導(dǎo)航項時,相應(yīng)的二級導(dǎo)航出現(xiàn)。在本案例中通過改變二級導(dǎo)航的高度來實現(xiàn)二級導(dǎo)航的顯示和消失。為了便于理解我畫了一個圖,如下:

在這個案例主要用到的知識有:設(shè)置定時器,清除定時器,mouseout和mouseover事件,另外還有css中position相關(guān)知識。本案例分為兩部分講解。第一部分html和css,第二部分js。
一. html和css
將導(dǎo)航這個導(dǎo)航條包裹在一個div中,這個div的position值為relative,高度為50px(導(dǎo)航條的高度為50px),寬度為100%,將最外層的div的position屬性設(shè)置為relative是因為二級導(dǎo)航要根據(jù)這個div來定位。這個導(dǎo)航條的結(jié)構(gòu)是二級嵌套無序列表。每一個一級導(dǎo)航項li都嵌套了它對應(yīng)的無序列表。需要將嵌套的無序列表移除文檔流。所以嵌套的無序列表的position值為absolute,top:50px(導(dǎo)航條的高度)。left:0;right:0;通過設(shè)置這些值可以使嵌套的無序列表寬度為瀏覽器視口的寬度。通過將li的display值設(shè)置inline-block并且將外層div的text-align設(shè)置為center使得導(dǎo)航項居中顯示。
注:在這個案例中一定要將嵌套的無序列表的position的值設(shè)置為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'>產(chǎn)品<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>設(shè)計周邊</strong>
</a>
</li>
</ul>
</li>
<li class='outerList' id='outerList2'><a href='#' id='link2' class='link'>服務(wù)<span></span></a>
<ul class='inter' id = 'inter2'>
<li>
<a href='#'>
<img src='img/獅子座.jpg'>
<strong>售后服務(wù)</strong>
</a>
</li>
<li>
<a href='#'>
<img src='img/蓮花禪.jpg'>
<strong>設(shè)計師</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部分涉及到的知識主要有:設(shè)置定時器,清除定時器,mouseout和mouseover事件。
mouseout事件當鼠標從一個元素上移入另一個元素的上時,會在失去鼠標的那個元素上觸發(fā)mouseout事件。獲得鼠標的那個元素可能是失去鼠標的元素的父元素或子元素,獲得鼠標的那個元素也可能位于失去鼠標元素的外部。當在一級導(dǎo)航項上觸發(fā)mouseout事件時,我們需要判斷獲得鼠標的元素是不是一級導(dǎo)航項的子孫元素。當一個元素觸發(fā)了mouseout事件時,去鼠標的元素為目標元素(target),獲得鼠標的元素為相關(guān)元素(relatedTarget)。所以需要判斷相關(guān)元素是否為一級導(dǎo)航項的子孫元素,如果是子孫元素,則相應(yīng)的導(dǎo)航項的二級導(dǎo)航項高度不變。如果不是子孫元素,則相應(yīng)的二級導(dǎo)航項消失。判斷是否為子孫元素的代碼如下:
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的值來確定是否該把二級菜單的高度變?yōu)?,如果flag1的值為false則讓outerList1對應(yīng)的二級菜單消失,如果flag2為false則將outerList2對應(yīng)的二級菜單消失。
mouseover事件當鼠標移入一個元素內(nèi)部時,獲得鼠標的元素上觸發(fā)這個事件,獲得鼠標的元素可能位于失去鼠標的外部,也可能位于失去鼠標元素的內(nèi)部。獲得鼠標的元素是目標元素,失去鼠標的元素為相關(guān)元素。在這個案例中我們只需要判斷mouseover的目標元素,但是對于mouseout事件我們需要判斷相關(guān)元素。
注:在支持DOM的瀏覽器中,mouseout和mouseover的相關(guān)元素都保存在事件對象(event)的relatedTagrget屬性中,但是在IE瀏覽器中,對于mouseout事件而言,相關(guān)事件保持在事件對象(event)的toElement屬性中,對于mouseover事件而言,相關(guān)事件保存在事件對象(event)的fromElement屬性中。
設(shè)置定時器和清除定時器在這個案例中嵌套無序列表的消失和出現(xiàn)是通過改變它的高度實現(xiàn)的,它的高度是逐漸變化,所以我使用的setTimeout這個定時器,為了能夠清除定時器還要將定時器標識保存在一個變量中。清除定時器的目的是為了防止當快速移動鼠標時嵌套無序列表的高度抖動(即:一個定時器里的回調(diào)函數(shù)讓高度增加,另一個定時器的回調(diào)函數(shù)讓高度減?。?。
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的瀏覽器將相關(guān)元素保持在relatedTarget屬性中
return event.relatedTarget;
}else if(event.toElement){
//在IE瀏覽器中mouseout事件的相關(guān)元素保存在toElement屬性中
return event.toElement;
}else if(event.fromElement){
//在IE瀏覽器中mouseover事件的相關(guān)元素保持在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事件:當鼠標從一個元素移入另一個元素時在鼠標離開的那個元素
//上觸發(fā),獲得鼠標的元素可能在失去鼠標元素的外部也可能在失去鼠標元素的
//內(nèi)部.所以需要判斷mouseout事件的相關(guān)元素是否為外部li(即id為outerList或id為outerList2)元素
//的子孫元素,如果是子孫元素,則內(nèi)部無序列表無須收起。
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);
//當鼠標移入時清除讓內(nèi)部ul長度減小的定時器,保證鼠標移入后
//內(nèi)部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);
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
js input輸入百分號保存數(shù)據(jù)庫失敗的解決方法
這篇文章主要介紹了js input輸入百分號保存數(shù)據(jù)庫失敗的解決方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-05-05
JavaScript中的finally()方法和Filter()方法詳解
finally是 JavaScript 構(gòu)造中使用的方法try-catch,Filter() 是 JavaScript 中的一種方法,可以通過處理數(shù)組輕松提供過濾后的輸出數(shù)據(jù),本文就給大家詳細的介紹一下JavaScript中的finally()方法和Filter()方法,需要的朋友可以參考下2023-08-08
基于bootstrap插件實現(xiàn)autocomplete自動完成表單
這篇文章主要介紹了基于bootstrap插件實現(xiàn)autocomplete自動完成表單的相關(guān)資料,感興趣的朋友可以參考一下2016-05-05

