之前做项目时经常用到一种触点式弹出菜单或者导航的功能,这个功能的主要应用场景是:web页面中多层分级导航或者子功能目录,但又考虑到页面区域有限,于是就考虑到在鼠标移动到某导航按钮上或者点击时,系统将在该按钮下浮动一个层,展现子导航或者子目录,当用户选择这些子目录/导航时,页面作出相应的操作,这种功能从人机交互和页面布局上都有很好的人性化体验。最近有些闲暇时间,将此功能重新整理并封装,形成一个公共插件,命名为:SelectItem.js,分享如下:
插件的主要实现的功能:
1. 功能模块化封装,提供一个对外的初始化接口供应用调用即可,首先需要绑定一个页面触发元素(链接/按钮等),传入元素id;
2. 提供子菜单/导航条目的初始化入口,调用方法输入需要初始化的条目数组,以参数方式输入;
3. 提供对外事件接口,如点击子菜单/导航的事件定义等,供调用方自定义事件实现;
4. 提供辅助添加功能,如果子菜单是选择信息,而初始化的条目又不符合调用需求,则需提供让给调用方自定义条目的功能,此辅助功能可以根据需要进行屏蔽;
效果图如下:(左侧为带自定义辅助功能的效果,右侧为不带自定义辅助功能的效果)
上述效果调用时的代码如下:
1 AutoDiv("btn", false).InitControl([
2 {
3 key: "12",
4 value: "二个选项(A | B)"
5 },
6 {
7 key: "13",
8 value: "三个选项(A | B | C)"
9 },
10 {
11 key: "14",
12 value: "四个选项(A | B | C | D)"
13 },
14 {
15 key: "15",
16 value: "五个选项(A | B | C | D | E)"
17 },
18 {
19 key: "0",
20 value: "自定义选项类型"
21 }
22 ],
23 function(value) {
24 //处理选中类型
25 alert(value);
26 },
27 function() {
28 //打开自定义选项创建窗口
29 alert("new dialog.");
30 });
31 }
可以看出,在页面中引用该js文件,然后对需要触点弹出的页面元素进行插件绑定即可,其中绑定函数AutoDiv("btn", false)有两个参数,第一个参数是需要绑定的页面元素id,第二个参数是个布尔值,需要自定义辅助功能时传递true,否则传递false。绑定好页面元素后,接下来是链式调用过程,函数InitControl有三个参数,第一个参数是需要展现的菜单项,它是一个键值对数组(key为选择项的反馈表示,value为菜单项显示信息);第二个参数是菜单项选择点击事件的实现,由调用方定义实现,函数有一个参数,即所选菜单项的key值;第三个参数是自定义辅助事件的实现,同样由调用方定义实现。
控件源码如下:
1 /*
2 * write by zhangyu,2013-07-09,v1.0
3 *
4 * 实现触点式菜单弹出及选择功能
5 *
6 */
7
8 function AutoDiv(id, allowNewFlag) {
9 //预留退路
10 if (!document.getElementById) return false;
11 if (!document.createElement) return false;
12
13 //保存当前上下文
14 var me = this;
15
16 me.targetControlID; //定义当前绑定的id及其对象
17 me.targetObj = null;
18 me.pupDivObj = null; //定义弹出浮动层对象
19 me.callBackFun = null; //选中事件
20 me.callBackNewFun = null; //自定义回调事件
21 me.isFocusIn = false; //标记浮动层是不是处于鼠标焦点内
22 me.allowNewFlag = false; //标记是否显示自定义选项类型选项
23 me.currentIndex = -1; //当前选中的结果索引
24 me.LastIndex = -1; //上一条选中的结果索引
25
26 me.Result; //最后结果‘13’、‘14’、‘15’、‘21’、‘31’
27
28 if (id != null && typeof (id) != undefined && id != "") {
29 if (!document.getElementById(id)) return false;
30 //赋值类的成员
31 me.targetControlID = id;
32 me.targetObj = document.getElementById(id);
33 }
34
35 if (allowNewFlag != null && typeof (allowNewFlag) != undefined && allowNewFlag != "") {
36
37 me.allowNewFlag = allowNewFlag;
38 }
39
40 if (me.pupDivObj == null || typeof (me.pupDivObj) == undefined) {
41
42 me.pupDivObj = document.createElement("div");
43 me.pupDivObj.setAttribute("class", "pupDiv");
44 //加载新建的div对象到文档中去
45 var parent = document.getElementById(me.targetControlID).parentNode;
46 if (parent != null && typeof (parent) != undefined) {
47 parent.appendChild(me.pupDivObj);
48 }
49 }
50 //浮动层弹出展现事件
51 me.pubDiv = function() {
52 me.pupDivObj.style.display = "block";
53 me.pupDivObj.style.backgroundColor = "#ffffff";
54 me.pupDivObj.style.position = "absolute";
55 me.pupDivObj.style.top = me.targetObj.getBoundingClientRect().top + 20;
56 me.pupDivObj.style.left = me.targetObj.getBoundingClientRect().left;
57 me.pupDivObj.style.width = "150";
58 //绑定内容
59 me.pupDivObj.innerHTML = me.GetContentTable();
60 //定义行事件
61 var result = me.pupDivObj.getElementsByTagName("td");
62 if (result.length > 0) {
63 //给每个Td动态绑定事件(新建了一个闭包,用来传递参数,否则所有元素的一下三个鼠标事件的参数i会是相同的值)
64 for (var i = 0; i < result.length; i++) {
65 var bb = new BiBaoOnMouseResult(i, me);
66 AddEvent(result[i], "mouseover", bb.OnMouseOverEx);
67 AddEvent(result[i], "mouseout", bb.OnMouseOutEx);
68 AddEvent(result[i], "click", bb.OnMouseClickEx);
69 }
70 }
71 }
72 //类型选项绑定函数
73 me.GetContentTable = function() {
74 var DivContent = "<table cellSpacing='0' cellPadding='1' align='center' border='0' id='DivContent_Table'>";
75 for (var i = 0; i < me.itemList.length - 1; i++) {
76 DivContent += " <tr ReturnValue='" + me.itemList[i].key + "' name='row'>"
77 + " <td height='15' nowrap>"
78 + " <div style='width:" + (me.pupDivObj.offsetWidth - 12) + ";' class='listOption' title='" + me.FliterString(me.itemList[i].value) + "'>" + me.FliterString(me.itemList[i].value) + "<div>"
79 + " </td>"
80 + " </tr>";
81 }
82 //添加自定义选项
83 if (me.allowNewFlag) {
84 DivContent += " <tr ReturnValue='" + me.itemList[me.itemList.length - 1].key + "' name='line'>"
85 + " <td nowrap>"
86 + " <div style='font-size:1px; border-top:solid 1px #dde0e0; height:3px;'></div>"
87 + " </td>"
88 + " </tr>"
89 + " <tr ReturnValue='" + me.itemList[me.itemList.length - 1].key + "' name='row'>"
90 + " <td height='15' nowrap>"
91 + " <div style='width:" + (me.pupDivObj.offsetWidth - 12) + ";' class='listOption' title='" + me.FliterString(me.itemList[me.itemList.length - 1].value) + "'>" + me.FliterString(me.itemList[me.itemList.length - 1].value) + "<div>"
92 + " </td>"
93 + " </tr>";
94 }
95 DivContent += "</table>";
96 return DivContent;
97 }
98 //定义过滤函数
99 me.FliterString = function(orgString) {
100 orgString = orgString.replace(new RegExp("\"", "gi"), """);
101 orgString = orgString.replace(new RegExp("'", "gi"), "'");
102 return orgString;
103 }
104 //绑定触发控件的click事件
105 AddEvent(me.targetObj, "click", function() {
106 me.pubDiv();
107 });
108 //绑定浮动层的事件
109 AddEvent(me.pupDivObj, "mouseover", function() {
110 me.isFocusIn = true;
111 });
112 AddEvent(me.pupDivObj, "mouseout", function() {
113 me.isFocusIn = false;
114 });
115 //隐藏弹出层
116 me.Hide = function() {
117 me.pupDivObj.style.display = "none";
118 me.currentIndex = -1;
119 }
120 //下拉菜单的鼠标事件
121 me.OnTdMouseOver = function(i) {
122 me.currentIndex = i;
123 var result = me.pupDivObj.firstChild;
124 result.rows[me.currentIndex].style.cursor = "point";
125 if (!result || result.rows.length <= 0)
126 return;
127 if (result.rows[me.currentIndex] != undefined && result.rows[me.currentIndex].name != "line") {
128
129 //取消之前选中项的颜色
130 if (result.rows[me.LastIndex] != null) {
131 result.rows[me.LastIndex].style.backgroundColor = "#FFFFFF";
132 result.rows[me.LastIndex].style.color = "#000000";
133 }
134 //改变选中项的颜色
135 if (result.rows[me.currentIndex] != undefined) {
136 result.rows[me.currentIndex].style.backgroundColor = "gray"; //"#3161CE";
137 result.rows[me.currentIndex].style.color = "#FFFFFF";
138 }
139 me.LastIndex = me.currentIndex;
140 }
141 }
142 me.OnTdMouseOut = function(i) {
143 var result = me.pupDivObj.firstChild;
144 if (!result || result.rows.length <= 0)
145 return;
146 if (result.rows[me.currentIndex] != undefined && result.rows[me.currentIndex].name != "line") {
147
148 result.rows[me.currentIndex].style.backgroundColor = "#FFFFFF";
149 result.rows[me.currentIndex].style.color = "#000000";
150 }
151 }
152 me.OnTdMouseClick = function(i) {
153 var evt = evt || window.event;
154 var result = me.pupDivObj.firstChild;
155 if (!result || result.rows.length <= 0)
156 return;
157 if (result.rows[me.currentIndex].name != "line") {
158
159 //给输入框赋值
160 me.Result = result.rows[me.currentIndex].getAttribute("ReturnValue");
161 //隐藏搜索结果
162 me.pupDivObj.style.display = "none";
163 me.currentIndex = -1;
164 //触发外部事件
165 if (me.Result == "0") {
166 //自定义
167 me.callBackNewFun();
168 }
169 else {
170 me.callBackFun(me.Result);
171 }
172 }
173 }
174 //绑定控件失去焦点事件
175 AddEvent(me.targetObj, "blur", function() {
176 if (!me.isFocusIn) {
177 me.Hide();
178 }
179 });
180 //初始化事件函数
181 me.InitControl = function(itemList, callBackFun, callBackNewFun) {
182 //需要绑定的选项
183 if (itemList.length > 0) {
184 me.itemList = itemList;
185 }
186 else { me.itemList = []; }
187 //初始化选项的点击函数
188 if (me.callBackFun == null || typeof (me.callBackFun) == undefined) {
189 me.callBackFun = callBackFun;
190 }
191 //初始化新建选项的点击函数
192 if (me.callBackNewFun == null || typeof (me.callBackNewFun) == undefined) {
193 me.callBackNewFun = callBackNewFun;
194 }
195 }
196 //添加对象事件
197 function AddEvent(target, eventType, callback) {
198 DeleteEvent(target, eventType, callback);
199 if (target.addEventListener) {
200 target.addEventListener(eventType, callback, false);
201 }
202 else {
203 //ie
204 target.attachEvent("on" + eventType, function(event) { return callback.call(target, event); });
205 }
206 }
207 //删除对象事件
208 function DeleteEvent(target, eventType, callback) {
209 if (target.removeEventListener) {
210 target.removeEventListener(eventType, callback, true);
211 }
212 else {
213 //ie
214 target.detachEvent("on" + eventType, callback);
215 }
216 }
217 //返回对象
218 return me;
219 };
220 /*
221 * 新建一个闭包,用于实现鼠标点击搜索结果时的事件,以解决通过训练传递的参数一直是最后一个索引的问题
222 *
223 * writter:zhangyu 2012-01-03
224 */
225 function BiBaoOnMouseResult(i, me) {
226 this.OnMouseClickEx = function() {
227 me.OnTdMouseClick(i);
228 };
229 this.OnMouseOverEx = function() {
230 me.OnTdMouseOver(i);
231 };
232 this.OnMouseOutEx = function() {
233 me.OnTdMouseOut(i);
234 };
235 }
View Code
点击这里下载源码