給力的右鍵選單
其中有幾點是我這近半年才發現的。
Scope Chain
Scope就是一般編程都必讀的一環,亦是經常讓人頭痛的東西。簡單來說就是:
var yard = "Tommy";
function house()
{
var bedroom = 'John';
alert(yard); // Tommy
}
// bedroom不能從外面存取
alert(bedroom); // undefined當然,這沒什麼大不了的,這是Scope的基本,犯不著我多說。
至於所謂的Scope Chain,就是Scope的連鎖。特點在於多個Scope環環相扣,就像骨牌一樣。
這個概念是我正煩惱要怎麼做一個右鍵選單時苦出來的。
為什麼我得自己另外苦一個出來呢?網絡上不是經已有很多類似的東西了?你憑什麼會覺得另起爐灶會做得比人家更好?
嘛第一點當然是我們的宗旨是做一個自家的API,第二點則是我煩惱了很久——覺得現今大部分的選單都無法做到我的要求(甚至是jQuery的ContextMenu)。
我們做用的GUI主要靠眼睛來接收這些圖像,而我們大腦處理這些影像的方式有一套特定的模式。我認為這點很重要,不然用戶使用起來便會感到挫折,大大打擊資訊傳遞的效率。
所以右鍵選單得配合大腦的運作模式,(這就是我覺得Windows做得好的地方。各種方面都很「brain-friendly」) 他的特點就是適當的停頓1以及選單的保留2。
以下是這個blog所使用的模組(感謝微軟,其實很多方面我都是直接參考Windows的做法。):
Right click
右鍵點擊
當然現在還是有些缺陷(例如頁面捲動時會跟隨畫面),但在使用方面我覺得已經能乎合我的基準了(笑)。
好了,話題扯遠了。回到技術方面的討論:
以下是我所使用的編碼:
(function() {
var writeLine = function(e)
{
alert('Clicked');
return false;
};
new ContextMenu($('my_test_obj'), [
{
name: "Copy"
, items: [
new EventKey("Copy a random number", writeLine)
, new EventKey("Copy a random character", writeLine)
]
}
, {
name: "Group1"
, items: [
{
name: "SubGroup1"
, items: [
new EventKey("Subitem1", writeLine)
, new EventKey("Subitem2", writeLine)
, new EventKey("Subitem3", writeLine)
]
}
,{
name: "SubGroup2"
, items: [
new EventKey("SubsubItem4", writeLine)
, new EventKey("SubsubItem5", writeLine)
]
}
, new EventKey("Item6", writeLine)
]
}
, new EventKey("Item1", writeLine)
, new EventKey("Item2", writeLine)
, new EventKey("Item3", writeLine)
, new EventKey("Item4", writeLine)
], 'RMB');
})();哈哈,當然這只是鍵單定義罷了(源碼很長。我會在文章最後附上)。
其實要做到這兩點,難度大大增加了。加上我自問組織能力不夠強,所以這花了我兩天的時間做呢。
抱歉我的塗鴉太難看了(哭)。
要解釋可能有點難度:由於選單的保留這一點,當滑鼠離開並於空白地方點擊時,選單得重設。
但是,選單已經打開了,在html方面就是這樣的狀態:
<ul>
<ul style="display:block;">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<ul style="display: block;">
<li>Sub Item 1</li>
<ul style="display: block;">Sub Item 1</ul>
</ul>
<li> ...
.
.
.
</ul>
.
.
.
</ul>現在我的問題是:到底要怎樣才能有效地收束經已打開了的了選單呢?(將所有display: block截掉)
「一般」的右鍵選單因為沒有保留機制,每當滑鼠移出選項時,選單就立即跟著改變了,根本不會有這個問題存在。
這就是我得出Status Chain這個概念的來源了(鳴,對不起,又是我的塗鴉):
抽出模組(ContextMenu)核心:
.
.
.
stepSubListeners = function (target)
{
var lockedOn = null, j, overedOn
, nodes = target.childNodes
;
// Collapse menu chain, each chain is a route
// Like dominoes
target.chainRoute = target.chainRoute || [];
for(var i in nodes)
{
j = nodes[i];
if(!(j && j.nodeType == 1)) continue;
// If this item has a submenu item
if(j.lastChild.nodeType == 1)
{
target.chainRoute[target.chainRoute.length] = {menu: j.lastChild, next: stepSubListeners(j.lastChild)};
}
IDOMElement(j).addEventListener(
new EventKey("MouseOver", function ()
{
// record "this (overed)" item
overedOn = this;
var subItem = this.lastChild;
registerDelay(function ()
{
// Hide last displayed submenu if submenu is available
if(lockedOn && lockedOn != subItem)
{
_chainHide ? _chainHide(lockedOn) : (lockedOn.style.display = "none");
// bind the chains into chain reactor
chainReactor.bind(lockedOn)();
}
// if mouse is still on "this (overed)" item
if(overedOn == this)
{
// and if this item has sub item
if(subItem.nodeType == 1)
{
lockedOn = this.lastChild;
_chainShow ? _chainShow(subItem) : (subItem.style.display = "");
}
}
// 延遲300毫秒
}.bind(this), 300);
}.bind(j))
);
}
return target.chainRoute.length ? chainReactor.bind(target) : null;
}
, chainReactor = function ()
{
if(this && this.chainRoute)
for(var i in this.chainRoute)
{
_chainHide ? _chainHide(this.chainRoute[i].menu) : (this.chainRoute[i].menu.style.display = "none");
if(this.chainRoute[i].next) this.chainRoute[i].next();
}
}
.
.
.好吧,我不多說了。上面的編碼會為每個子選單項目創做一個觸發。每當選單收束時,會觸發一串檢查,而檢查過程中會再碰到觸發,於而做出連鎖反應(Chain Reaction)。
而stepSubListeners開頭的參數定義,會跟據項目的不同而改變,steuSubListeners的特點就是會自我呼叫(笑),因此參數的scope會變成巢狀(Nested Variable Scope)。這就是我命名為「Scope Chain」的原因。
附上源碼(注:只作參考,無法直接使用,但請大家隨便改吧。)
PS: 呼,第一次寫技術討論的文章呢。雖然我知道這些東西早就有人想過了,但我還是想將自己的發現分享出來啊。見笑了。
- 當滑鼠移到子選單時,會有大概300毫秒的延遲(默認動作),子選單才會彈出。而作為延遲的補正,當滑鼠按下的時候,子選單會立即彈出。
- 當子選單於滑鼠移出選單區域後,選單不會消失。
由於眼晴對突然的動態(未預期,不在用戶設想之下所發生的變動)比較敏感,以上兩點能避免了「突然的改變」,減低了分散我們注意力的機會。
Sat Jan 18 2014 02:00:49 GMT+0000 (Coordinated Universal Time)
Last modified: Sun Apr 10 2022 13:11:08 GMT+0000 (Coordinated Universal Time)
Comments
No comments here.
Do you even comment?
website:
Not a valid website
Invalid email format
Please enter your email
*Name:
Please enter a name
Submit
抱歉,Google Recaptcha 服務被牆掉了,所以不能回覆了