<%-- 右键菜单 --%> <div> <%-- 层级 0 --%> <ul role="menu" level="0"> <%-- 通过给菜单项添加样式“hasChildren”并在li标签下添加菜单结构即可扩展子级菜单 --%> <li><a tabindex="-1" action="refreshzTreeObj">刷新</a> <ul role="menu" level="1"> <li><a tabindex="-1">将数据库复制到不同的主机/数据库</a></li> <li><a tabindex="-1">创建数据库</a></li> <li><a tabindex="-1">改变数据库</a></li> <li><a tabindex="-1">新数据搜索</a></li> <li><a tabindex="-1">创/建</a></li> <li><a tabindex="-1">更多数据库操作</a></li> <li></li> <li><a tabindex="-1">备份/导出</a></li> <li><a tabindex="-1">导入</a></li> <li></li> <li><a tabindex="-1">在创建数据库架构HTML</a></li> </ul> </li> </ul> <%-- 层级 1 --%> <ul role="menu" level="1"> <li><a tabindex="-1">将数据库复制到不同的主机/数据库</a></li> <li><a tabindex="-1">创建数据库</a></li> <li><a tabindex="-1">改变数据库</a></li> <li><a tabindex="-1">新数据搜索</a></li> <li><a tabindex="-1">创/建</a></li> <li><a tabindex="-1">更多数据库操作</a></li> <li></li> <li><a tabindex="-1">备份/导出</a></li> <li><a tabindex="-1">导入</a></li> <li></li> <li><a tabindex="-1">在创建数据库架构HTML</a></li> </ul> <%-- 层级 2 --%> <ul role="menu" level="2"> <li><a tabindex="-1">创建表</a></li> <li><a tabindex="-1">将表复制到不同的主机/数据库</a></li> <li><a tabindex="-1">数据搜索</a></li> <li></li> <li><a tabindex="-1">计划备份</a></li> <li><a tabindex="-1">备份表作为SQL转储</a></li> </ul> </div>
CSS:
/* 右键菜单 - start */ .dropdown-menu .dropdown-menu { position: absolute; top: -9px; left: 100%; } .dropdown-menu li { position: relative; } .dropdown-menu li.hasChildren:before { content: ''; position: absolute; top: 50%; right: 8px; width: 0; height: 0; margin-top: -5px; border-style: solid; border-color: transparent transparent transparent rgba(0, 0, 0, 0.5); border-width: 5px 0 5px 5px; pointer-events: none; } .dropdown-menu li.hasChildren:hover > .dropdown-menu { display: block; } /* 右键菜单 - end */
JS:
/* 以下为右键菜单插件(Bootstrap风格) */
;(function ($) {
'use strict';
/* CONTEXTMENU CLASS DEFINITION
* ============================ */
var toggle = '[data-toggle="context"]';
var ContextMenu = function (element, options) {
this.$element = $(element);
this.before = options.before || this.before;
this.onItem = options.onItem || this.onItem;
this.scopes = options.scopes || null;
if (options.target) {
this.$element.data('target', options.target);
}
this.listen();
};
ContextMenu.prototype = {
constructor: ContextMenu
, show: function (e) {
var $menu
, evt
, tp
, items
, relatedTarget = {relatedTarget: this, target: e.currentTarget};
if (this.isDisabled()) return;
this.closemenu();
if (this.before.call(this, e, $(e.currentTarget)) === false) return;
$menu = this.getMenu();
$menu.trigger(evt = $.Event('show.bs.context', relatedTarget));
tp = this.getPosition(e, $menu);
items = 'li:not(.divider)';
$menu.attr('style', '')
.css(tp)
.addClass('open')
.on('click.context.data-api', items, $.proxy(this.onItem, this, $(e.currentTarget)))
.trigger('shown.bs.context', relatedTarget);
// Delegating the `closemenu` only on the currently opened menu.
// This prevents other opened menus from closing.
$('html')
.on('click.context.data-api', $menu.selector, $.proxy(this.closemenu, this));
return false;
}
, closemenu: function (e) {
var $menu
, evt
, items
, relatedTarget;
$menu = this.getMenu();
if (!$menu.hasClass('open')) return;
relatedTarget = {relatedTarget: this};
$menu.trigger(evt = $.Event('hide.bs.context', relatedTarget));
items = 'li:not(.divider)';
$menu.removeClass('open')
.off('click.context.data-api', items)
.trigger('hidden.bs.context', relatedTarget);
$('html')
.off('click.context.data-api', $menu.selector);
// Don't propagate click event so other currently
// opened menus won't close.
if (e) {
e.stopPropagation();
}
}
, keydown: function (e) {
if (e.which == 27) this.closemenu(e);
}
, before: function (e) {
return true;
}
, onItem: function (e) {
return true;
}
, listen: function () {
this.$element.on('contextmenu.context.data-api', this.scopes, $.proxy(this.show, this));
$('html').on('click.context.data-api', $.proxy(this.closemenu, this));
$('html').on('keydown.context.data-api', $.proxy(this.keydown, this));
}
, destroy: function () {
this.$element.off('.context.data-api').removeData('context');
$('html').off('.context.data-api');
}
, isDisabled: function () {
return this.$element.hasClass('disabled') ||
this.$element.attr('disabled');
}
, getMenu: function () {
var selector = this.$element.data('target')
, $menu;
if (!selector) {
selector = this.$element.attr('href');
selector = selector && selector.replace(/.*(?=#[^\s]*$)/, ''); //strip for ie7
}
$menu = $(selector);
return $menu && $menu.length ? $menu : this.$element.find(selector);
}
, getPosition: function (e, $menu) {
var mouseX = e.clientX
, mouseY = e.clientY
, boundsX = $(window).width()
, boundsY = $(window).height()
, menuWidth = $menu.find('.dropdown-menu').outerWidth()
, menuHeight = $menu.find('.dropdown-menu').outerHeight()
, tp = {"position": "absolute", "z-index": 9999}
, Y, X, parentOffset;
if (mouseY + menuHeight > boundsY) {
Y = {"top": mouseY - menuHeight + $(window).scrollTop()};
} else {
Y = {"top": mouseY + $(window).scrollTop()};
}
if ((mouseX + menuWidth > boundsX) && ((mouseX - menuWidth) > 0)) {
X = {"left": mouseX - menuWidth + $(window).scrollLeft()};
} else {
X = {"left": mouseX + $(window).scrollLeft()};
}
// If context-menu's parent is positioned using absolute or relative positioning,
// the calculated mouse position will be incorrect.
// Adjust the position of the menu by its offset parent position.
parentOffset = $menu.offsetParent().offset();
X.left = X.left - parentOffset.left;
Y.top = Y.top - parentOffset.top;
return $.extend(tp, Y, X);
}
};
/* CONTEXT MENU PLUGIN DEFINITION
* ========================== */
$.fn.contextmenu = function (option, e) {
return this.each(function () {
var $this = $(this)
, data = $this.data('context')
, options = (typeof option == 'object') && option;
if (!data) $this.data('context', (data = new ContextMenu($this, options)));
if (typeof option == 'string') data[option].call(data, e);
});
};
$.fn.contextmenu.Constructor = ContextMenu;
/* APPLY TO STANDARD CONTEXT MENU ELEMENTS
* =================================== */
$(document)
.on('contextmenu.context.data-api', function () {
$(toggle).each(function () {
var data = $(this).data('context');
if (!data) return;
data.closemenu();
});
})
.on('contextmenu.context.data-api', toggle, function (e) {
$(this).contextmenu('show', e);
e.preventDefault();
e.stopPropagation();
});
}(jQuery));