使用 react-menu-aim 打造更友好的多级菜单

查看源码

写过多级菜单的同学应该都知道当年亚马逊的黑科技:鼠标从一级菜单滑向二级菜单时,如果中间经过了另一个一级菜单,并不会马上切换。这也避免了用户想看二级菜单的时候,必须先精准的横向移动到对应二级菜单的不便。

详细的原理在这篇博文 里都有解释,本文不再赘述。简单地说,就是在一级菜单项 mouseenter 的时候,根据鼠标之前的移动坐标,判断用户当前是否是想移动到二级菜单。若是,则 setTimeout 一段时间,再重新判断;若不是,则直接激活当前一级菜单项。

一图胜千言:

react-menu-aim

Demo

目前已经有人根据这个原理写了一个 jQuery 插件 jQuery-menu-aim,而我由于在最近的 React 项目中需要使用这个功能,就顺手写了一个 React 版本。

使用的方法也很简单,引入 react-menu-aim 作为一个 mixin,然后在 componentWillMount 的时候配置一下,最后在 render 的时候调用 mixin 提供的一些事件响应方法即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
var React = require('react');
var ReactMenuAim = require('react-menu-aim');

var Menu = React.createClass({
// 使用 ReactMenuAim mixin
mixins: [ReactMenuAim],

componentWillMount: function() {
// 在这里配置 ReactMenuAim
this.initMenuAim({
submenuDirection: 'right',
menuSelector: '.menu'
});
},

// 由于 ReactMenuAim 需要响应组件的 mouseenter 等事件,所以若你也需要
// 响应这些事件,可以把自己的 event handler 作为参数传给 ReactMenuAim
// 提供的 event handler。
//
// 例如下面就是组件自己的 event handler
handleSwitchMenuIndex: function(index) {
// ...
},


// this.handleMouseLeaveMenu 和 this.handleMouseEnterRow 是
// ReactMenuAim 提供的方法,让它们分别响应鼠标离开菜单和鼠标进入每一个
// 一级菜单选项的事件。
//
// 若 ReactMenuAim 判定应该激活当前 mouseenter 的一级菜单时,会调用
// 传入的回调。在这个例子里,就是我们自己的 this.handleSwitchMenuIndex 方法
render: function() {
return (
<div className="menu-container">
<ul className="menu" onMouseLeave={this.handleMouseLeaveMenu}>
<li className="menu-item" onMouseEnter={this.handleMouseEnterRow.bind(this, 0, this.handleSwitchMenuIndex)}>Menu Item 1</li>
<li className="menu-item" onMouseEnter={this.handleMouseEnterRow.bind(this, 1, this.handleSwitchMenuIndex)}>Menu Item 1</li>
</ul>
</div>
);
}
});

更多的配置项见 github repo