XP 风格 JavaScript 开始菜单






4.86/5 (21投票s)
多级、可滚动、浏览器兼容的 XP 风格 JavaScript 开始菜单和上下文菜单
引言
正如标题所示,本文介绍的是多级、可滚动、浏览器兼容的 XP 风格 JavaScript 开始菜单以及上下文菜单。
背景
开始菜单的需求源于一个项目需求,该项目需要以一种有条理的方式显示大量的应用程序链接。因此,我们找到了一个解决方案,在项目中实现类似开始菜单的功能,该功能可以轻松处理所有应用程序链接。
因此,我开始寻找一个可以塑造成 XP 风格开始菜单的 JavaScript 菜单 API。我发现了很多选项,例如 - 基于列表的菜单、基于树的菜单、基于 CSS 的菜单等,但没有一个适合我的需求。大多数免费提供的菜单都满足了多级菜单的要求,但没有一个是可滚动的。
最后,我决定准备自己的开始菜单 API,这就是结果。

Using the Code
首先,您需要执行以下步骤
- 在您的 HTML 页面的
HEAD
部分包含以下 CSS 和 JS 文件<LINK REL="stylesheet" TYPE="text/css" HREF="base.css" /> <LINK REL="stylesheet" TYPE="text/css" HREF="theme.css" /> <SCRIPT TYPE="text/javascript" SRC="Utility.js"></SCRIPT> <SCRIPT TYPE="text/javascript" SRC="StartMenu.js"></SCRIPT>
Utility.js 文件是一个多用途文件,其中包含操作 DOM 对象所需的常用函数。
StartMenu.js 是包含整体功能的核心文件。
两个 CSS 文件,即 - base.css 和 theme.css,用于设置菜单的样式。
- 在
body
部分,创建一个具有唯一 ID 的容器来容纳要创建的所有菜单项。<DIV ID='menuContainer'></DIV>
- 要创建菜单项,您可以添加一个 JavaScript 文件,也可以直接将代码放在
<script>
开/闭标签内。为了代码的可读性,我在提供的示例中添加了一个 JavaScript 文件。无论哪种方式,您都需要在您的文件中添加以下代码//Define a unique global variable name that depicts the StartMenu object. var menu = null; // Define a function that will be invoked at the time of body onload function bindOnLoad(e) { // function to calculate the dimensions of the screen _screenDimensions(); // re-calculating the dimensions on window resize _addEvent(window, function(e) { _screenDimensions(); }, "resize"); // disabling the text selection // (you can remove this line as per requirement) _disableSelection(document.body); // function which will create the menuitems as required createStartMenuItems(e); } function createStartMenuItems(e) { // Create StartMenu object with passing the unique ID // for the container created in step-2 menu = new StartMenu('menuContainer'); //Example API -1 for adding the menu item //startmenuObject.add(isSeparator, menuBarId, menuId, menuText, //menuOnClick, menuTooltip, subMenuBarId, menuBarCSS, meta) menu.add(false,'MenuBar01','MenuItem01','Menu Item 01',function(evt) {alert('Hi ! \n' + this.toString()); } , 'Description of Menu Item 01','MenuBar02','MainMenu', 'any extra information about the menu item goes here. You can access this information directly in your binded function'); menu.add(false,'MenuBar01','MenuItem02','Menu Item 02'); menu.add(true,'MenuBar01'); // adding a separator between the menu items menu.add(false,'MenuBar01','MenuItem03','Menu Item 03'); menu.add(false,'MenuBar02','MenuItem04','Menu Item 04 Javascript call', 'javascript:alert("you can call function this way also")'); menu.add(false,'MenuBar02','MenuItem05', 'Menu Item 05 Open codeproject.com','url:https://codeproject.org.cn/'); menu.add(false,'MenuBar02','MenuItem06','Menu Item 06'); menu.add(false,'MenuBar02','MenuItem07','Menu Item 07'); // ------------------------------------------- // More menuitems goes here // ------------------------------------------- //Example API -2 for adding the menu item var item = new MenuItemHashmap(); item.menuBarId = 'MenuBar02'; item.menuText = 'Menu item created using map'; item.menuOnClick = function(e) { alert('called from map.\n\n' + this.toString());} item.meta = 'extra information on this item goes here'; menu.addMap(item); item.clear(); item.menuBarId = 'MenuBar02'; item.menuText = 'SubMenu item created using map'; item.menuOnClick = function(e) { alert('called from next map.\n\n' + this.toString());} item.meta = 'extra information on this sub item goes here'; menu.addMap(item); //startmenuObject.init(menuBarId, eventObject[, isContextMenu]) menu.init("MenuBar01", e, false); // menuBarId is the first menuBar // to be displayed on click of start button }
- 向
body
部分添加onload
事件。还添加开始菜单按钮或链接。使用第 3 步中创建的全局StartMenu
对象为开始菜单按钮添加onclick
事件<BODY onload="bindOnLoad(event)"> <DIV ID="MenuButton" CLASS="MenuButton" ONCLICK="menu.show(null, event)"> Start</DIV>
就是这样。开始菜单已准备就绪。
创建上下文菜单
重复上面所示的第 2 步和第 3 步来创建上下文菜单。开始菜单和上下文菜单之间的唯一区别在于 API 调用 - menuObj.init()
。此方法中第三个参数的值决定了它是否为上下文菜单。请参阅下面的 API 描述以及附加的 sample3.htm 和 sample3.js 文件中的代码以获取更多详细信息。
理解 API
使用以下代码创建 StartMenu
对象
var menuObj = new StartMenu(containerDivId);
添加菜单项可以通过两种方式完成
(A) 添加
menuObj.add(isSeparator, menuBarId, menuId, menuText, menuOnClick,
menuTooltip, subMenuBarId, menuBarCSS, meta);
参数含义
isSeparator
: (类型布尔值) 设置为true
时,菜单项被视为分隔符,不允许进行任何操作。默认为false
。menuBarId
: 此特定菜单项所属的分组或菜单栏。这是一个必填参数。menuId
: 用于标识当前菜单项的唯一 ID。如果您不需要,可以将其留空。menuText
: 要在开始菜单上显示的文本。menuOnClick
: 这可以是 JavaScript 函数对象,也可以是带有前缀 "javascript:
" 或 "url:
" 的string
。menuTooltip
: 在菜单项上显示的鼠标悬停文本或工具提示。subMenuBarId
: 提供将作为此菜单项子项显示的菜单栏的 ID。menuBarCSS
: 可选的菜单栏 CSS,用于格式化任何特定的菜单栏(而非菜单项)。有关演示,请参阅源文件中提供的 Sample2.htm 文件。meta
: 任何可选的特定于菜单项的详细信息,需要存储起来,以便在处理项目点击事件时使用。
当 JavaScript 函数绑定到菜单项时,您可以使用 this
对象访问所有这些属性。例如,this.meta
, this.id
等。
(B) MenuItemHashmap
首先,像下面这样创建一个 MenuItemHashmap
对象
var itemObj = new MenuItemHashmap();
然后,设置项目的属性。这些属性与上面方法中的参数相同(如 (A) 部分所示)。您可以选择添加什么和排除什么,其余的将设置为默认值,如下所示
itemObj.menuBarId = 'MenuBar02';
itemObj.menuText = 'Menu item created using map';
itemObj.menuOnClick = function(e)
{ alert('called from map.\n\n' + this.toString());}
下一步是将此映射添加到先前创建的 StartMenu
对象中,如下所示
menuObj.addMap(itemObj);
您可以通过使用以下 API 将此对象重置为默认值来重用相同的项目对象来添加更多菜单项
itemObj.clear();
使用任何一个 API 添加完所有菜单项后,接下来需要像下面这样初始化 StartMenu
对象
menuObj.init(menuBarId, eventObject, isContextMenu);
参数含义
menuBarId
: 顾名思义,是使用上述 API 创建的菜单栏的 ID。点击开始菜单按钮时,此 ID 应该是第一个显示的菜单栏。eventObject
: 这是事件对象。isContextMenu
: 它决定相关的菜单对象是用于创建开始菜单还是上下文菜单。如果设置为true
,则相关的菜单对象将被视为上下文菜单。默认为false
或null
。当设置为true
时,menuBarId
中的值将是上下文菜单激活时要显示的第一个菜单栏的 ID。
完成所有这些步骤后,最后一步是将菜单绑定到任何链接、按钮、图像或文本。这可以通过在点击 DOM 对象或鼠标悬停在其上时调用以下 API 来完成(如上面第 4 步所示)。
menuObj.show(null, event);
关注点
我用于设计这段代码的基本概念是使用 DIV
和 SPAN
标签。有一个主 DIV
标签用于处理菜单项的滚动。下一个 DIV
标签用于组合所有菜单项,这些菜单项是使用 SPAN
标签构建的。因此,整体结构将如下所示
DIV
DIV
SPAN
因此,您可以操作外部 DIV
标签并应用任何所需的格式。Sample1.htm 是一个简单的开始菜单,而 Sample2.htm 文件展示了如何应用用户定义的格式。
我与此一起设计的另一件事是 Utility.js 文件,其中包含各种常用的函数,用于处理 DOM 对象。以下是可用的函数集
function _gs(obj)
: **获取样式**的缩写- 获取指定对象的当前样式。它返回一个包含当前高度、宽度、顶部、左侧位置、边框宽度以及填充的映射。
function _gol(obj)
: **获取左偏移量**的缩写- 以像素为单位获取指定对象距离屏幕左侧的距离。
function _got(obj)
: 获取顶部偏移量的缩写- 以像素为单位获取指定对象距离屏幕顶部的距离。
function _ge(id)
: **按 ID 获取元素**的缩写- 获取指定 ID 的 DOM 对象。
function _addEvent(target, functionref, eventType)
- 将任何事件绑定到指定的 target,并在事件发生时调用引用的函数。
function _getEvent(type, e)
- 根据指定的类型获取发生事件的 Source 或 Destination。如果类型是 "Src",它将返回生成事件的源 DOM 对象。如果类型是 "Dest",它将返回事件将结束的目标 DOM 对象。
function _processStyle(el, tc, action)
- 检查、添加或删除特定 DOM 对象的指定 CSS 类。在这里,"el" 是 DOM 对象,"tc" 是 CSS 类名,"action" 可以是 - "check"、"add" 或 "remove"。
function _screenDimensions()
- 计算当前屏幕的尺寸(高度和宽度),并将值存储在两个全局变量 - _dh 和 _dw (表示文档高度和文档宽度)中。
function _disableSelection(target)
- 禁用指定 DOM 对象的文本选择。
注意
包中还包含另一个 JavaScript 文件 - MessageBox.js。这是我设计的另一个实用程序。它已在 Sample2.htm 文件中使用。有关此实用程序及其用法的更多详细信息,请在此处 查看。
升级说明
代码升级(2.0 版本)向后兼容。如果有人已将 1.0 版本集成到他们的代码中,他们只需替换 StartMenu.js 文件即可升级到 2.0 版本。
2.0 版本中的功能
- 添加了多级、可滚动、跨浏览器上下文菜单
- 与前一版本向后兼容
- 使用相同的 API 创建上下文菜单
结论
希望大家都会发现这篇文章非常有帮助。
历史
- 2011 年 8 月 19 日:初始发布
- 2011 年 8 月 23 日:添加了注释
- 2011 年 9 月 15 日:2.0 版本,添加了多级、可滚动上下文菜单