這個效果本身難度不大,主要在程式結構和擴充中下了些功夫,務求用起來更方便,能用在更多的地方。
程式特點
1,同一個提示框用在多個觸發元素時,只需一個執行個體;
2,顯示和隱藏分別有點擊方式和觸發方式選擇;
3,能設定延時顯示和隱藏;
4,有25種預設定位位置;
5,可在預設定位基礎上,再自訂定位;
6,可設定自適應視窗定位;
程式說明
【Tip對象】
Tip對象就是用來顯示提示資訊的容器,程式用Tip屬性工作表示。這個沒什麼要求,程式初始化時會對它進行一些設定。
首先進行下面設定:
複製代碼 代碼如下:var css = this._cssTip;
css.margin = 0;
css.position = "absolute"; css.visibility = "hidden";
css.display = "block"; css.zIndex = 99;
css.left = this._cssTip.top = "-9999px";
其中margin設為0是為了避免一些定位問題,用visibility來隱藏而不是display是因為程式需要擷取Tip的offsetWidth、offsetHeight,還需要設定left和top是避免因Tip佔位出現的捲軸。
因為Tip可能會在其他定位元素裡面,所以還要設兩個offset修正參數: 複製代碼 代碼如下:var iLeft = 0, iTop = 0, p = this.Tip;
while (p.offsetParent) {
p = p.offsetParent; iLeft += p.offsetLeft; iTop += p.offsetTop;
};
this._offsetleft = iLeft;
this._offsettop = iTop;
最後給Tip的mouseover加一個事件,具體後面再說明。
【觸發對象】
由於很多情況下都是一個Tip對應多個地方的提示,所以程式參考了Table排序的方式,添加了一個Add方法。
一個Tip執行個體化後,再用Add方法就可以對多個觸發元素分別添加觸發對象,程式中用_trigger屬性工作表示當前的觸發對象。
Add方法的一個必要參數是觸發元素,就是觸發顯示Tip的元素。
需要的話還可以用options參數,來自訂觸發對象的屬性,包括:
屬性: 預設值//說明 複製代碼 代碼如下:ShowType: "both",//顯示方式
HideType: "both",//隱藏方式
ShowDelayType: "touch",//顯示延遲方式
HideDelayType: "touch",//隱藏延遲方式
ShowDelay: 300,//顯示延時時間
HideDelay: 300,//隱藏延時時間
Fixed: {},//定位對象
onShow: function(){},//顯示時執行
onHide: function(){}//隱藏時執行
具體作用後面再說明,可以在程式初始化時修改這些預設值。
一個經典應用是在onShow中把Tip修改為各個觸發對象對應的內容。
此外還有Elem屬性儲存觸發元素。
【顯示和隱藏】
提示效果的一個重點就是顯示和隱藏提示資訊。程式是通過設定Tip的visibility是否hidden來顯示和隱藏Tip的。
具體的顯示和隱藏程式分別在Show和Hide程式中,還有ReadyShow和ReadyHide程式,主要用來處理延時。
這種提示效果的一個特點是滑鼠移動到Tip上時,會保持顯示狀態。
為了實現這個效果,給Tip的mouseover寫入程式:
this.Check(e.relatedTarget) && clearTimeout(this._timer);
其中Check程式是用來判斷relatedTarget是不外部元素,即滑鼠離開的元素是不是外部元素。
如果是外部元素,就說明當前是隱藏延時階段,那麼只要清除定時器來取消隱藏就可以了。
這裡的外部元素是指觸發元素和Tip對象本身及其內部元素以外的元素。
這個有點拗口,那再看看Check程式是怎麼判斷的就明白了: 複製代碼 代碼如下:return !this._trigger ||
!(
this.Tip === elem || this._trigger.Elem === elem ||
Contains(this.Tip, elem) || Contains(this._trigger.Elem, elem)
);
首先判斷_trigger是否存在,不存在的話說明是剛開始觸發,也看成是外部觸發。
存在的話再判斷傳遞過來的元素是不是Tip或觸發元素本身,最後再用Contains判斷判斷是不是在Tip或觸發元素內部。
ps:關於Contains請參考這裡的比較文檔位置。
這樣得到的是判斷是否內部元素,最後取反就是判斷是否外部元素了。
【點擊方式】
點擊方式顯示是指點擊觸發元素的時候顯示Tip。
在Add程式中會給觸發元素的click事件綁定以下程式: 複製代碼 代碼如下:addEvent(elem, "click", BindAsEventListener(this, function(e){
if ( this.IsClick(trigger.ShowType) ) {
if ( this.CheckShow(trigger) ) {
this.ReadyShow(this.IsClick(trigger.ShowDelayType));
} else {
clearTimeout(this._timer);
};
};
}));
首先根據ClickShow判斷是否進行點擊顯示,再用CheckShow檢測是否同一個觸發對象。
CheckShow程式是這樣的: 複製代碼 代碼如下:if (trigger !== this._trigger) {
this.Hide(); this._trigger = trigger; return true;
} else { return false; };
如果不是同一個觸發對象,就先執行Hide清理前一個觸發對象,防止衝突,再執行ReadyShow來顯示。
如果是同一個觸發對象,就說明當前是延時隱藏階段,清除定時器保持顯示狀態就行了。
對應的,點擊方式隱藏是指點擊外部元素的時候隱藏Tip。
在ReadyShow裡,當使用點擊方式隱藏時,就會把_fCH綁定到document的click事件裡:
this.IsTouch(trigger.HideType) && addEvent(this._trigger.Elem, "mouseout", this._fTH);
注意這裡要把隱藏綁定事件放到ReadyShow,而不是Show裡面,因為延時的時候有可能還沒有顯示就觸發了隱藏事件。
其中_fCH是在初始化時定義的一個屬性,用於添加和移除點擊隱藏事件: 複製代碼 代碼如下:this._fCH = BindAsEventListener(this, function(e) {
if (this.Check(e.target) && this.CheckHide()) {
this.ReadyHide(this.IsClick(this._trigger.HideDelayType));
};
});
注意不同於點擊顯示,由於綁定的是document,隱藏前要先確定e.target是不是外部元素。
其中CheckHide是作用是檢查Tip當前是不是隱藏狀態: 複製代碼 代碼如下:if (this._cssTip.visibility === "hidden") {
clearTimeout(this._timer);
removeEvent(this._trigger.Elem, "mouseout", this._fTH);
this._trigger = null;
removeEvent(document, "click", this._fCH);
return false;
} else { return true; };
如果本來就是隱藏狀態,清除定時器移除事件就行,不需要再執行Hide了。
【觸發方式】
觸發方式針對的是mouseover和mouseout,它的流程跟點擊方式是差不多的。
觸發方式顯示是指滑鼠從外部元素進入觸發元素(觸發mouseover)的時候顯示Tip。
在Add程式中會給觸發元素的mouseover事件綁定以下程式: 複製代碼 代碼如下:addEvent(elem, "mouseover", BindAsEventListener(this, function(e){
if ( this.IsTouch(trigger.ShowType) ) {
if (this.CheckShow(trigger)) {
this.ReadyShow(this.IsTouch(trigger.ShowDelayType));
} else if (this.Check(e.relatedTarget)) {
clearTimeout(this._timer);
};
};
}));
跟點擊方式類似,也需要執行一次CheckShow,但不同的是,還會用Check判斷e.relatedTarget是不是外部對象。
這是因為mouseover可能是從觸發元素的內部元素(包括Tip)進入或內部元素冒泡觸發的,而這些情況不需要任何操作。
對應的,觸發方式隱藏是指滑鼠從觸發元素或Tip離開時隱藏Tip。
當TouchHide為true時,在ReadyShow的時候會把_fTH綁定到觸發元素的mouseout事件裡:
this.IsTouch(trigger.HideType) && addEvent(this._trigger.Elem, "mouseout", this._fTH);
在Show的時候,再綁定到Tip的mouseout:
this.IsClick(trigger.HideType) && addEvent(document, "click", this._fCH);
在ReadyShow綁定的原因同上,而Tip只需顯示時綁定。
其中_fTH跟_fCH類似,也是在初始化時定義的一個屬性,用於添加和移除觸發隱藏事件: 複製代碼 代碼如下:this._fTH = BindAsEventListener(this, function(e) {
if (this.Check(e.relatedTarget) && this.CheckHide()) {
this.ReadyHide(this.IsTouch(this._trigger.HideDelayType));
};
});
不同的是mouseout在Check的時候是用e.relatedTarget。
【觸發原理】
上面是從程式的角度說明了觸發顯示和隱藏的過程,但要真正理解的話還需要做一次細緻的分析。
下面是以觸發方式的顯示隱藏為例做的流程圖:
下面是文字說明:
1,等待觸發顯示;
2,進入觸發元素,如果設定延時,跳到3,如果沒有設定延時,跳到4;
3,延時時間內,離開到外部元素,清除定時器,返回1,超過延時時間,跳到4;
4,執行顯示程式;
5,顯示Tip狀態;
6,離開觸發元素,如果是進入到Tip,跳到7,如果是離開到外部元素,跳到9;
7,保持顯示狀態;
8,離開Tip,如果是進入觸發元素,返回5,如果是離開到外部元素,跳到9;
9,如果設定延時,跳到10,如果沒有設定延時,跳到11;
10,延時時間內,如果進入Tip,清除定時器,返回7,如果進入觸發元素,清除定時器,返回5,超過延時時間,跳到11;
11,執行隱藏程式,返回1;
再對照程式,應該就能理解整個流程了,當然可能還不是那麼好理解。
這個流程也只是單例的情況,多例的時候還要多加一些判斷。
可以說這個流程看似不難,但如果想做一個最佳化的流程,那要考慮的細節地方可能會讓人受不了。
點擊方式跟觸發方式的流程是差不多的,而且更簡單,這裡就不重複了。
【元素定位】
完成了顯示隱藏,就到本程式另一個重點,元素定位。
程式使用一個GetRelative函數,通過定位元素、參考元素和參數對象來擷取形如{ Left: 100, Top: 200 }的定位參數結果。
計算結果結合了以下定位方式:預設定位,自訂定位,自適應定位。
觸發對象的Fixed屬性就是用來儲存定位參數對象的,包括一下屬性:
屬性: 預設值//說明
Align: "clientleft",//水平方向定位
vAlign: "clienttop",//垂直方向定位
CustomLeft: 0,//自訂left定位
CustomTop: 0,//自訂top定位
PercentLeft: 0,//自訂left百分比定位
PercentTop: 0,//自訂top百分比定位
Adaptive: false,//是否自適應定位
Reset: false//自適應定位時是否重新置放
下面再看看如何通過這些屬性設定定位。
【預設定位和自訂定位】
預設定位的意思是使用程式25個預設位置來定位。
25個位置是怎麼來的呢?看下面的具體示範: