Limbo: 混沌位面 ———— 这里既空虚又充实,没有规则,没有约束,创造来自思考,生存依赖想像,现实源自梦想。
首页 给我发Email! 订阅

[造车轮计划]今天写了一个右键菜单的JS类

右键菜单在web交互设计里并不是很常用的东西,因为普通用户在浏览网页时很少会想到能用右键来操作,就像他们很难习惯拖拽一样,但在某些环境中,拖拽会成为很自然的操作——其实都跟桌面软件的操作习惯有关。所以右键菜单在某些情况下也是很有用的,抓虾的feed列表里就可以使用右键菜单,如果网页里调用了google maps,而且占据了很大的客户端面积,用右键菜单也会更方便。最近做的项目里就要用到这个,我写了一个简单的右键菜单,并且学习把它封装成一个类。

以下是html形式的代码:

  1. <div id="mousemenu" style="display: none; width: 200px;">     
  2.  
  3. <div class="mousemenumain">      
  4.  <ul id="allitems">         
  5.   <li><a href=""><span>菜单项目1</span></a></li>         
  6.   <li><a href=""><span>菜单项目2</span></a></li>        
  7.   <li><a href=""><span>菜单项目3</span></a></li>         
  8.   <li><a href=""><span>菜单项目4长度超出长度超出长度超出长度超出长度超出长度超出</span></a></li>         
  9.   <li><a href=""><span>菜单项目5</span></a></li>         
  10.   <li><a href=""><span>菜单项目6</span></a></li>         
  11.   <li><a href=""><span>菜单项目7</span></a></li>         
  12.   <li><a href=""><span>菜单项目8</span></a></li>     
  13.  </ul>  
  14. </div>  
  15. </div>

菜单的CSS:

  1. #mousemenu{
  2. position:absolute;
  3. background:url(leftbg.jpg) no-repeat;
  4.  
  5. }
  6.  
  7. .mousemenumain{
  8. width:100%;
  9. float:left;
  10.  
  11. margin:0px 5px 0px 22px;
  12. background:url(tempbg.jpg);
  13.  
  14. border:1px solid #999;
  15. border-left:none;
  16. }
  17.  
  18. *:first-child+html .mousemenumain{margin:0px 5px 0px 11px;} * html .mousemenumain{margin:0px 5px 0px 11px;}
  19.  
  20. .mousemenumain ul{
  21. padding:0px;
  22. margin:0px 0px 0px 0px;
  23.  
  24. list-style:none;
  25. width:100%;
  26. }
  27.  
  28. .mousemenumain ul li{
  29.  
  30. width:100%;
  31. }
  32.  
  33. .mousemenumain ul li a{
  34. display:block;
  35.  
  36. text-decoration:none;
  37. color:#000000;
  38. margin:0px 0px 0px 0px;
  39.  
  40. height:20px;
  41. line-height:20px;
  42. overflow-x:hidden;
  43.  
  44. width:200px;
  45. }
  46.  
  47. .mousemenumain ul li a:hover{
  48.  
  49. background:#0000ee;
  50. border:0px solid #00ccff;
  51. text-decoration:none;
  52.  
  53. color:#fff;
  54. cursor:pointer;
  55. }
  56.  
  57. .mousemenumain ul li span{
  58.  
  59. margin:0px 15px;
  60. display:block;
  61. }

