在部落格園博文中新增自定義右鍵選單的方法詳解
頁面設計
首先將這三個功能以一個列表<ul>的形式放置。滑鼠移入時樣式改變,移出時還原
<style> body{margin: 0;} ul{ margin: 0; padding: 0; list-style: none; } .list{ width: 100px; text-align: center; cursor: pointer; font:20px/40px '宋體'; background-color: #eee; } .in:hover{ background-color: lightblue; color: white; font-weight:bold; } </style> <ul id="list" class="list"> <li class="in">頂部</li> <li class="in">點贊</li> <li class="in">評論</li> </ul>
選單邏輯
選單邏輯共包括阻止預設行為、顯隱效果和位置判斷三個部分
預設行為
通常,點選右鍵時,會彈出瀏覽器的預設右側選單
通過return false可以實現阻止預設行為的效果,當然也可以使用preventDefault()和returnValue,詳細資訊移步至此
document.oncontextmenu = function(){ return false; }
顯隱
右鍵選單預設隱藏,點選右鍵時顯示,點選左鍵時再隱藏
關於元素顯隱,個人總結過共9種思路,本文就用最簡單的display屬性
<div id="test" style="height: 100px;width: 100px;background-color: pink;"></div> <script> document.onclick = function(){ test.style.display = 'none'; } document.oncontextmenu = function(){ test.style.display = 'block'; return false; } </script>
位置判斷
滑鼠物件共有6對座標位置資訊,若把右鍵選單設定為fixed固定定位,則選擇clientX/Y即可
一般地,右鍵選單的左上角位置應該是當前滑鼠的座標處
但是,還有另外2種情況需要考慮
【1】如果滑鼠的位置到視口底部的位置小於選單的高度,則滑鼠的位置為選單的底部位置
【2】如果滑鼠的位置到視口右側的位置小於選單的寬度,則視口的右側為選單的右側
元素的尺寸資訊共有偏移尺寸offset、可視區尺寸client和滾動尺寸scroll,此時選單的寬高應該為偏移尺寸offsetWidth/offsetHeight(全尺寸包含width、padding、border)
<div id="test" style="position:fixed;height: 100px;width: 100px;background-color: pink;"></div> <script> document.onclick = function(){ test.style.display = 'none'; } document.oncontextmenu = function(e){ e = e || event; test.style.left = e.clientX + 'px'; test.style.top = e.clientY + 'px'; //注意,由於加法、減法的優先順序高於大於、小於的優先順序,所以不用加括號,詳細情況移步至此 if(document.documentElement.clientHeight - e.clientY < test.offsetHeight){ test.style.top = e.clientY - test.offsetHeight + 'px'; } if(document.documentElement.clientWidth - e.clientX < test.offsetWidth){ test.style.left = document.documentElement.clientWidth - test.offsetHeight + 'px'; } test.style.display = 'block'; return false; } </script>
功能實現
共用有回到頂部、點贊和評論三個功能需要實現
回到頂部
回到頂部共有5種實現方法,下面使用可讀寫的scrollTop屬性進行效果實現
<body style="height: 3000px;"> <button id="test" style="position:fixed;right:10px;bottom:10px;">回到頂部</button> <script> var timer = null; test.onclick = function(){ cancelAnimationFrame(timer); timer = requestAnimationFrame(function fn(){ var oTop = document.body.scrollTop || document.documentElement.scrollTop; if(oTop > 0){ document.body.scrollTop = document.documentElement.scrollTop = oTop - 160; timer = requestAnimationFrame(fn); }else{ cancelAnimationFrame(timer); } }); } </script> </body>
但是,上面的程式碼有一個問題,就是當頁面內容較多時,回到頂部的動畫效果將持續很長時間。因此,使用時間版的運動更為合適,假設回到頂部的動畫效果共運動500ms,則程式碼如下所示
<body style="height: 2000px;"> <button id="test" style="position:fixed;right:10px;bottom:10px;">回到頂部</button> <script> var timer = null; test.onclick = function(){ cancelAnimationFrame(timer); //獲取當前毫秒數 var startTime = +new Date(); //獲取當前頁面的滾動高度 var b = document.body.scrollTop || document.documentElement.scrollTop; var d = 500; var c = b; timer = requestAnimationFrame(function func(){ var t = d - Math.max(0,startTime - (+new Date()) + d); document.documentElement.scrollTop = document.body.scrollTop = t * (-c) / d + b; timer = requestAnimationFrame(func); if(t == d){ cancelAnimationFrame(timer); } }); } </script> </body>
點贊
點贊函式是部落格園自己寫的,我們看不到內部函式也無法使用。如果想在右鍵選單中使用點贊功能,就需要模擬點選事件。點選右鍵選單中的點贊項時,觸發部落格園的自帶的點贊項的click事件
由下圖可知,點贊函式加在<div class="diggit">上
由一個小例子來說明模擬點選事件如何實現
點選按鈕1時,顯示1;點選按鈕2時,也要實現同樣的功能
<button id="btn1">按鈕1</button> <button id="btn2">按鈕2</button> <div id="result" style="height: 30px;width: 100px;background-color: pink;"></div> <script> btn1.onclick= function(){ result.innerHTML += '1'; } btn2.onclick = btn1.onclick; </script> 如法炮製 <div id="test">點贊</div> <script> window.onload = function(){ test.onclick = document.getElementById('div_digg').children[0].onclick; } </script>
增加獲取最新點贊數的功能
當id為'menuFavour'的div元素被點選時,更新點贊數。但,由於從伺服器獲取最新資料以及相關元素的內容發生變化,都需要時間,所以增加2秒的延遲
<div id="menuFavour">點贊(<span id="favourNum">0</span>贊)</div> <script> //模擬原始點贊按鈕的點選事件 menuFavour.onclick = document.getElementById('div_digg').children[0].onclick; //獲取贊成數的函式 function getfavourNum(){ favourNum.innerHTML = document.getElementById('digg_count').innerHTML; } //頁面載入時獲取贊成數 getfavourNum(); //點選選單中的贊成項後,再獲取最新的贊成數 menuFavour.addEventListener('click',function(){ setTimeout(function(){ getfavourNum(); },2000); }) </script>
評論
點選右鍵選單中的評論項時,頁面定位到評論區的位置
由圖中可知,評論區為<div id="comment_form_container">
將元素置於可視區域內有很多方法,如scrollTo()、scrollBy()、通過scrollTop計算、scrollIntoView()方法等等,詳細情況移步至此
下面利用scrollIntoView()方法滾動當前元素,進入瀏覽器的可見區域
<div id="test">評論</div> <script> window.onload = function(){ test.onclick = function(){ document.getElementById('comment_form_container').scrollIntoView(); } } </script>
完整原始碼
將HTML結構和CSS樣式寫成javascript生成的行為,最終形成一份js檔案,程式碼如下
//requestAnimationFrame相容寫法 if(!window.requestAnimationFrame){ var lastTime = 0; window.requestAnimationFrame = function(callback){ var currTime = new Date().getTime(); var timeToCall = Math.max(0,16.7-(currTime - lastTime)); var id = window.setTimeout(function(){ callback(currTime + timeToCall); },timeToCall); lastTime = currTime + timeToCall; return id; } } if (!window.cancelAnimationFrame) { window.cancelAnimationFrame = function(id) { clearTimeout(id); }; } //事件處理程式相容寫法 function addEvent(target,type,handler){ if(target.addEventListener){ target.addEventListener(type,handler,false); }else{ target.attachEvent('on'+type,function(event){ return handler.call(target,event); }); } } /*******生成元素*******/ var list = document.createElement('ul'); list.id = 'list'; list.innerHTML = '<li id="menuTop">回到頂部</li>\ <li id="menuFavour">點贊(<span id="favourNum">0</span>贊)</li>\ <li id="menuCommand">評論</li>'; document.body.appendChild(list); /*******新增樣式**********/ function loadStyles(str){ var style = document.createElement("style"); style.type = "text/css"; try{ style.innerHTML = str; }catch(ex){ style.styleSheet.cssText = str; } var head = document.getElementsByTagName('head')[0]; head.appendChild(style); } loadStyles("#list{margin: 0!important;\ padding: 0!important;\ width: 120px;\ text-align: center;\ cursor: pointer;\ font:20px/40px '宋體';\ background-color: #eee;\ position:fixed;\ display:none;}\ #list li{list-style:none!important;}\ #list li:hover{background-color: lightblue;color: white;font-weight:bold;}"); //DOM結構穩定後,再操作 addEvent(window,'load',contextMenuLoad); function contextMenuLoad(){ /********顯示和隱藏選單***********/ addEvent(document,'click',function(){ list.style.display = 'none'; }) //右鍵點選時,選單顯示 document.oncontextmenu = function(e){ e = e || event; //通常情況下,選單的位置就是滑鼠的位置 list.style.left = e.clientX + 'px'; list.style.top = e.clientY + 'px'; //當滑鼠的位置到視口底部的位置小於選單的高度,則滑鼠的位置為選單的底部位置 if(document.documentElement.clientHeight - e.clientY < list.offsetHeight){ list.style.top = e.clientY - list.offsetHeight + 'px'; } //當滑鼠的位置到視口右側的位置小於選單的寬度,則視口的右側為選單的右側 if(document.documentElement.clientWidth - e.clientX < list.offsetWidth){ list.style.left = document.documentElement.clientWidth - list.offsetHeight + 'px'; } list.style.display = 'block'; //點選右鍵的同時按下ctrl鍵,那麼將顯示預設右鍵選單 if(e.ctrlKey){ list.style.display = 'none'; //如果只是點選右鍵,則顯示自定義選單 }else{ return false; } } /*********回到頂部功能*********/ var timer = null; menuTop.onclick = function(){ cancelAnimationFrame(timer); //獲取當前毫秒數 var startTime = +new Date(); //獲取當前頁面的滾動高度 var b = document.body.scrollTop || document.documentElement.scrollTop; var d = 500; var c = b; timer = requestAnimationFrame(function func(){ var t = d - Math.max(0,startTime - (+new Date()) + d); document.documentElement.scrollTop = document.body.scrollTop = t * (-c) / d + b; timer = requestAnimationFrame(func); if(t == d){ cancelAnimationFrame(timer); } }); }; /*********點贊功能**********/ //模擬原始點贊按鈕的點選事件 var digg = document.getElementById('div_digg'); if(digg){ menuFavour.onclick = digg.children[0].onclick; } //獲取贊成數的函式 function getfavourNum(){ if(digg){ favourNum.innerHTML = digg.children[0].children[0].innerHTML; } } //頁面載入時獲取贊成數 getfavourNum(); if(menuFavour.addEventListener){ menuFavour.addEventListener('click',function(){ setTimeout(function(){ getfavourNum(); },2000); }) }else{ menuFavour.attachEvent('onclick',2000); }) } /*********評論功能*********/ menuCommand.onclick = function(){ document.getElementById('comment_form_container').scrollIntoView(); } }
更多關於在部落格園中新增程式碼的文章請點選下面的相關連結