JavaScript 实现触点式弹出菜单插件
之前做项目时经常用到一种触点式弹出菜单或者导航的功能,这个功能的主要应用场景是:web页面中多层分级导航或者子功能目录,但又考虑到页面区域有限,于是就考虑到在鼠标移动到某导航按钮上或者点击时,系统将在该按钮下浮动一个层,展现子导航或者子目录,当用户选择这些子目录/导航时,页面作出相应的操作,这种功能从人机交互和页面布局上都有很好的人性化体验。最近有些闲暇时间,将此功能重新整理并封装,形成一个公共插件,命名为:SelectItem.js,分享如下:
插件的主要实现的功能:
1. 功能模块化封装,提供一个对外的初始化接口供应用调用即可,首先需要绑定一个页面触发元素(链接/按钮等),传入元素id;
2. 提供子菜单/导航条目的初始化入口,调用方法输入需要初始化的条目数组,以参数方式输入;
3. 提供对外事件接口,如点击子菜单/导航的事件定义等,供调用方自定义事件实现;
4. 提供辅助添加功能,如果子菜单是选择信息,而初始化的条目又不符合调用需求,则需提供让给调用方自定义条目的功能,此辅助功能可以根据需要进行屏蔽;
效果图如下:(左侧为带自定义辅助功能的效果,右侧为不带自定义辅助功能的效果)


