作为俺网页设计课的某个实验作业,觉得老师提供的案例代码过于简陋,于是自己琢磨了个优雅又精美的下拉菜单功能。

效果

menu

       菜单点击触发,点击事件实现收起展开菜单,点击菜单项收起其他菜单项的一打开菜单,每项自带的after伪元素实现的三角提示标,并随收起展开状态动态变换效果。。。做完直接对👴🏿的聪明才智佩服的五体投地。

       代码实现也简洁,层次属实清晰,可谓惊天地泣鬼神集代码带成者😅

代码实现

      下拉菜单功能的中心思想是通过一个类表示按下菜单后的样式,本例取active类。

      设置active类的元素即样式表示为菜单选中状态。无active类下,子级菜单为隐藏状态,即透明度为0,且关闭鼠标事件,三角图形向下指示展开。设置active类下,子菜单显示,透明度为1,启用鼠标事件,三角图形向上指示收起。

      为了实现鼠标事件,单击元素为其设置active类,动态切换分配active类,则需用javascript配置交互。

HTML

      作为数量众多的菜单项与相应的子级菜单元素,需要将菜单划分为主级与子级两层,且需要将属于同菜单块的菜单项存放在无序列表中,故基础HTML架构应有如下排布:

<ul id="mainMenu">
                    <li>
                        <a href="#" role="button" id="mainBtn">Xbox Game Pass</a>
                        <ul id="subMenu">
                            <li><a href="#">概观</a></li>
                            <li><a href="#">浏览游戏</a></li>
                            <li><a href="#">Xbox Game Pass for PC</a></li>
                            <li><a href="#">Xbox Live Gold</a></li>
                        </ul>
                    </li>
                    <li>
                        <a href="#" role="button" id="mainBtn">游戏</a>
                        <ul id="subMenu">
                            <li><a href="#">选购所有主机游戏</a></li>
                            <li><a href="#">使用Xbox的电脑游戏</a></li>
                            <li><a href="#">所有游戏展示</a></li>
                        </ul>
                    </li>
                    <li>
                        <a href="#" role="button" id="mainBtn">设备</a>
                        <ul id="subMenu">
                            <li>
                                <p>所有Xbox主机</p>
                            </li>
                            <li><a href="#">全新 Xbox Series X</a></li>
                            <li><a href="#">全新 Xbox Series S</a></li>
                            <hr>
                            <li>
                                <p>所有Xbox配件</p>
                            </li>
                            <li><a href="#">控制器</a></li>
                            <li><a href="#">头戴式设备</a></li>
                            <li><a href="#">硬盘与存储空间</a></li>
                        </ul>
                    </li>
                    <li>
                        <a href="#" role="button" id="mainBtn">社群</a>
                        <ul id="subMenu">
                            <li><a href="#">Xbox Community</a></li>
                            <li><a href="#">多人游戏</a></li>
                        </ul>
                    </li>
                    <li>
                        <a href="#" role="button" id="mainBtn">支持</a>
                        <ul id="subMenu">
                            <li><a href="#">支持首页</a></li>
                            <li><a href="#">Xbox Live 状态</a></li>
                            <li><a href="#">无障碍游戏</a></li>
                            <li><a href="#">支持服务的新功能</a></li>
                        </ul>
                    </li>
                </ul>

       其中,mainMenu用于标识主菜单类,subMenu用于标识二级菜单列表,mainBtn标识主菜单项,即文字背后需要添加三角形指示图形的元素。

      接下来开始对元素具体装饰

CSS

       首先对主菜单类容器指定项目的行内弹性盒子排布,使其菜单项横向排列:

#mainMenu{
    display:inline-flex;
}

       子菜单与主菜单同样是以无序列表存放,对于子菜单,需要将子菜单块准确置于对应主菜单项的底下,故需先将子菜单项设置绝对定位,再给子菜单的父li类设置相对定位,以标识参考系。配置了必要及其他装饰属性代码应如下:

#mainMenu li{
    position: relative;
}
#subMenu{
    opacity: 0;
    position: absolute;
    top:  100%;
    left: 0;
    min-width: 100%;
    background-color: white;
    border: 1px solid lightgray;
    pointer-events: none;
    border-radius: .366em;
    -webkit-border-radius: .366em;
    -moz-border-radius: .366em;
    -ms-border-radius: .366em;
    -o-border-radius: .366em;
    transition: all 0.2s;
    -webkit-transition: all 0.2s;
    -moz-transition: all 0.2s;
    -ms-transition: all 0.2s;
    -o-transition: all 0.2s;
}

       其他对a标签的按钮样式及hover伪元素样式设置就不作过多赘述,接下来实现主菜单项的指示三角形,这里使用after伪元素样式。

  • ::after对具有行内属性的标签的作用相当于加在文字尾部的样式,必须指定content属性,否则无法显示。
  • 实现三角形形状,需要灵活使用border属性,对尖头方向的border设置为none

配置了基本样式及微调下的::after应如下:

#mainBtn::after{
    display: inline-block;
    margin-left: .255rem;
    vertical-align: .255rem;
    content: '';
    border-top: .3em solid;
    border-left:.3em solid transparent;
    border-right: .3em solid transparent;
    border-bottom: none;
    transition: all 0.2s;
    -webkit-transition: all 0.2s;
    -moz-transition: all 0.2s;
    -ms-transition: all 0.2s;
    -o-transition: all 0.2s;
}

       接下来应对active类进行样式设置;

       对于active类下的子菜单块,设置显示等属性:

.active #subMenu{
    opacity: 1;
    top: 120%;
    pointer-events: all;
}

对三角图形伪元素类,设置为向上指示,可重新配置border实现尖头向上,本例则为了方便且具有动态效果,利用了transform旋转:

.active #mainBtn::after{
    transform-origin: center;
    transform: rotateZ(180deg);
    -webkit-transform: rotateZ(180deg);
    -moz-transform: rotateZ(180deg);
    -ms-transform: rotateZ(180deg);
    -o-transform: rotateZ(180deg);
}

       核心CSS设置完成,为实现active类的动态分配,需编写javascript。

Javascript

       为实现active类的统一控制,将该类分配给主菜单块的li标签;

       编写自定义函数实现类切换功能:

const mainBtn = document.querySelectorAll('#mainMenu #mainBtn');
    //active类切换器
    function activeToggler(index) {
        for (let j = 0; j < mainBtn.length; j++) {
            const activeList = mainBtn[j].parentElement;
            if (j != index) {
                activeList.classList.remove('active');      //移除索引外的active
            }
        }
        mainBtn[index].parentElement.classList.toggle('active');

        activeToggler函数带有1个index参数,表示mainBtn类数组的序号,以该序号作为索引,for循环遍历所有mainBtn类,按照传入的index,移除除index索引元素外的active类。由于active需加在li上,故需选择mainBtn的父元素,即父li。

        toggle方法能实现灵活执行add与remove方法,具有指定类则remove,没有指定类则add这个类。

        为每个mainBtn绑定点击事件,实现点击按钮即为其父li配置active类:

//绑定点击事件
    for (let i = 0; i < mainBtn.length; i++) {
        const btn = mainBtn[i];
        btn.onclick = function () {
            activeToggler(i);
        }
    }

        为养成好的习惯,同学们需要将这些交互功能代码放置在页面加载对象window.onload中,故完整js代码应如图:

window.onload = function () {
    const mainBtn = document.querySelectorAll('#mainMenu #mainBtn');

    //绑定点击事件
    for (let i = 0; i < mainBtn.length; i++) {
        const btn = mainBtn[i];
        btn.onclick = function () {
            activeToggler(i);
        }
    }

    //active类切换器
    function activeToggler(index) {
        for (let j = 0; j < mainBtn.length; j++) {
            const activeList = mainBtn[j].parentElement;
            if (j != index) {
                activeList.classList.remove('active');      //移除索引外的active
            }
        }
        mainBtn[index].parentElement.classList.toggle('active');
    }
}

结语

        下拉菜单实现其实用不了多少js代码,如果单纯使子菜单在hover时而不是鼠标单击时显示的话,甚至可以直接用纯CSS实现,无需半句javascript。

        单击显示也无非是利用了css设置active类样式,js动态控制active这一基本思想,虽说在带佬们眼中,这种小技巧实属常识中的常识,但对初学者来说还是受益匪浅滴。

        怎么样铁汁们,学废了🐴,另外现在评论区可以发emoji了嗷,蛆宝宝们走起来😅