控制菜单弹出位置的JS程序:

  1. document.oncontextmenu = ShowMenu;
  2.  
  3. function ShowMenu(ev){
  4.  
  5.  var obj=document.getElementById('mousemenu');
  6.  obj.style.display='none';
  7.  ev = ev || window.event;
  8.  
  9.  var showplace=GetMousexy(ev);
  10.  obj.style.top=showplace.y+"px";
  11.  
  12.  obj.style.left=showplace.x+"px";
  13.  
  14.  var menuplaceX=parseInt(obj.style.left)+parseInt(obj.style.width)+document.body.scrollLeft; //获得菜单右边缘的坐标
  15.  
  16.  var j=0;
  17.  for(var i=0;i<document.getElementById('allitems').childNodes.length;i++){  //获得菜单项的数量
  18.  
  19.   if(document.getElementById('allitems').childNodes.item(i).tagName=="LI"){j++;}
  20.  
  21.  }
  22.  
  23.  var menuplaceY=parseInt(obj.style.top)+20*j-document.body.scrollTop; //获得菜单下边缘的坐标
  24.  
  25.  if(menuplaceX>document.body.clientWidth){  //如果菜单右边缘超出浏览器边框,则让菜单出现在鼠标左侧
  26.    obj.style.left=(showplace.x-parseInt(obj.style.width)-26)+"px";
  27.  
  28.  }
  29.  if(menuplaceY>document.documentElement.clientHeight){ //如果菜单下边缘超出浏览器边框,则让菜单出现在鼠标上侧
  30.  
  31.    obj.style.top=(showplace.y-20*j)+"px";
  32.  
  33.  }
  34.  
  35.  obj.style.display='';
  36.  return false//阻止原来的右键菜单出现
  37.  
  38. }
  39.  
  40. function GetMousexy(ev){  //获取鼠标坐标
  41.  if(ev.pageX || ev.pageY){
  42.  
  43.            return {x:ev.pageX, y:ev.pageY}//适用于FIREFOX
  44.  
  45.         } return {
  46.            x:ev.clientX + document.body.scrollLeft - document.body.clientLeft//适用于IE
  47.  
  48.            y:ev.clientY + document.body.scrollTop  - document.body.clientTop
  49.  
  50.         }
  51. }

需要注意的就是不能让菜单超出浏览器边缘,产生滚动条。

最后的效果:点这里

