js實(shí)現(xiàn)自定義contextmenu

鼠標(biāo)大家每天都在用,右鍵單擊鼠標(biāo)后會(huì)出現(xiàn)一個(gè)菜單,下面的文字就嘗試實(shí)現(xiàn)這個(gè)功能,效果如下圖:

效果圖

為了實(shí)現(xiàn)這個(gè)功能,分解一下我們需要做的事情:

  • document對(duì)象監(jiān)聽鼠標(biāo)點(diǎn)擊事件
  • 觸發(fā)鼠標(biāo)點(diǎn)擊事件猜憎,獲取鼠標(biāo)當(dāng)前所在瀏覽器的位置
  • 根據(jù)當(dāng)前位置調(diào)整contextmenu的顯示坐標(biāo)
  • 顯示contextmenu

接下來(lái)一步步實(shí)現(xiàn)每個(gè)過(guò)程。

監(jiān)聽何種事件類型

click ? keydown搔课,keypress胰柑,keyup ? mousedown,mouseup ?

  • 首先:不能使用click事件辣辫,DOM event3規(guī)范已經(jīng)明確指出:

Note: click事件應(yīng)該僅能夠被鼠標(biāo)左鍵觸發(fā)旦事,鼠標(biāo)中鍵和右鍵不應(yīng)該觸發(fā)click事件. DOM-Level-3-Events

具體到瀏覽器實(shí)現(xiàn)上:ie各個(gè)版本和chrome是一致的,均不支持鼠標(biāo)右鍵觸發(fā)click事件急灭,firefox實(shí)現(xiàn)了一個(gè)折中的方案姐浮,對(duì)于document元素支持鼠標(biāo)右鍵觸發(fā)click事件,其他元素則不觸發(fā)click事件葬馋。

原因:鼠標(biāo)右鍵以及中鍵支持click事件容易造成click事件的混淆卖鲤,試想一下,我們編寫的絕大多數(shù)click事件處理函數(shù)中畴嘶,并沒(méi)有對(duì)鼠標(biāo)按鍵進(jìn)行區(qū)分蛋逾,但我們默認(rèn)的是鼠標(biāo)左鍵觸發(fā),因此使用鼠標(biāo)右鍵是未預(yù)料的行為窗悯,詳細(xì)信息可以參考這里

  • keydown区匣,keypress,keyup這三個(gè)事件也不能使用蒋院,這三個(gè)是鍵盤事件亏钩,不能捕獲到鼠標(biāo)事件莲绰,

  • 最終我們選擇的事件就是mousedown和mouseup,在mousedown事件中清除頁(yè)面中已經(jīng)出現(xiàn)的contextmenu姑丑,在mouseup事件中顯示contextmenu

  • 另一個(gè)需要考慮的問(wèn)題是蛤签,如何阻止鼠標(biāo)右鍵單擊的默認(rèn)行為,你可能嘗試在mouseup事件中阻止事件默認(rèn)行為栅哀,但這是沒(méi)用的震肮,因?yàn)橛|發(fā)contextmenu菜單的事件并不是mouseup,而是contextmenu事件留拾,所以要阻止瀏覽器鼠標(biāo)右鍵單擊事件可以這樣寫
    document.oncontextmenu = function(){
    return false;
    }
    這里也不必?fù)?dān)心兼容性問(wèn)題戳晌,各個(gè)瀏覽器對(duì)contextmenu事件支持的很好,關(guān)于stopPropagation, preventDefault 和 return false 的區(qū)別痴柔,可以參考這篇文章

獲取鼠標(biāo)位置

chrome瀏覽器的event對(duì)象提供了下面幾組鼠標(biāo)位置坐標(biāo)

  • clientX/clientY
  • layerX/layerY
  • offsetX/offsetY
  • pageX/pageY
  • screenX/screenY
  • webkitMovementX/webkitMovementY
  • x/y