上述效果调用时的代码如下:
AutoDiv("btn", false).InitControl([
{
key: "12",
value: "二个选项(A | B)"
},
{
key: "13",
value: "三个选项(A | B | C)"
},
{
key: "14",
value: "四个选项(A | B | C | D)"
},
{
key: "15",
value: "五个选项(A | B | C | D | E)"
},
{
key: "0",
value: "自定义选项类型"
}
],
function(value) {
//处理选中类型
alert(value);
},
function() {
//打开自定义选项创建窗口
alert("new dialog.");
});
}
可以看出,在页面中引用该js文件,然后对需要触点弹出的页面元素进行插件绑定即可,其中绑定函数AutoDiv("btn", false)有两个参数,第一个参数是需要绑定的页面元素id,第二个参数是个布尔值,需要自定义辅助功能时传递true,否则传递false。绑定好页面元素后,接下来是链式调用过程,函数InitControl有三个参数,第一个参数是需要展现的菜单项,它是一个键值对数组(key为选择项的反馈表示,value为菜单项显示信息);第二个参数是菜单项选择点击事件的实现,由调用方定义实现,函数有一个参数,即所选菜单项的key值;第三个参数是自定义辅助事件的实现,同样由调用方定义实现。
控件源码如下:
/*
* write by zhangyu,2013-07-09,v1.0
*
* 实现触点式菜单弹出及选择功能
*
*/ function AutoDiv(id, allowNewFlag) {
//预留退路
if (!document.getElementById) return false;
if (!document.createElement) return false; //保存当前上下文
var me = this; me.targetControlID; //定义当前绑定的id及其对象
me.targetObj = null;
me.pupDivObj = null; //定义弹出浮动层对象
me.callBackFun = null; //选中事件
me.callBackNewFun = null; //自定义回调事件
me.isFocusIn = false; //标记浮动层是不是处于鼠标焦点内
me.allowNewFlag = false; //标记是否显示自定义选项类型选项
me.currentIndex = -1; //当前选中的结果索引
me.LastIndex = -1; //上一条选中的结果索引 me.Result; //最后结果‘13’、‘14’、‘15’、‘21’、‘31’ if (id != null && typeof (id) != undefined && id != "") {
if (!document.getElementById(id)) return false;
//赋值类的成员
me.targetControlID = id;
me.targetObj = document.getElementById(id);
} if (allowNewFlag != null && typeof (allowNewFlag) != undefined && allowNewFlag != "") { me.allowNewFlag = allowNewFlag;
} if (me.pupDivObj == null || typeof (me.pupDivObj) == undefined) { me.pupDivObj = document.createElement("div");
me.pupDivObj.setAttribute("class", "pupDiv");
//加载新建的div对象到文档中去
var parent = document.getElementById(me.targetControlID).parentNode;
if (parent != null && typeof (parent) != undefined) {
parent.appendChild(me.pupDivObj);
}
}
//浮动层弹出展现事件
me.pubDiv = function() {
me.pupDivObj.style.display = "block";
me.pupDivObj.style.backgroundColor = "#ffffff";
me.pupDivObj.style.position = "absolute";
me.pupDivObj.style.top = me.targetObj.getBoundingClientRect().top + 20;
me.pupDivObj.style.left = me.targetObj.getBoundingClientRect().left;
me.pupDivObj.style.width = "150";
//绑定内容
me.pupDivObj.innerHTML = me.GetContentTable();
//定义行事件
var result = me.pupDivObj.getElementsByTagName("td");
if (result.length > 0) {
//给每个Td动态绑定事件(新建了一个闭包,用来传递参数,否则所有元素的一下三个鼠标事件的参数i会是相同的值)
for (var i = 0; i < result.length; i++) {
var bb = new BiBaoOnMouseResult(i, me);
AddEvent(result[i], "mouseover", bb.OnMouseOverEx);
AddEvent(result[i], "mouseout", bb.OnMouseOutEx);
AddEvent(result[i], "click", bb.OnMouseClickEx);
}
}
}
//类型选项绑定函数
me.GetContentTable = function() {
var DivContent = "<table cellSpacing='0' cellPadding='1' align='center' border='0' id='DivContent_Table'>";
for (var i = 0; i < me.itemList.length - 1; i++) {
DivContent += " <tr ReturnValue='" + me.itemList[i].key + "' name='row'>"
+ " <td height='15' nowrap>"
+ " <div style='width:" + (me.pupDivObj.offsetWidth - 12) + ";' class='listOption' title='" + me.FliterString(me.itemList[i].value) + "'>" + me.FliterString(me.itemList[i].value) + "<div>"
+ " </td>"
+ " </tr>";
}
//添加自定义选项
if (me.allowNewFlag) {
DivContent += " <tr ReturnValue='" + me.itemList[me.itemList.length - 1].key + "' name='line'>"
+ " <td nowrap>"
+ " <div style='font-size:1px; border-top:solid 1px #dde0e0; height:3px;'></div>"
+ " </td>"
+ " </tr>"
+ " <tr ReturnValue='" + me.itemList[me.itemList.length - 1].key + "' name='row'>"
+ " <td height='15' nowrap>"
+ " <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>"
+ " </td>"
+ " </tr>";
}
DivContent += "</table>";
return DivContent;
}
//定义过滤函数
me.FliterString = function(orgString) {
orgString = orgString.replace(new RegExp("\"", "gi"), """);
orgString = orgString.replace(new RegExp("'", "gi"), "'");
return orgString;
}
//绑定触发控件的click事件
AddEvent(me.targetObj, "click", function() {
me.pubDiv();
});
//绑定浮动层的事件
AddEvent(me.pupDivObj, "mouseover", function() {
me.isFocusIn = true;
});
AddEvent(me.pupDivObj, "mouseout", function() {
me.isFocusIn = false;
});
//隐藏弹出层
me.Hide = function() {
me.pupDivObj.style.display = "none";
me.currentIndex = -1;
}
//下拉菜单的鼠标事件
me.OnTdMouseOver = function(i) {
me.currentIndex = i;
var result = me.pupDivObj.firstChild;
result.rows[me.currentIndex].style.cursor = "point";
if (!result || result.rows.length <= 0)
return;
if (result.rows[me.currentIndex] != undefined && result.rows[me.currentIndex].name != "line") { //取消之前选中项的颜色
if (result.rows[me.LastIndex] != null) {
result.rows[me.LastIndex].style.backgroundColor = "#FFFFFF";
result.rows[me.LastIndex].style.color = "#000000";
}
//改变选中项的颜色
if (result.rows[me.currentIndex] != undefined) {
result.rows[me.currentIndex].style.backgroundColor = "gray"; //"#3161CE";
result.rows[me.currentIndex].style.color = "#FFFFFF";
}
me.LastIndex = me.currentIndex;
}
}
me.OnTdMouseOut = function(i) {
var result = me.pupDivObj.firstChild;
if (!result || result.rows.length <= 0)
return;
if (result.rows[me.currentIndex] != undefined && result.rows[me.currentIndex].name != "line") { result.rows[me.currentIndex].style.backgroundColor = "#FFFFFF";
result.rows[me.currentIndex].style.color = "#000000";
}
}
me.OnTdMouseClick = function(i) {
var evt = evt || window.event;
var result = me.pupDivObj.firstChild;
if (!result || result.rows.length <= 0)
return;
if (result.rows[me.currentIndex].name != "line") { //给输入框赋值
me.Result = result.rows[me.currentIndex].getAttribute("ReturnValue");
//隐藏搜索结果
me.pupDivObj.style.display = "none";
me.currentIndex = -1;
//触发外部事件
if (me.Result == "0") {
//自定义
me.callBackNewFun();
}
else {
me.callBackFun(me.Result);
}
}
}
//绑定控件失去焦点事件
AddEvent(me.targetObj, "blur", function() {
if (!me.isFocusIn) {
me.Hide();
}
});
//初始化事件函数
me.InitControl = function(itemList, callBackFun, callBackNewFun) {
//需要绑定的选项
if (itemList.length > 0) {
me.itemList = itemList;
}
else { me.itemList = []; }
//初始化选项的点击函数
if (me.callBackFun == null || typeof (me.callBackFun) == undefined) {
me.callBackFun = callBackFun;
}
//初始化新建选项的点击函数
if (me.callBackNewFun == null || typeof (me.callBackNewFun) == undefined) {
me.callBackNewFun = callBackNewFun;
}
}
//添加对象事件
function AddEvent(target, eventType, callback) {
DeleteEvent(target, eventType, callback);
if (target.addEventListener) {
target.addEventListener(eventType, callback, false);
}
else {
//ie
target.attachEvent("on" + eventType, function(event) { return callback.call(target, event); });
}
}
//删除对象事件
function DeleteEvent(target, eventType, callback) {
if (target.removeEventListener) {
target.removeEventListener(eventType, callback, true);
}
else {
//ie
target.detachEvent("on" + eventType, callback);
}
}
//返回对象
return me;
};
/*
* 新建一个闭包,用于实现鼠标点击搜索结果时的事件,以解决通过训练传递的参数一直是最后一个索引的问题
*
* writter:zhangyu 2012-01-03
*/
function BiBaoOnMouseResult(i, me) {
this.OnMouseClickEx = function() {
me.OnTdMouseClick(i);
};
this.OnMouseOverEx = function() {
me.OnTdMouseOver(i);
};
this.OnMouseOutEx = function() {
me.OnTdMouseOut(i);
};
}
点击这里下载源码
JavaScript 实现触点式弹出菜单插件的更多相关文章
- Mui --- 弹出菜单
mui框架内置了弹出菜单插件,弹出菜单显示内容不限,但必须包裹在一个含.mui-popover类的div中,如下即为一个弹出菜单内容: <div id="popover" c ...
- Vue2的右键弹出菜单(vue-contextmenu)
给大家推荐一个基于Vue2的右键弹出菜单插件,支持单一SPA页面以及可以在循环绑定中使用. 项目地址为:https://github.com/chIIC/vue-...demo1: 父组件绑定右键事件 ...
- 向上弹出菜单jQuery插件
插件名:柯乐义英文名:Keleyijs文件名称:jquery.keleyi.js插件功能:该插件可以让你轻易地在页面上构建一个向上弹出的二级菜单. 示例查看:http://keleyi.com/kel ...
- html5手机端遮罩弹出菜单代码
效果体验:http://hovertree.com/texiao/html5/17/ 效果图: 代码如下: <!doctype html> <html lang="zh&q ...
- 一款效果精致的 jQuery 多层滑出菜单插件
想要以用户友好的方式呈现多级菜单是件不容易的事情,而且还要跨浏览器兼容就更难了.Multi-Level Push Menu 这款 jQuery 插件提供了呈现这种菜单的解决方案,能够让你无限制的展示菜 ...
- DIV+CSS制作二级横向弹出菜单,略简单
没有使用JavaScript控制二级菜单的显示,结果如上图所示. 代码如下: <!DOCTYPE html> <html> <head> <meta char ...
- html + js 右 点击 弹出 菜单
页面 引用jar 包 <link rel="stylesheet" href="../../style/zui.min.css" type="t ...
- Android ListView两种长按弹出菜单方式
转自:http://www.cnblogs.com/yejiurui/p/3247527.html package com.wyl.download_demo; import java.util.Ar ...
- 弹出框插件layer使用
layer是一款近年来备受青睐的web弹层组件,她具备全方位的解决方案,致力于服务各水平段的开发人员,您的页面会轻松地拥有丰富友好的操作体验. 插件官方地址:http://layer.layui.co ...
随机推荐
- usaco 安慰奶牛
Description 约翰有N个牧场,编号依次为1到N.每个牧场里住着一头奶牛.连接这些牧场的有P条 道路,每条道路都是双向的.第j条道路连接的是牧场Sj和Ej,通行需要Lj的时间.两牧场之 间最多 ...
- Centos6.4 openNebula
OpenNebula 是一套开源的云计算基础管理工具,用来方便管理员在数据中心统一部署.创建.分配和管理大量的虚拟机,企业数据中心可以利用这套工具搭建自己的私有云,为 自己内部提供 IaaS 服务,类 ...
- Java学习——数据类型【2】
1. 对象与类 对象:类的一个实例,有状态和行为. 类:一个模板,描述一类对象的行状态和行为. 2. 类可以包含的变量 类型 局部变量 成员变量 类变量(静态变量) 定义位置 方法.或语句块中 类中, ...
- 2016-5-19模拟测试 bzoj3652 bzoj3653 bzoj3654
T1 description 给定正整数\(n\),定义\(f(x) = \max{y \ \mathrm{xor}\ x}(y<n)\) \(x\)在\([0,n)\)随机取值,求\(f(x) ...
- 【OpenCV】基于kmeans的细胞检测方法
问题是这样的,有一幅经过二值化处理之后的图像,我们希望统计其中细胞的个数,和不同粘连情况的细胞个数,比如,下图中有1个细胞组成连通区域的,也有2个细胞组成连通区域的,也有更多个细胞组成连通区域的,我们 ...
- unity3d中namespace的使用注意问题
最近在重构游戏中的老一套逻辑,同时要加入新的功能逻辑,因为各种命名问题,就使用namespace进行控制.但是namespace在unity3d引擎中的使用与一般不同,需要注意一点:要作为Compon ...
- c#常用工具类:文件和二进制转换
//================二进制相关转换类============== #region 将文件转换为二进制数组 /// <summary> /// 将文件转换为二进制数组 /// ...
- 安装Win7和Ubuntu12.04双系统后,意外删除Ubuntu12.04引导文件,出现error:unknown filesystem;grub rescue>错误的解决方案
很久之前在Win7基础上安装了Ubuntu12.04系统,采用硬盘安装的方法.分了1个10G的硬盘分区F盘用于存放Ubuntu12.04的引导文件,其实完全可以制作一个Ubuntu12.04的U盘启动 ...
- Oracle 数据库用户管理
Oracle 数据库用户管理 Oracle 权限设置 一.权限分类: 系统权限:系统规定用户使用数据库的权限.(系统权限是对用户而言). 实体权限:某种权限用户对其它用户的表或视图的存取权限 ...
- careercup-栈与队列 3.1
3.1 描述如何只用一个数组来实现三个栈. 解答 我们可以很容易地用一个数组来实现一个栈,压栈就往数组里插入值,栈顶指针加1: 出栈就直接将栈顶指针减1:取栈顶值就把栈顶指针指向的单元的值返回: 判断 ...