但是这样做的话,就必须把菜单的HTML代码写在网页面文件里,不能随时调用,如果一个页面里会出现多个菜单的话,就要每个都写一套HTML代码。如果用JS来创建菜单,并且把CSS和程序都封装到一个类里面,每次要用菜单的时候就只需要写很少的代码创建一个实例,虽然第一次比较麻烦,但以后就很方便了:

  1. /**********************************************
  2. *   Name: context menu class
  3. *   Author: Dexter.Yy (dexter.yy@gmail.com)
  4. *   Author URI: http://www.limboy.com/
  5. *   Version: 2006.10.22
  6. **********************************************/ 
  7. function RightMenu()
  8. {
  9. var MainMenu;     //主菜单
  10.  
  11.    var InLineUl;
  12. var MenuItem;   //菜单项
  13.  
  14. this.MainMenuPosition = "absolute";
  15. this.MainMenuLeftBackground = "url(leftbg.jpg) no-repeat"//主菜单右边文字
  16.  
  17. this.MainMenuWidth = "200px";
  18. this.MainMenuHeight = "0px";
  19. this.MainMenuRightBackground = "url(tempbg.jpg)";   //主菜单内容部分的背景
  20.  
  21. this.createMainMenu();
  22.  
  23. }
  24.  
  25. RightMenu.prototype.createMainMenu = function()   //创建主菜单
  26.  
  27. {
  28. this.InLineUl = document.createElement("ul");
  29.  
  30. this.InLineUl.style.padding = "0px";
  31. this.InLineUl.style.margin = "0px 0px 0px 0px";
  32.  
  33. this.InLineUl.style.listStyle = "none";
  34. this.InLineUl.style.width = "100%";
  35.  
  36. this.InLineUl.id="allitems";
  37. InLineDiv = document.createElement("div");
  38.  
  39. InLineDiv.style.width = "100%";   //整个菜单的样式
  40.  
  41.  InLineDiv.style.margin = "0px 5px 0px 22px";
  42. InLineDiv.style.background = this.MainMenuRightBackground;
  43.  
  44. InLineDiv.style.border = "2px solid #0033ff";
  45. InLineDiv.style.borderLeft = "none";
  46.  
  47. InLineDiv.appendChild(this.InLineUl.cloneNode(true));
  48. this.MainMenu = document.createElement("div");
  49.  
  50. this.MainMenu.style.position = this.MainMenuPosition;
  51. this.MainMenu.style.background = this.MainMenuLeftBackground;
  52.  
  53. this.MainMenu.style.width = this.MainMenuWidth;
  54. this.MainMenu.appendChild(InLineDiv);
  55.  
  56. this.MainMenu.style.display = "none";
  57. document.body.appendChild(this.MainMenu);
  58.  
  59.  
  60.  
  61. }
  62.  
  63. RightMenu.prototype.appendMenuItem = function(MenuItems)   //添加菜单项
  64.  
  65. {
  66.  
  67. if(MenuItems=='')return;
  68.  
  69. if(this.MainMenu.firstChild.firstChild)
  70.  
  71. {
  72.  this.MainMenuHeight = "0px";
  73.     this.MainMenu.firstChild.replaceChild(this.InLineUl.cloneNode(true),this.MainMenu.firstChild.firstChild);
  74.  
  75. }
  76.  
  77. for(var item in MenuItems)
  78. {
  79.  
  80.  
  81.  MenuItemSpan = document.createElement("span");
  82.  MenuItemSpan.style.margin = "0px 15px";
  83.  
  84.  MenuItemSpan.style.height = "28px";
  85.  MenuItemSpan.style.lineHeight = "28px";
  86.  
  87.  MenuItemSpan.style.display = "block";
  88.        MenuItemSpan.innerHTML = MenuItems[item][0];
  89.  
  90.  MenuItemSpan.setAttribute("hasmenu",1);
  91.  MenuItemSpan.oncontextmenu = function(){return false;}
  92.  
  93.  MenuItemA = document.createElement("a");
  94.  MenuItemA.setAttribute('href',MenuItems[item][2]);
  95.  
  96.  MenuItemA.target = MenuItems[item][1];
  97.  MenuItemA.onmouseout = function()    //每个菜单项的样式
  98.  
  99.  {
  100.   this.style.background = "";
  101.   this.style.display = "block";
  102.  
  103.   this.style.textDecoration = "none";
  104.   this.style.color = "#000000";
  105.  
  106.   this.style.margin = "0px 0px 0px 0px";
  107.   this.style.width = "200px";
  108.  
  109.   this.style.height = "28px";
  110.   this.style.lineHeight = "28px";
  111.  
  112.   this.style.fontSize = "12px";
  113.   this.style.overflowX = "hidden";
  114.  
  115.  }
  116.  MenuItemA.onmouseout();
  117.  MenuItemA.onmouseover = function()   //鼠标滑过时菜单项的样式
  118.  
  119.  {
  120.   this.style.background = "#0000ee";
  121.  
  122.   this.style.border = "0px solid #00ccff";
  123.   this.style.textDecoration = "none";
  124.  
  125.   this.style.color = "#fff";
  126.   this.style.cursor = "pointer";
  127.  
  128.  }
  129.  MenuItemA.appendChild(MenuItemSpan);
  130.  MenuItemLi = document.createElement("li");
  131.  
  132.  MenuItemLi.style.width = "100%";
  133.  
  134.  MenuItemLi.appendChild(MenuItemA);
  135.  
  136.  this.MainMenuHeight =(parseInt(this.MainMenuHeight)+parseInt(MenuItemSpan.style.height))+"px";
  137.  
  138.  this.MainMenu.firstChild.firstChild.appendChild(MenuItemLi);
  139.  
  140.  
  141. }
  142.  
  143. }
  144.  
  145. RightMenu.prototype.GetMousexy = function(ev)
  146. {
  147.  
  148. if(ev.pageX || ev.pageY)
  149. {
  150.  
  151.       return {x:ev.pageX, y:ev.pageY};
  152.  
  153.    }
  154. return {
  155.      x:ev.clientX + document.body.scrollLeft - document.body.clientLeft,
  156.  
  157.      y:ev.clientY + document.body.scrollTop  - document.body.clientTop
  158.  
  159.    }
  160. }
  161.  
  162. RightMenu.prototype.ShowMenu = function(ev,MenuItems)
  163.  
  164. {
  165. this.appendMenuItem(MenuItems);
  166. this.MainMenu.style.display='none';
  167.  
  168. ev = ev || window.event;
  169. var showplace=this.GetMousexy(ev);
  170.  
  171. this.MainMenu.style.top=showplace.y+"px";
  172.  
  173. this.MainMenu.style.left=showplace.x+"px";
  174.  
  175.  
  176. var menuplaceX=parseInt(this.MainMenu.style.left)+parseInt(this.MainMenu.style.width)+document.body.scrollLeft;
  177.  
  178. if(menuplaceX>document.body.clientWidth)
  179. {
  180.  this.MainMenu.style.left=(showplace.x-parseInt(this.MainMenu.style.width)-26)+"px";
  181.  
  182. }
  183.  
  184. var menuplaceY=parseInt(this.MainMenu.style.top)+parseInt(this.MainMenuHeight)-document.body.scrollTop;
  185. if(menuplaceY>document.documentElement.clientHeight)
  186.  
  187. {
  188.  this.MainMenu.style.top=(showplace.y-parseInt(this.MainMenuHeight))+"px";
  189.  
  190. }
  191. this.MainMenu.style.display='';
  192. return false;
  193.  
  194. }

