目的: 實(shí)現(xiàn)下圖的效果.
效果展示
- 基本思路
左邊創(chuàng)建一個(gè)列表
右邊創(chuàng)建響應(yīng)的彈出框
鼠標(biāo)給到mouseenter事件對(duì)應(yīng)的展示右邊的彈出框
代碼如下:
HTML代碼:
<div class="section" id="news">
<!--菜單-->
<div class="menu">
<ul>
<li class="item_top" data-id="item"><a href="javascript:void(0);"><i class="icon"></i><span>行業(yè)分類</span></a></li>
<li data-id="item1"><a href="javascript:void(0);"><i class="icon"></i><span>工業(yè)機(jī)械</span></a></li>
<li data-id="item2"><a href="javascript:void(0);"><i class="icon"></i><span>洗衣機(jī)</span></a></li>
<li data-id="item3"><a href="javascript:void(0);"><i class="icon"></i><span>家用電器</span></a></li>
<li data-id="item4"><a href="javascript:void(0);"><i class="icon"></i><span>工業(yè)</span></a></li>
<li data-id="item5"><a href="javascript:void(0);"><i class="icon"></i><span>化工</span></a></li>
<li data-id="item6"><a href="javascript:void(0);"><i class="icon"></i><span>電動(dòng)車</span></a></li>
<li data-id="item7"><a href="javascript:void(0);"><i class="icon"></i><span>洗衣機(jī)</span></a></li>
<li data-id="item8"><a href="javascript:void(0);"><i class="icon"></i><span>工程</span></a></li>
<li class="item_bottom" data-id="item"><a href="javascript:void(0);"><i class="icon"></i><span>定制</span></a></li>
</ul>
<!--彈出框-->
<div class="menu_pop">
<!--排序a-z-->
<div class="industy_sort"></div>
<a href=""><i class="icon multiple"></i></a>
<!--模塊切換-->
<div class="industy_part" id="part_item1">0</div>
<div class="industy_part" id="part_item2">1</div>
<div class="industy_part" id="part_item3">2</div>
<div class="industy_part" id="part_item4">3</div>
<div class="industy_part" id="part_item5">4</div>
<div class="industy_part" id="part_item6">5</div>
<div class="industy_part" id="part_item7">6</div>
<div class="industy_part" id="part_item8">7</div>
<!--多選的時(shí)候下面的確定按鈕-->
<div class="confirm"> </div>
<!--行業(yè)專家-->
<div class="industy_people" id="people_item1">0</div>
<div class="industy_people" id="people_item2">1</div>
<div class="industy_people" id="people_item3">2</div>
<div class="industy_people" id="people_item4">3</div>
<div class="industy_people" id="people_item5">4</div>
<div class="industy_people" id="people_item6">5</div>
<div class="industy_people" id="people_item7">6</div>
<div class="industy_people" id="people_item8">7</div>
</div>
</div>
<div class="right_wrap">
</div>
</div>
CSS代碼:
#news{
background: #ffffff;
width: 100%;
max-width: 1024px;
margin: auto;
position: relative;
z-index: 2;
margin-top: -5px;
}
/*左側(cè)菜單模塊*/
#news>.menu{
width: 180px;
display: inline-block;
box-sizing: border-box;
position: relative;
box-shadow: 2px 2px 6px 2px rgb(0,0,0);
box-shadow: 2px 2px 6px 2px rgba(0,0,0,0.08);
-webkit-box-shadow: 2px 2px 6px 2px rgba(0,0,0,0.08);
}
#news>.menu>ul{
width: 100%;
}
#news>.menu>ul li{
padding-left: 14px;
line-height: 56px;
height: 56px;
}
#news>.menu>ul li.active{
background: #F03800;
}
#news>.menu>ul li.active a{
text-decoration: none;
}
#news>.menu>ul li.active a span{
color: #FFFFFF;
}
#news>.menu>ul li a{
display: block;
padding-left: 3px;
border-bottom: 1px solid #F0F0F0;
width: 100%;
}
#news>.menu>ul li a i {
display: inline-block;
width: 15px;
height: 18px;
vertical-align: middle;
margin-right: 15px;
background: red;
}
#news>.menu li.item_top{
line-height: 50px;
height: 50px;
}
#news>.msg_menu>ul li a span{
color: #666666;
font-size: 18px;
max-width: 130px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
display: inline-block;
vertical-align: middle;
}
#news>.menu>.menu_pop{
width: 850px;
height: 555px;
background: #FFFFFF;
position: absolute;
left: 181px;
top: 0px;
z-index: 5;
box-shadow: 0px 0px 12px 6px rgba(0,0,0,0.1);
overflow: hidden;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
display: none;
}
/*#news>.menu>.menu_pop.active{*/
/*display: block;*/
/*visibility: visible;*/
/*}*/
#news>.menu>.menu_pop>.industy_part,
#news>.menu>.menu_pop>.industy_people{
display: none;
}
#news>.menu>.menu_pop>.industy_part.active {
display: block;
}
#news>.menu>.menu_pop>.industy_people.active {
display: block;
}
問題1
當(dāng)我們想將鼠標(biāo)移入到右側(cè)菜單中,鼠標(biāo)必須保持在當(dāng)前的小塊中,否則就會(huì)造成右側(cè)的彈出框改變。用戶在使用的時(shí)候經(jīng)常會(huì)出現(xiàn)鼠標(biāo)不小心移入了其他的模塊,導(dǎo)致右邊展示內(nèi)容變化,得不到用戶希望的結(jié)果。
- 解決方法
我們利用setTimeout這個(gè)函數(shù)來給一個(gè)延遲,這里我給的是300毫秒。延遲判斷鼠標(biāo)的位置,這樣即便用戶移入到左側(cè)菜單的其他模塊中短時(shí)間內(nèi)也不會(huì)影響右側(cè)菜單的展示。
問題2
如果我們給到了延遲,延遲也會(huì)給我們?cè)斐梢恍﹩栴},如果用戶并不打算往右側(cè)的菜單中移入,只是想簡(jiǎn)單的切換左側(cè)菜單的選項(xiàng),我們給出延遲函數(shù)就會(huì)造成右側(cè)彈出框的延遲展示,嚴(yán)重影響用戶的體驗(yàn)。
- 解決辦法
這里我們引出了去抖技術(shù),這個(gè)也是目前電商網(wǎng)站普遍運(yùn)用的技術(shù)之一。
去抖技術(shù)
什么是去抖技術(shù)?
我個(gè)人的理解就是通過對(duì)用戶可能做的行為進(jìn)行預(yù)測(cè),合理運(yùn)用setTimeout這個(gè)函數(shù),進(jìn)而做出不同的處理,最大程度的提高用戶體驗(yàn)度。
下面我就拿我做的項(xiàng)目舉例:
下面是我們需要展示給用戶的菜單。
用戶行為判斷:
- 用戶可能要從當(dāng)前菜單移入到具體的彈出菜單。
- 用戶打算切換當(dāng)前菜單。
- 情況1分析

