RxJS Practice - Hover Dropdown Menu 2nd version
Use RxJS to create hover-dropdown-menu.
by hugogo7646
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.0.1/Rx.js"></script>
<div class="wrapper">
<div class="menu" targetDropdown="dropdown1">
Menu1
<div class="menu_child">
Menu1 Child
</div>
</div>
<div class="menu" targetDropdown="dropdown2">
Menu2
<div class="menu_child">
Menu2 Child
</div>
</div>
<div class="dropdown" id="dropdown1">
Dropdown1
<div class="dropdown_child">
Dropdown1 Child
</div>
</div>
<div class="dropdown" id="dropdown2">
Dropdown2
<div class="dropdown_child">
Dropdown2 Child
</div>
</div>
</div>
CSS
.wrapper {
width: 500px;
margin: 0 auto;
}
.menu {
background-color: yellow;
display: inline-block;
width: 45%;
margin: 0;
}
.menu_child {
background-color: #00ffd0;
width: 30%;
margin: 0 auto;
}
.dropdown {
background-color: green;
width: 80%;
height: 0px;
overflow: hidden;
position: absolute;
}
.dropdown_child {
background-color: #bc0eda;
width: 300px;
margin: 0 auto;
}
JavaScript
//取得 Menu 及 Dropdown 的 DOM
var $menu = $(".menu");
var $dropdown = $(".dropdown");
// 偵測 Menu 及 Dropdown 的 mouseenter 和 mouseleave 事件
var $menuMouseLeaveEvent = Rx.Observable.fromEvent($menu, "mouseleave");
var $dropdownMouseLeaveEvent = Rx.Observable.fromEvent($dropdown, "mouseleave");
var $menuMouseEnterEvent = Rx.Observable.fromEvent($menu, "mouseenter");
var $dropdownMouseEnterEvent = Rx.Observable.fromEvent($dropdown, "mouseenter");
//將 Menu 的 mouseenter 和 mouseleave 事件轉成對應的資訊物件
//(這裡儲存了滑鼠有無在 Menu 上面、滑鼠滑進 (或滑出) 了哪個 Menu DOM 的資訊),
//並將 mouseenter 和 mouseleave 事件 merge 起來,只要看最後的狀態就好
var isMouseOnMenuEvent = Rx.Observable.merge(
$menuMouseEnterEvent.map(function(e) {
return {
isHovered : true,
hoveredDom : e.currentTarget
};
}),
$menuMouseLeaveEvent.map(function(e) {
return {
isHovered : false,
hoveredDom : e.currentTarget
};
}));
//將 Dropdown 的 mouseenter 和 mouseleave 事件轉成對應的資訊物件
//(這裡儲存了滑鼠有無在 Dropdown 上面、滑鼠滑進 (或滑出) 了哪個 Dropdown DOM 的資訊)
//並將 mouseenter 和 mouseleave 事件 merge 起來,只要看最後的狀態就好
var isMouseOnDropDownEvent = Rx.Observable.merge(
$dropdownMouseEnterEvent.map(function() {
return {
isHovered : true
};
}),
$dropdownMouseLeaveEvent.map(function() {
return {
isHovered :false
};
})).startWith({
// 因為 RxJS 的 combineLatest() 不能 combine NULL 的事件 (會出錯),
// 而一開始 Dropdown 沒有被打開時,不會有 Dropdown 的 mouseenter 和 mouseleave 事件,
// 所以在這邊用 startWith 來手動增加一個 event 在最前面
isHovered :false
});
// 將 isMouseOnMenuEvent 和 isMouseOnDropDownEvent 兩個事件流 Combine 在一起,
// 只看各自的最後一個狀態。
// 因為可能存在在短時間中,事件還沒有被觸發的情況,在這之中的狀態會不準而不對,
// 例如:
// 滑鼠滑出了 Menu,要準備滑進 Dropdown 但還沒滑進的這一段時間中,
// 此時 isOnMenu = false,但 isDropdown 也等於 false,這時用這組狀態判斷 Dropdown 的開關就會不正確,
// 所以在這使用了 debounceTime() 來延長事件偵測的時間,例如滑鼠移出 Menu 後,等 100 毫秒看有沒有其他事件再決定送出事件
var mouseHoverStatus = Rx.Observable.combineLatest(isMouseOnMenuEvent, isMouseOnDropDownEvent)
.debounceTime(100);
// 在註冊事件 (subscribe)...