调用这个类的方法是……首先创建菜单的对象,在window.onload = function(){}或body onload=”"里加上:

  1. YYmenu = new RightMenu();

注意不能写作var YYmenu = new RightMenu();那样YYmenu就不是全局的了……

接下来,根据实际需要,设置菜单的具体项目,写成一个数组:[”菜单项的名称”,”打开方式”,”点击的事件”],比如:

  1. var b=[  //设置菜单b的具体项目
  2. ["菜单b菜单b1","_blank","javascript:alert('hahahaha')"],
  3.  
  4. ["菜单b菜单b2","","javascript:alert('xixixixi')"],
  5. ["菜单b菜单b3","","javascript:alert('xixixixi')"],
  6.  
  7. ["菜单b菜单b4","","javascript:alert('xixixixi')"],
  8. ["菜单b菜单b5","","javascript:alert('xixixixi')"],
  9.  
  10. ["菜单b菜单b6","","javascript:alert('xixixixi')"]
  11. ];

需要几种样式的菜单,就建立几个数组。然后,给需要右键弹出菜单的页面元素加上oncontextmenu事件和自定义属性“hasmenu”(如果没有覆盖整个页面的全局右键菜单,就不用加这个属性), 比如:

  1. <div oncontextmenu="return YYmenu.ShowMenu(event,b)" hasmenu="1">这是需要右键弹出菜单的元素</div>

参数“b”就是包含菜单项目的那个数组。

如果想要在整个页面任何地方点右键都可以弹出一个通用的菜单,需要像这些调用:

  1. if (window.ActiveXObject){
  2.  document.oncontextmenu=function(yy)  //适用于IE
  3.  
  4.     {   //alert(event.srcElement.nodeName);
  5.      if (event.srcElement.getAttribute("hasmenu")!="1"){
  6.  
  7.       return YYmenu.ShowMenu(yy,a);
  8.          }
  9.     } 
  10. }else{
  11.  
  12.  document.oncontextmenu=function(event)  //适用于FIREFOX
  13.     {  
  14.      if (event.target.getAttribute("hasmenu")!="1"){
  15.  
  16.       return YYmenu.ShowMenu(event,a);
  17.          }
  18.     }
  19.  
  20. }

这里我处理的不太好,IE和FIREFOX在获取鼠标当前目标时,有一些差异还没完全理解……

最后的效果:点这里

这个javascript类的源文件:点这里

菜单的外观我就没时间弄了,如果你想把菜单做成本文左上角那副图里的效果,只要换一张背景图片,并且给每个LI加上背景就行了……

- 分类: JavaScript, 界面

    发表评论

(一定要填写)

(一定要填写, 不会公开喔!)

GOSPEL OF YY

    There is no emotion, there is peace.
    There is no ignorance, there is knowledge.
    There is no passion, there is serenity.
    There is no chaos, there is harmony.
    There is no death, there is the Force.

    --
    The Jedi code