前言
事情要从我写的练手小案例开始说起——案例是一个仿小米官网的二级联动分类菜单栏+轮播图。使用了less预编译和vue框架。效果图如下:
预期效果应该是:当鼠标悬浮在左边分类菜单栏时会对应显示二级菜单栏,鼠标移动到二级菜单栏时可以点击对应的连接进行跳转。
但是实际上,当我的鼠标从悬浮在菜单栏上,移动到二级菜单栏上时,二级菜单栏会从显示变成隐藏。根据js代码的逻辑,应该是不会隐藏才对的,由此我开始了找bug之路。
html结构+css样式
分类菜单栏和二级菜单栏的html结构如下:
<!-- 分类菜单 -->
<div class="menu-content" @mouseout="hideSubMenu">
<div class="menu-item" v-for="(item, index) in menu_list" @mouseover="showSubMenu(index)">
<a href="">
<span>{{ item.title }}</span>
<i class="fa fa-angle-right"></i>
</a>
</div>
</div>
<!-- 二级菜单 -->
<div class="sub-menu" v-show="subMenuShow" @mouseover="showSubMenu" @mouseout="hideSubMenu">
<div class="inner-box" v-for="(item, index) in menu_list">
<div class="sub-inner-box" v-show="index === menu_item_index">
<div class="title">{{ item.title }}</div>
<div class="sub-row" v-for="(item1) in item.list">
<span class="bold">{{ item1.menu_title}}:</span>
<a href="" class="menu" v-for="(item2) in item1.menus">{{ item2 }}</a>
</div>
</div>
</div>
</div>
其中:
subMenuShow是布尔值类型的,用于控制二级菜单的显示和隐藏;showSubMenu是方法,用于修改subMenuShow值为true,使二级菜单显示;hideSubMenu是方法,用于修改subMenuShow值为false,使二级菜隐藏;menu_item_index是当前所展示的二级菜单类别的索引值menu_list是二级菜单中的菜单内容
less样式编写如下:
// 分类菜单
.menu-content {
position: relative;
width: 244px;
height: 100%;
background-color: rgba(7,17,27,0.25);
padding: 6px 0px 0;
box-sizing: border-box;
.menu-item {
width: 100%;
height: 64px;
padding:0 26px;
box-sizing: border-box;
a {
position: relative;
display: flex;
align-items: center;
width: 100%;
height: 100%;
color: #fff;
padding: 0 5px;
border-bottom: 1px solid rgba(255,255,255,0.2);
box-sizing: border-box;
i {
position: absolute;
right: 18px;
}
}
}
.menu-item:last-child a {
border: none;
}
}
查找问题
内外边距
经过初步调试,发现当鼠标移动到下图方框内的位置时会隐藏
而这个位置刚好是menu-item所设置的内边距,于是开始思考是不是内外边距不能触发mouseover,经过查找资料及验证:
验证外边距margin:给该盒子设置20px的外边距、0px的内边距和mouseover事件,当鼠标从外面进入到margin区域时,不会触发mouseover事件,只有进入到盒子内部(背景色为粉色)才触发mouseover事件。
验证内边距padding:给该盒子设置20px的内边距、0px的外边距和mouseover事件,当鼠标从外面进入刚进入到粉色区域时,就触发mouseover事件。
由此得出结论:padding可以触发mouseover事件,margin事件不可以。
但是由于我做css布局时使用的就是padding内边距,所以按道理来说是可以正常触发mouseover事件的,所以可以得知并不是内外边距的问题。
定位层级提升
根据控制台的输出情况,可以发现每次移动到方框位置时,就会调用hideSubMenu方法(该方法的作用是隐藏二级菜单)。而绑定了该方法的地方只有分类菜单中的最外层类名为menu-content的元素和二级菜单中的最外层类名为sub-menu的元素(若忘记可到上面html代码中查看)。
在hideSubMenu方法中打印触发该事件的目标元素可以发现输出的是a元素
由此可以得知是由a元素触发的mouseout事件,根据事件冒泡机制,触发了绑定在类名为menu-content元素上的mouseout事件监听器,从而调用了隐藏二级菜单的方法hideSubMenu。
而根据控制台中的选择指定元素进行检查发现,当鼠标放在a标签外、menu-item上(左图中黑色圆点处)时,所指向的元素并不是我们所期待的menu-item(右图),而是最外层的banner。
原来这是因为在我编写的css样式中,给banner和a设置了绝对定位,但没给menu-item设置定位。导致banner的层级提升覆盖了menu-item,而a和banner在同一层级,但a在banner后,所以a居上。
因此当鼠标从a移动到menu-item上时,鼠标所在的层级是banner的那个层级,而不是menu-item的那个层级。所以鼠标在离开a标签时触发mouseout事件调用hideSubMenu方法,二级菜单被隐藏。鼠标进入menu-item时触发的是banner的mouseover事件,而banner并未绑定mouseover事件的处理函数,所以不调用回调函数。
解决方法
给menu-item或它外层的menu-content设置定位:position: relative使得层级得到提升。
层级提升后,鼠标移入移出menu-item时,就能正常触发mouseover和mouseout事件——在鼠标移出a时触发mouseout事件隐藏二级菜单,移入menu-item时触发mouseover事件显示二级菜单,以此达到一直显示二级菜单的效果。
注:案例来自(xhslink.com/UYC8jh)