用戶當(dāng)前的鼠標(biāo)是A點(diǎn),如果用戶希望切換到右側(cè)菜單那么用戶鼠標(biāo)的移動(dòng)軌跡必定會(huì)經(jīng)過三角形ABC,也就是說鼠標(biāo)的下一個(gè)點(diǎn)一定會(huì)在三角形內(nèi)部。就如同上圖,我們假設(shè)下一個(gè)點(diǎn)是P點(diǎn)。那么我們就可以說只要P在三角形ABC內(nèi)部那么用戶目的就是為了切換到右側(cè)菜單。此時(shí)我們給用戶延遲,避免切換菜單時(shí)候造成問題。
- 情況2分析
如果用戶想切換左邊的菜單選項(xiàng),那么用戶鼠標(biāo)移入的方向主要是上下移動(dòng),絕對(duì)不會(huì)朝著右側(cè)移動(dòng),所以我們可以判斷如果P不在三角形ABC內(nèi)部時(shí)候,那么用戶目的是切換左側(cè)選項(xiàng)。
所以說判斷P點(diǎn)在不在三角形ABC內(nèi)部就顯得很關(guān)鍵。
我們這里用到了大學(xué)的判斷方法,即通過向量判斷,如果向量PA、向量PB的叉乘和向量PB、向量PC的叉乘以及向量PC、向量PA的叉乘最后的符號(hào)是否一致,如果符號(hào)一致那么說明P點(diǎn)在三角形內(nèi)部。
下面是具體代碼:
//向量
function vector(a,b) {
return {
x: b.x - a.x,
y: b.y - a.y
}
}
//向量的叉乘
function vectorProductor(v1,v2) {
var res = v1.x*v2.y-v2.x*v1.y;
return res;
}
//判斷P是否在三角形ABC內(nèi)部
function isPointInTrangle(p,a,b,c) {
var pa = vector(p, a);
var pb = vector(p, b);
var pc = vector(p, c);
var t1 = vectorProductor(pa, pb);
var t2 = vectorProductor(pb, pc);
var t3 = vectorProductor(pc, pa);
return sameSign(t1, t2) && sameSign(t2, t3);
}
//判斷符號(hào)是否相同
function sameSign(a,b) {
return (a ^ b) >= 0
}
最后把代碼整合一下就是最終的JS代碼
//這里運(yùn)用了debounce技術(shù)來處理菜單的移入移出問題
$(document).ready(function() {
var activeRow,
activeMenu,
activeIndusty,
timer,
mouseInSub = false,
mouseTrack = [],
moveHandler;
$(".menu_pop").mouseenter(function () {
mouseInSub = true;
}).mouseleave(function () {
mouseInSub = false;
});
moveHandler = function(e) {
mouseTrack.push({
x: e.pageX,
y: e.pageY
})
if (mouseTrack.length > 3) {
mouseTrack.shift();
}
}
$(".menu").mouseenter(function () {
$(".menu_pop").show();
$(document).bind('mousemove',moveHandler);
}).mouseleave(function () {
$(".menu_pop").hide();
if (activeRow) {
activeRow.removeClass('active');
activeRow = null;
}
if (activeMenu) {
activeMenu.hide();
activeMenu = null;
}
if (activeIndusty) {
activeIndusty.hide();
activeIndusty = null;
}
$(document).unbind('mousemove',moveHandler);//解綁
})
$(".menu ul li").mouseenter(function () {
if (!activeRow) {
activeRow = $(this).addClass("active");
activeMenu = $('#part_' + $(this).attr("data-id"));
activeIndusty = $("#people_" + $(this).attr("data-id"));
activeMenu.show();
activeIndusty.show();
return;
}
var that = $(this);
if (timer) {
clearTimeout(timer);
}
var currMousePos = mouseTrack[mouseTrack.length - 1];
var leftCorner = mouseTrack[mouseTrack.length - 2];
var delay = needDelay($(".menu_pop"),leftCorner,currMousePos);
if (delay) {
timer = setTimeout(function () {
if (mouseInSub) {
return;//如果在子菜單中立即返回
}
activeRow.removeClass('active');
activeMenu.hide();
activeIndusty.hide();
activeRow = that;
activeRow.addClass('active');
activeMenu = $('#part_' + that.attr("data-id"));
activeMenu.show();
activeIndusty = $("#people_" + that.attr("data-id"));
activeIndusty.show();
},300);
} else {
var preActiveRow = activeRow;
var preActiveMenu = activeMenu;
var preActiveIndusty = activeIndusty;
activeRow = that;
activeMenu = $('#part_' + that.attr("data-id"));
activeIndusty = $("#people_" + that.attr("data-id"));
preActiveRow.removeClass('active');
preActiveMenu.hide();
preActiveIndusty.hide();
activeRow.addClass('active');
activeMenu.show();
activeIndusty.show();
}
});
});
//向量
function vector(a,b) {
return {
x: b.x - a.x,
y: b.y - a.y
}
}
//向量的叉乘
function vectorProductor(v1,v2) {
var res = v1.x*v2.y-v2.x*v1.y;
return res;
}
function isPointInTrangle(p,a,b,c) {
var pa = vector(p, a);
var pb = vector(p, b);
var pc = vector(p, c);
var t1 = vectorProductor(pa, pb);
var t2 = vectorProductor(pb, pc);
var t3 = vectorProductor(pc, pa);
return sameSign(t1, t2) && sameSign(t2, t3);
}
//判斷符號(hào)是否相同
function sameSign(a,b) {
return (a ^ b) >= 0
}
//是否需要延遲
function needDelay(elem,leftCorner,currMousePos) {
var offset = elem.offset();
var leftCorner = leftCorner;
var currMousePos = currMousePos;
var topLeft = {
x: offset.left,
y: offset.top
}
var bottomLeft = {
x: offset.left,
y: offset.top + elem.height()
}
return isPointInTrangle(currMousePos,leftCorner,topLeft,bottomLeft);
}