让前端飞前端开发

js实现自定义contextmenu

2014-10-18  本文已影响8111人  江枫

鼠标大家每天都在用,右键单击鼠标后会出现一个菜单,下面的文字就尝试实现这个功能,效果如下图:

效果图

为了实现这个功能,分解一下我们需要做的事情:

接下来一步步实现每个过程。

监听何种事件类型

click ? keydown,keypress,keyup ? mousedown,mouseup ?

Note: click事件应该仅能够被鼠标左键触发,鼠标中键和右键不应该触发click事件. DOM-Level-3-Events

具体到浏览器实现上:ie各个版本和chrome是一致的,均不支持鼠标右键触发click事件,firefox实现了一个折中的方案,对于document元素支持鼠标右键触发click事件,其他元素则不触发click事件。

原因:鼠标右键以及中键支持click事件容易造成click事件的混淆,试想一下,我们编写的绝大多数click事件处理函数中,并没有对鼠标按键进行区分,但我们默认的是鼠标左键触发,因此使用鼠标右键是未预料的行为,详细信息可以参考这里

获取鼠标位置

chrome浏览器的event对象提供了下面几组鼠标位置坐标

IE8不支持pageX/Y,ff提供了mozMovementX/Y,但是ff没有x/y这一组坐标。
真够多的,选择哪一组呢?建议先阅读下面的文章,中文翻译原文

选哪一组值现在就一目了然了。

    (function(){
        var menu = document.createElement("div");
        menu.id="contextmenu";
        menu.className="hidden";
        document.body.appendChild(menu);
            
        bindEvent(document , "contextmenu" , closeContextMenu);
        bindEvent(document , "mouseup" , openNewContextMenu);
        bindEvent(document , "mousedown" , closeNewContextMenu);
            
        function closeContextMenu(){
            return false;
        }   
            
        function openNewContextMenu( ev ){
            ev = ev || window.event;
            var btn = ev.button;
            if( btn == 2){      
                menu.style.left = ev.clientX +"px";
                menu.style.top = ev.clientY +"px";
                menu.className = "show";
            } 
        }
    
        function closeNewContextMenu( ev ){
            menu.className = "hidden";
        }
    
        function bindEvent(elem , eventType , callback){
            var ieType = ["on" + eventType ];
            if( ieType in elem ){
                elem[ ieType ] = callback;
            }else if("attachEvent" in elem){
                elem.attachEvent(ieType ,callback);
            }else{
                elem.addEventListener(eventType ,callback , false);
            }
        }
  })();

//css
#contextmenu{
    width:180px;
    height: 240px;
    background-color:#f2f2f2;
    position:absolute;
    border:1px solid #BFBFBF;
    box-shadow:2px 2px 3px #aaaaaa;
}

.show{
    display:block;  
}

.hidden{
    display:none;   
}

效果图:

效果图

BUG
发现一个BUG:我们的contextmenu并没有检查浏览器的边界,系统自带的功能是下面这样的:

系统自带

边界检查

为了做边界检查,需要知道哪些参数?

获取鼠标的位置跟之前是一样的

x = ev.clientX ,
y = ev.clientY ;

获取浏览器视口的尺寸

document.documentElement.clientWidth;
document.documentElement.clientHeight;

获取contextmenu的宽度和高度

menu.offsetWidth
menu.offsetHeight  

最终的代码:

function getNextContextMenuPostion( ev ){
    var x = ev.clientX ,y = ev.clientY , 
    html = document.documentElement, vx = html.clientWidth , vy = html.clientHeight,
    mw =  menu.offsetWidth, mh =  menu.offsetHeight;
    return {
        left : (x + mw) > vx ? (vx - mw ) : x,
        top : (y + mh) > vy ? (vy - mh ) : y
    }
}
上一篇 下一篇

猜你喜欢

热点阅读