本來想做一個集合浮動定位和滑鼠跟隨的tooltips效果,但發現定位和滑鼠跟隨在一些關鍵的地方還是不同的,還是分開來吧。
這個效果本身難度不大,主要在程序結構和擴展中下了些功夫,務求用起來更方便,能用在更多的地方。
程式特點
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd ">
<html xmlns=" http://www.w3.org/1999/xhtml ">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<title>JavaScript 浮動定位提示效果</title>
<script>
var $$ = function (id) {
return "string" == typeof id ? document.getElementById(id) : id;
};
var isIE = navigator.userAgent.indexOf('MSIE') != -1;
var isIE6 = isIE && ([/MSIE (d).0/i.exec(navigator.userAgent)][0][1] == "6");
var isChrome = navigator.userAgent.indexOf('Chrome') != -1;
var isSafari = navigator.userAgent.indexOf('AppleWebKit') != -1;
// with input from Tino Zijdel, Matthias Miller, Diego Perini
// http://dean.edwards.name/weblog/2005/10/add-event/
function addEvent(element, type, handler) {
if (element.addEventListener) {
element.addEventListener(type, handler, false);
} else {
if (!handler.$$guid) handler.$$guid = addEvent.guid++;
if (!element.events) element.events = {};
var handlers = element.events[type];
if (!handlers) {
handlers = element.events[type] = {};
if (element["on" + type]) {
handlers[0] = element["on" + type];
}
}
handlers[handler.$$guid] = handler;
element["on" + type] = handleEvent;
}
};
addEvent.guid = 1;
function removeEvent(element, type, handler) {
if (element.removeEventListener) {
element.removeEventListener(type, handler, false);
} else {
if (element.events && element.events[type]) {
delete element.events[type][handler.$$guid];
}
}
};
function handleEvent(event) {
var returnValue = true;
event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event);
var handlers = this.events[event.type];
for (var i in handlers) {
this.$$handleEvent = handlers[i];
if (this.$$handleEvent(event) === false) {
returnValue = false;
}
}
return returnValue;
};
function fixEvent(event) {
event.target = event.srcElement;
if(event.type == "mouseout") {
event.relatedTarget = event.toElement;
}else if(event.type == "mouseover") {
event.relatedTarget = event.fromElement;
}
return event;
};
var Extend = function(destination, source) {
for (var property in source) {
destination[property] = source[property];
}
return destination;
}
var Contains = function(a, b){
return a.contains ? a != b && a.contains(b) : !!(a.compareDocumentPosition(b) & 16);
}
var Bind = function(object, fun) {
var args = Array.prototype.slice.call(arguments, 2);
return function() {
return fun.apply(object, args.concat(Array.prototype.slice.call(arguments)));
}
}
var BindAsEventListener = function(object, fun) {
var args = Array.prototype.slice.call(arguments, 2);
return function(event) {
return fun.apply(object, [event].concat(args));
}
}
var FixedTips = function(tip, options){
this.Tip = $$(tip);//提示框
this._trigger = null;//觸發對象
this._timer = null;//定時器
this._cssTip = this.Tip.style;//簡化程式碼
this._onshow = false;//記錄目前顯示狀態
this.SetOptions(options);
//處理Tip對象
this._cssTip.margin = 0;//避免定位問題
this._cssTip.position = "absolute";
this._cssTip.visibility = "hidden";
this._cssTip.display = "block";
this._cssTip.zIndex = 99;
this._cssTip.left = this._cssTip.top = "-9999px";//避免佔位出現捲軸
//offset修正參數
var iLeft = iTop = 0, p = this.Tip;
while (p.offsetParent) {
p = p.offsetParent; iLeft += p.offsetLeft; iTop += p.offsetTop;
};
this._offsetleft = iLeft;
this._offsettop = iTop;
//移入Tip物件時保持顯示狀態
addEvent(this.Tip, "mouseover", BindAsEventListener(this, function(e){
//如果是外部元素進入,表示目前是隱藏延時階段,那麼清除定時器取消隱藏
this.Check(e.relatedTarget) && clearTimeout(this._timer);
}));
//ie6處理select
if (isIE6) {
this._iframe = document.createElement("<iframe style='position:absolute;filter:alpha(opacity=0);display:none;'>");
document.body.insertBefore(this._iframe, document.body.childNodes[0]);
};
//用於點擊方式隱藏
this._fCH = BindAsEventListener(this, function(e) {
if (this.Check(e.target) && this.CheckHide()) {
this.ReadyHide(this._trigger.ClickHideDelay);
};
});
//用於觸發方式隱藏
this._fTH = BindAsEventListener(this, function(e) {
if (this.Check(e.relatedTarget) && this.CheckHide()) {
this.ReadyHide(this._trigger.TouchHideDelay);
};
});
};
FixedTips.prototype = {
_doc: document.documentElement,//簡化程式碼
//設定預設屬性
SetOptions: function(options) {
this.options = {//預設值
ClickShow: true,//是否點擊方式顯示
ClickShowDelay: false,//是否點選顯示延遲
ClickHide: true,//是否點擊方式隱藏
ClickHideDelay: false,//是否點選隱藏延時
TouchShow: true,//是否觸發方式顯示
TouchShowDelay: true,//是否觸發顯示延遲
TouchHide: true,//是否觸發方式隱藏
TouchHideDelay: true,//是否觸發隱藏延遲
ShowDelay: 300,//顯示延遲時間
HideDelay: 300,//隱藏延遲時間
Align: "clientleft",//水平方向定位
vAlign: "clienttop",//垂直方向定位
Custom: { left: 0, top: 0 },//自訂定位
Percent: { left: 0, top: 0 },//自訂百分比定位
Adaptive: false,//是否自適應定位
Reset: false,//自適應定位時是否重新定位
onShow: function(){},//顯示時執行
onHide: function(){}//隱藏時執行
};
Extend(this.options, options || {});
},
//檢查觸發元素
檢查: function(elem) {
//傳回是否外部元素(即觸發元素和Tip物件本身及其內部元素以外的元素物件)
return !this._trigger ||
!(
this.Tip === elem || this._trigger.Elem === elem ||
Contains(this.Tip, elem) || Contains(this._trigger.Elem, elem)
);
},
//準備顯示
ReadyShow: function(delay) {
clearTimeout(this._timer);
var trigger = this._trigger;
//點擊方式隱藏
trigger.ClickHide && addEvent(document, "click", this._fCH);
//觸發方式隱藏
trigger.TouchHide && addEvent(this._trigger.Elem, "mouseout", this._fTH);
//是否延遲觸發
if (delay) {
this._timer = setTimeout(Bind(this, this.Show), trigger.ShowDelay);
} else { this.Show(); };
},
//顯示
Show: function() {
clearTimeout(this._timer);
this._trigger.onShow();//放在前面方便修改屬性
//根據預設定位元和自訂定位計算left和top
var trigger = this._trigger, rect = trigger.Elem.getBoundingClientRect(),
scrolldoc = isChrome || isSafari ? document.body : this._doc,
scrollLeft = scrolldoc.scrollLeft, scrollTop = scrolldoc.scrollTop,
customLeft = trigger.Custom.left, customTop = trigger.Custom.top,
iLeft = this.GetLeft(rect, trigger.Align) + customLeft,
iTop = this.GetTop(rect, trigger.vAlign) + customTop;
//自訂百分比定位
if (trigger.Percent.left) { iLeft += .01 * trigger.Percent.left * trigger.Elem.offsetWidth; };
if (trigger.Percent.top) { iTop += .01 * trigger.Percent.top * trigger.Elem.offsetHeight; };
//自適應視窗定位
if (trigger.Adaptive) {
//修正定位參數
var maxLeft = this._doc.clientWidth - this.Tip.offsetWidth,
maxTop = this._doc.clientHeight - this.Tip.offsetHeight;
if (trigger.Reset) {
//自動重新定位
if (iLeft > maxLeft || iLeft < 0) {
iLeft = this.GetLeft(rect, 2 * iLeft > maxLeft ? "left" : "right") + customLeft;
};
if (iTop > maxTop || iTop < 0) {
iTop = this.GetTop(rect, 2 * iTop > maxTop ? "top" : "bottom") + customTop;
};
} else {
//修正到適合位置
iLeft = Math.max(Math.min(iLeft, maxLeft), 0);
iTop = Math.max(Math.min(iTop, maxTop), 0);
};
};
//設定位置並顯示
this._cssTip.left = iLeft + scrollLeft - this._offsetleft + "px";
this._cssTip.top = iTop + scrollTop - this._offsettop + "px";
this._cssTip.visibility = "visible";
//ie6處理select
if (isIE6) {
this._iframe.style.left = iLeft + scrollLeft + "px";
this._iframe.style.top = iTop + scrollTop + "px";
this._iframe.style.width = this.Tip.offsetWidth + "px";
this._iframe.style.height = this.Tip.offsetHeight + "px";
this._iframe.style.display = "";
};
//觸發方式隱藏
trigger.TouchHide && addEvent(this.Tip, "mouseout", this._fTH);
},
//取得相對觸發元素的left
GetLeft: function(rect, align) {
switch (align.toLowerCase()) {
case "left" :
return rect.left - this.Tip.offsetWidth;
case "clientleft" :
return rect.left;
case "center" :
return (rect.left + rect.right - this.Tip.offsetWidth)/2;
case "clientright" :
return rect.right - this.Tip.offsetWidth;
case "right" :
default :
return rect.right;
};
},
//取得相對觸發元素的top
GetTop: function(rect, valign) {
switch (valign.toLowerCase()) {
case "top" :
return rect.top - this.Tip.offsetHeight;
case "clienttop" :
return rect.top;
case "center" :
return (rect.top + rect.bottom - this.Tip.offsetHeight)/2;
case "clientbottom" :
return rect.bottom - this.Tip.offsetHeight;
case "bottom" :
default :
return rect.bottom;
};
},
//準備隱藏
ReadyHide: function(delay) {
clearTimeout(this._timer);
if (delay) {
this._timer = setTimeout(Bind(this, this.Hide), this._trigger.HideDelay);
} else { this.Hide(); };
},
//隱藏
Hide: function() {
clearTimeout(this._timer);
//設定隱藏
this._cssTip.visibility = "hidden";
this._cssTip.left = this._cssTip.top = "-9999px";
//ie6處理select
if (isIE6) { this._iframe.style.display = "none"; };
//處理觸發對象
if (!!this._trigger) {
this._trigger.onHide();
removeEvent(this._trigger.Elem, "mouseout", this._fTH);
}
this._trigger = null;
//移除事件
removeEvent(this.Tip, "mouseout", this._fTH);
removeEvent(document, "click", this._fCH);
},
//新增觸發對象
Add: function(elem, options) {
//建立一個觸發對象
var elem = $$(elem), trigger = Extend(Extend({ Elem: elem }, this.options), options || {});
//點擊方式顯示
addEvent(elem, "click", BindAsEventListener(this, function(e){
if (trigger.ClickShow) {
if (this.CheckShow(trigger)) {
this.ReadyShow(trigger.ClickShowDelay);
} else {
clearTimeout(this._timer);
};
};
}));
//觸發方式顯示
addEvent(elem, "mouseover", BindAsEventListener(this, function(e){
if (trigger.TouchShow) {
if (this.CheckShow(trigger)) {
this.ReadyShow(trigger.TouchShowDelay);
} else if (this.Check(e.relatedTarget)) {
clearTimeout(this._timer);
};
};
}));
//回傳觸發對象
return trigger;
},
//顯示檢查
CheckShow: function(trigger) {
if (trigger !== this._trigger) {
//不是同一個觸發物件就先執行Hide防止衝突
this.Hide(); this._trigger = trigger; return true;
} else { return false; };
},
//隱藏檢查
檢查Hide: function() {
if (this._cssTip.visibility === "hidden") {
//本來就是隱藏狀態,不需要再執行Hide
clearTimeout(this._timer);
removeEvent(this._trigger.Elem, "mouseout", this._fTH);
this._trigger = null;
removeEvent(document, "click", this._fCH);
return false;
} else { return true; };
}
};
</script>
</head>
<body>
<style>
.trigger{border:1px solid #003099; color:#003099; background:#e2e7ff; padding:10px; width:200px; height:100px; margin-left:150px;
.tip{border:1px solid #c00000; color:#c00000; background:#ffcccc; padding:5px; line-height:20px;}
</style>
<div style="padding:50px;">
<div id="idTip" class="tip"></div>
<div id="idTrigger1" class="trigger">
<select>
<option>test</option>
</select>
</div>
<br>
水平位置:
<label>
<input name="nAlign" type="radio" value="left" />
left </label>
<label>
<input name="nAlign" type="radio" value="clientleft" />
clientleft </label>
<label>
<input name="nAlign" type="radio" value="center" />
center </label>
<label>
<input name="nAlign" type="radio" value="clientright" />
clientright </label>
<label>
<input name="nAlign" type="radio" value="right" checked="checked" />
right </label>
<br>
垂直位置:
<label>
<input name="nVAlign" type="radio" value="top" />
top </label>
<label>
<input name="nVAlign" type="radio" value="clienttop" />
clienttop </label>
<label>
<input name="nVAlign" type="radio" value="center" />
center </label>
<label>
<input name="nVAlign" type="radio" value="clientbottom" />
clientbottom </label>
<label>
<input name="nVAlign" type="radio" value="bottom" checked="checked" />
bottom </label>
<br>
<br>
自訂定位:
left:
<input id="idLeft" type="text" size="5" value="0" maxlength="3" />
top:
<input id="idTop" type="text" size="5" value="0" maxlength="3"/>
<br>
<br>
<input id="idClick" type="checkbox" checked="checked" />
<label for="idClick">點擊方式</label>
<input id="idTouch" type="checkbox" checked="checked" />
<label for="idTouch">觸發方式</label>
<br>
<br>
延時時間:
<input id="idDelayTime" type="text" size="5" value="0" maxlength="4"/>
<input id="idDelay" type="button" value=" 取消延遲" />
<br>
<br>
其他應用範例: <br>
<br>
<div id="idTest1"> 利用 title: <a href=" http://www.cnblogs.com/cloudgamer/archive/2008/11/17/1334778.html "title="拖放效果">拖放效果</a> <a href=" http://www.cnblogs.com/cloudgamer/archive/2008/12/03/1346386.html " title="拖曳縮放效果">拖曳縮放效果</a > <a href=" http://www.cnblogs.com/cloudgamer/archive/2008/07/21/1247267.html " title="圖片切割效果">圖片切割效果</a> </div >
<br>
<br>
流行的頭像顯示效果: <br>
<br>
<div id="idTest2"> <a href=" http://www.cnblogs.com/cloudgamer/archive/2008/07/06/1236770.html " title="圖片滑動切換效果"> <img src= "/articleimg/2009/07/6852/r_mx1.jpg" border="0"/></a> <a href=" http://www.cnblogs.com/cloudgamer/archive/2008/05/ 23/1205642.html " title="圖片變換效果"><img src="/articleimg/2009/07/6852/r_mx2.jpg" border="0"/></a> <a href=" http://www.cnblogs.com/cloudgamer/archive/2008/05/13/1194272.html " title="圖片滑動展示效果"><img src="/articleimg/2009/07/6852/r_mx3.jpg " border="0"/></a> </div>
<br>
<br>
關閉按鈕: <a id="idTest3" href=" http://www.cnblogs.com/cloudgamer/archive/2009/05/18/TableFixed.html">Table行定位效果</a>
</div>
<script>
var forEach = function(array, callback, thisObject){
if(array.forEach){
array.forEach(callback, thisObject);
}else{
for (var i = 0, len = array.length; i < len; i++) { callback.call(thisObject, array[i], i, array); }
}
}
///////////////////////////////////////
var ft = new FixedTips("idTip");
///////////////////////////////////////
var trigger1 = ft.Add("idTrigger1", {
onShow: function(){
//定位測試
var sAlign = this.Align, sVAlign = this.vAlign;
forEach(document.getElementsByName("nAlign"), function(o){ if(o.checked){ sAlign = o.value; } });
forEach(document.getElementsByName("nVAlign"), function(o){ if(o.checked){ sVAlign = o.value; } });
this.Align = sAlign;
this.vAlign = sVAlign;
this.Custom.left = $$("idLeft").value | 0;
this.Custom.top = $$("idTop").value | 0;
trigger1.ShowDelay = trigger1.HideDelay = $$("idDelayTime").value | 0 || 300;
ft.Tip.innerHTML = sAlign + "<br>" + sVAlign + "<br>" + "left: " + this.Custom.left + ", top: " + this.Custom.top;
}
});
//延時測試
$$("idDelayTime").value = trigger1.ShowDelay;
$$("idDelay").onclick = function(){
if(trigger1.TouchShowDelay){
trigger1.ClickShowDelay = trigger1.ClickHideDelay =
trigger1.TouchShowDelay = trigger1.TouchHideDelay = false;
$$("idDelayTime").disabled = true;
this.value = " 設定延遲";
}else{
trigger1.ClickShowDelay = trigger1.ClickHideDelay =
trigger1.TouchShowDelay = trigger1.TouchHideDelay = true;
$$("idDelayTime").disabled = false;
this.value = " 取消延遲";
}
}
//方式測試
$$("idClick").onclick = function(){
trigger1.ClickShow = trigger1.ClickHide = this.checked;
}
$$("idTouch").onclick = function(){
trigger1.TouchShow = trigger1.TouchHide = this.checked;
}
////////////////////////////////////////
forEach($$("idTest1").getElementsByTagName("a"), function(o){
var title = o.title; o.title = "";
ft.Add(o, { vAlign: "bottom", Percent: { left: 50, top: 0 }, onShow: function(){ ft.Tip.innerHTML = title; } });
})
////////////////////////////////////////
forEach($$("idTest2").getElementsByTagName("a"), function(o){
var img = o.getElementsByTagName("img")[0], title = o.title;
o.title = "";
ft.Add(img, { Custom: { left: -6, top: -6 },
onShow: function(){
var str = '<a href="' + o.href + '"><img src="' + img.src + '" style="padding-bottom:5px;" border="0"/></ a>';
str += '<br /><a href="' + o.href + '">' + title + '</a>';
ft.Tip.innerHTML = str;
}
});
})
////////////////////////////////////////
ft.Add("idTest3", { ClickHide: false, TouchHide: false, Align: "right",
onShow: function(){
var str = ' <a href=" http://www.cnblogs.com/cloudgamer/archive/2009/03/11/1408333.html ">顏色梯度和漸變效果</a><br />';
str += ' <a href=" http://www.cnblogs.com/cloudgamer/archive/2008/10/20/1314766.html ">仿163網盤無刷新檔案上傳系統</a><br / >';
str += '<input type="button" onclick="ft.Hide();" value="點擊關閉" />';
ft.Tip.innerHTML = str;
}
});
</script>
</body>
</html>
JavaScript,定位,浮動,提示,tooltips,FixedTips,Tip
程式說明
Tip物件:
Tip物件就是用來顯示提示訊息的容器,程式用Tip屬性表示。這個沒什麼要求,程式初始化時會對它做一些設定。
首先進行下面設定:
this._cssTip.margin = 0;
this._cssTip.position = "absolute";
this._cssTip.visibility = "hidden";
this._cssTip.display = "block";
this._cssTip.zIndex = 99;
this._cssTip.left = this._cssTip.top = "-9999px";
其中margin設為0是為了避免一些定位問題,用visibility來隱藏而不是display是因為程式需要取得Tip的offsetWidth、offsetHeight,還需要設定left和top是避免因Tip佔位出現的捲軸。
因為Tip可能會在其他定位元素裡面,所以還要設兩個offset修正參數:
var iLeft = iTop = 0, p = this.Tip.offsetParent;
while (!(p === document.body || p === document.documentElement)) {
iLeft += p.offsetLeft; iTop += p.offsetTop; p = p.offsetParent;
};
this._offsetleft = iLeft;
this._offsettop = iTop;
最後給Tip的mouseover加一個事件,具體後面再來說明。