當用戶在視圖邊緣(如右下角)右鍵召喚菜單欄的時候,菜單仍然從選中元素的右下角彈出,這時二級菜單欄一般都離開了視圖區域,用戶無法進一步操作。
這個問題挺常見的,原作者的留言板:


但是作者應該是已經不再維護了,最后一個版本還是2011年10月的。
我給出的比較初步的解決方案:
因為作者沒有給出鼠標事件的接口,只能在庫的源碼中修改坐標計算邏輯,以達到根據位置自適應彈出菜單的目的。
思路:判斷右鍵點擊位置,與窗口(我的是iframe窗口)大小作比較,取中心點分為坐標系的四個象限。
1、在第一象限召喚菜單欄,顯示在觸發事件元素的左下角
2、在第二象限召喚菜單欄,顯示在觸發事件元素的左上角
3、在第三象限召喚菜單欄,顯示在觸發事件元素的右上角
4、在第四象限召喚菜單欄,顯示在觸發事件元素的右下角
完整的代碼如下:
/* * smartMenu.js 智能上下文菜單插件 * http://www.zhangxinxu.com/ * * Copyright 2011, zhangxinxu * * 2011-05-26 v1.0 編寫 * 2011-06-03 v1.1 修復func中this失准問題 * 2011-10-10 v1.2 修復腳本放在<head>標簽中層無法隱藏的問題 * 2011-10-30 v1.3 修復IE6~7下二級菜單移到第二項隱藏的問題 */ (function($) { var D = $(document).data("func", {}); $.smartMenu = $.noop; $.fn.smartMenu = function(data, options) { var B = $("body"), defaults = { name: "", offsetX: 2, offsetY: 2, textLimit: 6, beforeShow: $.noop, afterShow: $.noop }; var params = $.extend(defaults, options || {}); var htmlCreateMenu = function(datum) { var dataMenu = datum || data, nameMenu = datum? Math.random().toString(): params.name, htmlMenu = "", htmlCorner = "", clKey = "smart_menu_"; if ($.isArray(dataMenu) && dataMenu.length) { htmlMenu = '<div id="smartMenu_'+ nameMenu +'" class="'+ clKey +'box">' + '<div class="'+ clKey +'body">' + '<ul class="'+ clKey +'ul">'; $.each(dataMenu, function(i, arr) { if (i) { htmlMenu = htmlMenu + '<li class="'+ clKey +'li_separate"> </li>'; } if ($.isArray(arr)) { $.each(arr, function(j, obj) { var text = obj.text, htmlMenuLi = "", strTitle = "", rand = Math.random().toString().replace(".", ""); if (text) { if (text.length > params.textLimit) { text = text.slice(0, params.textLimit) + "…"; strTitle = ' title="'+ obj.text +'"'; } if ($.isArray(obj.data) && obj.data.length) { htmlMenuLi = '<li class="'+ clKey +'li" data-hover="true">' + htmlCreateMenu(obj.data) + '<a href="javascript:" class="'+ clKey +'a"'+ strTitle +' data-key="'+ rand +'"><i class="'+ clKey +'triangle"></i>'+ text +'</a>' + '</li>'; } else { htmlMenuLi = '<li class="'+ clKey +'li">' + '<a href="javascript:" class="'+ clKey +'a"'+ strTitle +' data-key="'+ rand +'">'+ text +'</a>' + '</li>'; } htmlMenu += htmlMenuLi; var objFunc = D.data("func"); objFunc[rand] = obj.func; D.data("func", objFunc); } }); } }); htmlMenu = htmlMenu + '</ul>' + '</div>' + '</div>'; } return htmlMenu; }, funSmartMenu = function() { var idKey = "#smartMenu_", clKey = "smart_menu_", jqueryMenu = $(idKey + params.name); if (!jqueryMenu.size()) { $("body").append(htmlCreateMenu()); //事件 $(idKey + params.name +" a").bind("click", function() { var key = $(this).attr("data-key"), callback = D.data("func")[key]; if ($.isFunction(callback)) { callback.call(D.data("trigger")); } $.smartMenu.hide(); return false; }); $(idKey + params.name +" li").each(function() { var isHover = $(this).attr("data-hover"), clHover = clKey + "li_hover"; $(this).hover(function() { var jqueryHover = $(this).siblings("." + clHover); jqueryHover.removeClass(clHover).children("."+ clKey +"box").hide(); jqueryHover.children("."+ clKey +"a").removeClass(clKey +"a_hover"); if (isHover) { $(this).addClass(clHover).children("."+ clKey +"box").show(); $(this).children("."+ clKey +"a").addClass(clKey +"a_hover"); } }); }); return $(idKey + params.name); } return jqueryMenu; }; $(this).each(function() { this.oncontextmenu = function(e) { //回調 if ($.isFunction(params.beforeShow)) { params.beforeShow.call(this); } e = e || window.event; //阻止冒泡 e.cancelBubble = true; if (e.stopPropagation) { e.stopPropagation(); } //隱藏當前上下文菜單,確保頁面上一次只有一個上下文菜單 $.smartMenu.hide(); var st = D.scrollTop(); var jqueryMenu = funSmartMenu(); if (jqueryMenu) { /* 2017.12.4修改:根據觸發事件的位置,自適應方向彈出 */ if( e.clientX <= (e.view.innerWidth/2) && e.clientY <= (e.view.innerHeight/2))//左上 { jqueryMenu.css({ display: "block", left: e.clientX + params.offsetX , top: e.clientY + st + params.offsetY , }); } if( e.clientX >= (e.view.innerWidth/2) && e.clientY <= (e.view.innerHeight/2))//右上 { jqueryMenu.css({ display: "block", left: e.clientX + params.offsetX -100, top: e.clientY + st + params.offsetY , }); } if( e.clientX <= (e.view.innerWidth/2) && e.clientY >= (e.view.innerHeight/2))//左下 { jqueryMenu.css({ display: "block", left: e.clientX + params.offsetX , top: e.clientY + st + params.offsetY -120, }); } if( e.clientX >= (e.view.innerWidth/2) && e.clientY >= (e.view.innerHeight/2))//右下 { jqueryMenu.css({ display: "block", left: e.clientX + params.offsetX -100, top: e.clientY + st + params.offsetY -120, }); } D.data("target", jqueryMenu); D.data("trigger", this); //回調 if ($.isFunction(params.afterShow)) { params.afterShow.call(this); } return false; } }; }); if (!B.data("bind")) { B.bind("click", $.smartMenu.hide).data("bind", true); } }; $.extend($.smartMenu, { hide: function() { var target = D.data("target"); if (target && target.css("display") === "block") { target.hide(); } }, removeevent: function (event) { var target = D.data("target"); if (target) { target.remove(); if ($.isFunction(event)) { event.call(this); } } }, remove: function() { var target = D.data("target"); if (target) { target.remove(); } } }); })(jQuery);
修改之后的效果:
在右下方召喚
在左下方召喚
在左上方召喚
在右上方召喚
大概效果就是這樣了,經過測試,菜單欄不會彈到視窗外面了。
