1、实现分类导航菜单的基本布局结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="jquery-3.3.1.js"></script>
<style>
.wrap{
position: relative;/*为了使菜单是相对于这个div容器定位的而不是相对于body*/
width: 200px;
left: 50px;
top:50px;
}
ul{
padding: 15px 0;
margin:9;
list-style: none;
background-color: #CC33FF;
color: #FFFFFF;
border-right-width: 0;
}
li{
display: block;
height: 30px;
line-height: 30px;/*实现垂直居中的一种方法 行高和高度相等*/
padding-left: 12px;
cursor: pointer;/*光标呈现为指示链接的指针(一只手)*/
font-size: 14px;
position: relative;
}
/*当样式的切换需要用到Js来配合的时候就添加类,因为Js可以很方便的添加或移动类,如果不需要的话就用Hover来控制*/
li.active{
background-color: #FFFF66;
}
li span:hover{
color: coral;
}
.none{
display: none;
}
#sub{
width: 600px;
position: absolute;
border: 1px solid #f7f7f7;
background: #f7f7f7;
box-shadow:2px 0 5px #99CC33;/*加阴影*/
left:200px;
top: 0;
box-sizing: border-box;/*您需要并排放置两个带边框的框,可通过将 box-sizing 设置为 "border-box"。这可令浏览器呈现出带有指定宽度和高度的框,并把边框和内边距放入框中。盒模型采用Border-box*/
margin: 0;
padding: 10px;
}
.sub_content a{
font-size: 12px;
color: #0000CC;
text-decoration: none;/*去掉下划线*/
border-left: 1px solid #99CC33;
padding: 0 10px;
margin: 4px 0;
}
.sub_content dl{
/*因为DL中间会有浮动元素,所以加入overflow:hidden来触发bfc令他可以被浮动元素撑开而不是失去自己的高度*/
overflow: hidden;
}
.sub_content dt{
float: left;/*水平排列*/
width: 70px;
font-weight: bold;
clear: left;
position: relative;
}
.sub_content dd{
float: left;
margin-left: 5px;
border-top: 1px solid #eee;
margin-bottom: 5px;
}
.sub_content dt i{
width: 4px;
height: 14px;
font: 400 9px /14px consolas;/*分号前面代表font-size 分好后面代表line-height*/
position: absolute;
right: 5px;
top:5px;
}
</style>
</head>
<body>
<!--承载菜单-->
<div class="wrap">
<!--用无序列表实现一级菜单-->
<ul>
<!--加入id用来与二级菜单进行区分-->
<li data-id="a">
<span>一</span>
</li>
<li data-id="b">
<span>二 / 三 / 四</span>
</li>
<li data-id="c">
<span>五 / 六 / 七</span>
</li>
<li data-id="d">
<span>八 / 九 / 十</span>
</li>
<li data-id="e">
<span>十一 / 十二 / 十三</span>
</li>
<li data-id="f">
<span>十四 / 十五 / 十六</span>
</li>
<li data-id="g">
<span>十七 / 十八 / 十九</span>
</li>
<li data-id="h">
<span>二十 / 二一 / 二二</span>
</li>
</ul>
<div id="sub" class="">
<!--二级菜单的id与一级菜单的id一一对应 方便用DataSet来控制-->
<div id="a" class="sub_content ">
<dl>
<!--在HTML里面不能直接写箭头,因为会跟元素标签的箭头混了,用这种方式<i>></i>-->
<dt><!--标题-->
<a href="#">1<i>></i></a>
</dt>
<dd><!--子内容-->
<a href="#">1.1</a>
<a href="#">1.2</a>
<a href="#">1.3</a>
</dd>
</dl>
<dl>
<!--在HTML里面不能直接写箭头,因为会跟元素标签的箭头混了,用这种方式<i>></i>-->
<dt><!--标题-->
<a href="#">2<i>></i></a>
</dt>
<dd><!--子内容-->
<a href="#">2.1</a>
<a href="#">2.2</a>
<a href="#">2.3</a>
</dd>
</dl>
<dl>
<!--在HTML里面不能直接写箭头,因为会跟元素标签的箭头混了,用这种方式<i>></i>-->
<dt><!--标题-->
<a href="#">3<i>></i></a>
</dt>
<dd><!--子内容-->
<a href="#">3.1</a>
<a href="#">3.2</a>
<a href="#">3.3</a>
</dd>
</dl>
<dl>
<!--在HTML里面不能直接写箭头,因为会跟元素标签的箭头混了,用这种方式<i>></i>-->
<dt><!--标题-->
<a href="#">4<i>></i></a>
</dt>
<dd><!--子内容-->
<a href="#">4.1</a>
<a href="#">4.2</a>
<a href="#">4.3</a>
</dd>
</dl>
<dl>
<!--在HTML里面不能直接写箭头,因为会跟元素标签的箭头混了,用这种方式<i>></i>-->
<dt><!--标题-->
<a href="#">5<i>></i></a>
</dt>
<dd><!--子内容-->
<a href="#">5.1</a>
<a href="#">5.2</a>
<a href="#">5.3</a>
</dd>
</dl>
<dl>
<!--在HTML里面不能直接写箭头,因为会跟元素标签的箭头混了,用这种方式<i>></i>-->
<dt><!--标题-->
<a href="#">6<i>></i></a>
</dt>
<dd><!--子内容-->
<a href="#">6.1</a>
<a href="#">6.2</a>
<a href="#">6.3</a>
</dd>
</dl>
<dl>
<!--在HTML里面不能直接写箭头,因为会跟元素标签的箭头混了,用这种方式<i>></i>-->
<dt><!--标题-->
<a href="#">7<i>></i></a>
</dt>
<dd><!--子内容-->
<a href="#">7.1</a>
<a href="#">7.2</a>
<a href="#">7.3</a>
</dd>
</dl>
<dl>
<!--在HTML里面不能直接写箭头,因为会跟元素标签的箭头混了,用这种方式<i>></i>-->
<dt><!--标题-->
<a href="#">8<i>></i></a>
</dt>
<dd><!--子内容-->
<a href="#">8.1</a>
<a href="#">8.2</a>
<a href="#">8.3</a>
</dd>
</dl>
</div>
</div>
</div>
</body>
</html>
2、开发普通二级菜单
2.1、事件代理方式进行绑定
2.2、mouseenter和mouseover的区别
使用mouseover/mouseout时,如果鼠标移动到子元素上,即便没有离开父元素,也会触发父元素的mouseout事件;
使用mouseenter/mouseleave时,如果鼠标没有离开父元素,在其子元素上任意移动,也不会触发mouseleave事件
$(document).ready(function(){
var sub = $("#sub");
var activeRow;
var activeMenu;
$("#test").on('mouseenter',function (e) {
sub.removeClass('none')
}).on('mouseleave',function(e){
sub.addClass('none');
/*当鼠标离开一级菜单容器的时候,如果存在激活的行,要把样式去掉*/
if(activeRow){
activeRow.removeClass('active')
activeRow=null;
}
if(activeMenu){
activeMenu.addClass('none')
activeMenu = null;
}
})
//对一级菜单中的每一个列表项绑定事件,采用事件代理的方式进行绑定 并不是利用循环
.on('mouseenter','li',function (e) {
//当前移动过去的时候并没有激活的列表项
if(!activeRow){
activeRow=$(e.target).addClass('active')
activeMenu = $('#'+ activeRow.data('id'))
activeMenu.removeClass('none')
return;
}
activeRow.removeClass()
})
});
利用延迟和去抖技术进行优化
1、切换子菜单的时候用SetTimeout设置延迟
2、debounce(js里面常用)去抖技术,在事件被频繁触发时,只进行一次处理
if (timer){
clearTimeout(timer)
}
timer = setTimeout(function(){
if (mouseInSub){
return
}
activeRow.removeClass('active');
activeMenu.addClass('none');
activeRow = $(e.target);
activeRow.addClass('active');
activeMenu = $('#'+activeRow.data('id'))
activeMenu.removeClass('none')
timer=null
},300)
3、基于用户行为预测的切换技术
新增function.js
function sameSign(a,b) {
//二进制的政府表示在最高位,1标识负,0标识整 儿异或运算只有两个都为0时才是0
return (a ^ b) >= 0
}
function vector(a,b) {
return{
x:b.x - a.x,
y:b.y - a.y
}
}
function vectorProduct(v1,v2) {
return v1.x * v2.y - v2.x * v1.y;
}
//叉乘公式的判断方法
function isPointinTrangle(p,a,b,c) {
var pa = vector(p,a)
var pb = vector(p,b)
var pc = vector(p,c)
var t1 = vectorProduct(pa,pb)
var t2 = vectorProduct(pb,pc)
var t3 = vectorProduct(pa,pc)
//使用位运算的技巧来判断是否符号一致
return sameSign(t1,t2) && sameSign(t2,t3)
}
//写一个函数来判断是否需要延迟
function needDelay(elem,leftCorner,currMousrPos) {
//利用jQuery的offset方法获取鼠标所在位置的上下边缘坐标
var offset =elem.offset();
var topLeft = {
x:offset.left,
y:offset.top
}
var bottomLeft = {
x:offset.left,
y:offset.top + elem.height()
}
return isPointinTrangle(currMousrPos,leftCorner,topLeft,bottomLeft)
}
TabTest.js修改
var currMousePos = mouseTrack[mouseTrack.length -1]
var leftCorner = mouseTrack[mouseTrack.length -2]
var delay = needDelay(sub,leftCorner,currMousePos)
//如果在三角形内
if (delay){
timer = setTimeout(function(){
if (mouseInSub){
return
}
activeRow.removeClass('active');
activeMenu.addClass('none');
activeRow = $(e.target);
activeRow.addClass('active');
activeMenu = $('#'+activeRow.data('id'))
activeMenu.removeClass('none')
timer=null
},300)
}else{
//如果不在三角形内直接进行菜单的切换
var preveActiveRow = activeRow;
var preveActiveMenu = activeMenu;
activeRow = $(e.target)
activeMenu = $('#'+activeRow.data('id'))
preveActiveRow.removeClass('active');
preveActiveMenu.addClass('none');
activeRow.addClass('active')
activeMenu.removeClass('none');
}