IE8不支持pageX/Y躬厌,ff提供了mozMovementX/Y,但是ff沒(méi)有x/y這一組坐標(biāo)竞帽。
真夠多的,選擇哪一組呢鸿捧?建議先閱讀下面的文章屹篓,中文翻譯原文

  • clientX/Y 提供了相對(duì)于viewport的以CSS像素度量的坐標(biāo)
  • pageX/Y提供了相對(duì)于<html>元素的以CSS像素度量的坐標(biāo)
  • screenX/Y提供了相對(duì)于電腦屏幕的以設(shè)備像素進(jìn)行度量的坐標(biāo)
  • offsetX/offsetY提供了相對(duì)于父容器的以CSS像素度量的坐標(biāo)
  • layerX/layerY提供了相對(duì)于absolute匙奴,relative元素的坐標(biāo)堆巧,如不存在,則相對(duì)于<html>元素泼菌。

選哪一組值現(xiàn)在就一目了然了谍肤。

    (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
發(fā)現(xiàn)一個(gè)BUG:我們的contextmenu并沒(méi)有檢查瀏覽器的邊界,系統(tǒng)自帶的功能是下面這樣的:

系統(tǒng)自帶

邊界檢查

為了做邊界檢查哗伯,需要知道哪些參數(shù)荒揣?

  • 鼠標(biāo)相對(duì)于瀏覽器視口的坐標(biāo)
  • contextmenu的寬度和高度
  • 瀏覽器視口的寬度和高度

獲取鼠標(biāo)的位置跟之前是一樣的

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
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市焊刹,隨后出現(xiàn)的幾起案子系任,更是在濱河造成了極大的恐慌,老刑警劉巖虐块,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俩滥,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡贺奠,警方通過(guò)查閱死者的電腦和手機(jī)霜旧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)儡率,“玉大人挂据,你說(shuō)我怎么就攤上這事以清。” “怎么了棱貌?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵玖媚,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我婚脱,道長(zhǎng)今魔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任障贸,我火速辦了婚禮错森,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘篮洁。我一直安慰自己涩维,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布袁波。 她就那樣靜靜地躺著瓦阐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪篷牌。 梳的紋絲不亂的頭發(fā)上睡蟋,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音枷颊,去河邊找鬼戳杀。 笑死,一個(gè)胖子當(dāng)著我的面吹牛夭苗,可吹牛的內(nèi)容都是我干的信卡。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼题造,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼傍菇!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起界赔,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤桥嗤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后仔蝌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泛领,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年敛惊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了渊鞋。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锡宋,靈堂內(nèi)的尸體忽然破棺而出儡湾,到底是詐尸還是另有隱情,我是刑警寧澤执俩,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布徐钠,位于F島的核電站,受9級(jí)特大地震影響役首,放射性物質(zhì)發(fā)生泄漏尝丐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一衡奥、第九天 我趴在偏房一處隱蔽的房頂上張望爹袁。 院中可真熱鬧,春花似錦矮固、人聲如沸失息。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)盹兢。三九已至,卻和暖如春守伸,著一層夾襖步出監(jiān)牢的瞬間蛤迎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工含友, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人校辩。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓窘问,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親宜咒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子惠赫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 總結(jié): 鼠標(biāo)事件 1.click與dbclick事件$ele.click()$ele.click(handler(...
    阿r阿r閱讀 1,598評(píng)論 2 10
  • 事件流 IE和Netscape開發(fā)團(tuán)隊(duì)提出了完全相反的兩種事件流的概念,事件冒泡流和事件捕獲流故黑。 事件冒泡 事件由...
    exialym閱讀 928評(píng)論 0 9
  • 13.1 事件流 “DOM2級(jí)事件”規(guī)定事件流包括3個(gè)階段:事件捕獲階段场晶,處于目標(biāo)階段混埠,事件冒泡階段。事件捕獲表示...
    Elevens_regret閱讀 416評(píng)論 0 0
  • 第1章 鼠標(biāo)事件 1-1 jQuery鼠標(biāo)事件之click與dbclick事件 用交互操作中诗轻,最簡(jiǎn)單直接的操作就是...
    mo默22閱讀 1,263評(píng)論 0 6
  • Android靜態(tài)代碼分析 [TOC] 最佳項(xiàng)目里面來(lái)了很多新的小伙伴钳宪,然后每個(gè)人的代碼風(fēng)格還不一樣,雖然有代碼風(fēng)...
    流水不腐小夏閱讀 2,276評(píng)論 0 5