用原生JS實現的一個導航下拉菜單,下拉菜單的寬度與瀏覽器視口的寬度一樣(js+html+css)


這個導航下拉菜單需要實現的功能是:下拉菜單的寬度與瀏覽器視口的寬度一樣寬;一級導航只有兩項,當鼠標移到一級導航上的導航項時,相應的二級導航出現。在本案例中通過改變二級導航的高度來實現二級導航的顯示和消失。為了便於理解我畫了一個圖,如下:

在這個案例主要用到的知識有:設置定時器,清除定時器,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">
</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);

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM