当前位置: 首页 > 图文教程 > 网络编程 > Javascript > JavaScript教程:网页浮动定位提示效果

Javascript
JavaScript DOM学习第八章 表单错误提示
JavaScript DOM 学习第九章 选取范围的介绍
JavaScript CSS修改学习第一章 查找位置
JavaScript CSS修改学习第二章 样式
JavaScript CSS修改学习第三章 修改样式表
JavaScript CSS 修改学习第四章 透明度设置
JavaScript CSS修改学习第五章 给“上传”添加样式
JavaScript CSS修改学习第六章 拖拽
Jquery乱码的一次解决过程 图解教程
javascript 包裹节点 提高效率
javascript inneHTML的地雷
javascript 定义新对象方法
判定对象是否为window的js代码
jquery validator 插件增加日期比较方法
jquery 得到当前页面高度和宽度的两个函数
JavaScript 编写匿名函数的几种方法
jQuery 操作下拉列表框实现代码
jQuery入门问答 整理的几个常见的初学者问题
第一个JavaScript入门基础 document.write输出
javascript入门基础之私有变量

Javascript 中的 JavaScript教程:网页浮动定位提示效果


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-09-28   浏览: 217 ::
收藏到网摘: n/a

本来想做一个集合浮动定位和鼠标跟随的tooltips效果,但发现定位和鼠标跟随在一些关键的地方还是不同的,还是分开来吧。
这个效果本身难度不大,主要在程序结构和扩展中下了些功夫,务求用起来更方便,能用在更多的地方。

程序特点

  1. 同一个提示框用在多个触发元素时,只需一个实例;
  2. 显示和隐藏分别有点击方式和触发方式选择;
  3. 能设置延时显示和隐藏;
  4. 有25种预设定位位置;
  5. 可在预设定位基础上,再自定义定位;
  6. 可设置自适应窗口定位;

完整实例下载(点击下载)

运行代码框

[Ctrl+A 全部选择 提示:你可先修改部分代码,再按运行]

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加一个事件,具体后面再说明。

 

触发对象

由于很多情况下都是一个Tip对应多个地方的提示,所以程序参考了 Table排序 的方式,添加了一个Add方法。
一个Tip实例化后,再用Add方法就可以对多个触发元素分别添加触发对象,程序中用_trigger属性表示当前的触发对象。
Add方法的一个必要参数是触发元素,就是触发显示Tip的元素。
需要的话还可以用options参数,来自定义触发对象的属性,包括:

属性:  默认值//说明
ClickShow:  true,//是否点击方式显示
ClickShowDelay: false,//是否点击显示延时
ClickHide:  true,//是否点击方式隐藏
ClickHideDelay: false,//是否点击隐藏延时
TouchShow:  true,//是否触发方式显示
TouchShowDelay: true,//是否触发显示延时
TouchHide:  true,//是否触发方式隐藏
TouchHideDelay: true,//是否触发隐藏延时
ShowDelay:  300,//显示延时时间
HideDelay:  300,//隐藏延时时间
vAlign:  "clienttop",//垂直方向定位
Align:  "left",//水平方向定位
Custom:  { left: 0, top: 0 },//自定义定位
Percent:  { left: 0, top: 0 },//自定义百分比定位
Adaptive:  true,//是否自适应定位
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 (trigger.ClickShow) {
        if (this.CheckShow(trigger)) {
            this.ReadyShow(trigger.ClickShowDelay);
        } 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里,当ClickHide为true时,就会把_fCH绑定到document的click事件里:

trigger.ClickHide && addEvent(document, "click", this._fCH);

注意这里要把隐藏绑定事件放到ReadyShow,而不是Show里面,因为延时的时候有可能还没有显示就触发了隐藏事件。

其中_fCH是在初始化时定义的一个属性,用于添加和移除点击隐藏事件:

this._fCH = BindAsEventListener(this, function(e) {
    if (this.Check(e.target) && this.CheckHide()) {
        this.ReadyHide(this._trigger.ClickHideDelay);
    }
});

注意不同于点击显示,由于绑定的是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 (trigger.TouchShow) {
        if (this.CheckShow(trigger)) {
            this.ReadyShow(trigger.TouchShowDelay);
        } else if (this.Check(e.relatedTarget)) {
            clearTimeout(this._timer);
        };
    };
}));

跟点击方式类似,也需要执行一次CheckShow,但不同的是,还会用Check判断e.relatedTarget是不是外部对象。
这是因为mouseover可能是从触发元素的内部元素(包括Tip)进入或内部元素冒泡触发的,而这些情况不需要任何操作。

对应的,触发方式隐藏是指鼠标从触发元素或Tip离开时隐藏Tip。
当TouchHide为true时,在ReadyShow的时候会把_fTH绑定到触发元素的mouseout事件里:

trigger.TouchHide && addEvent(this._trigger.Elem, "mouseout", this._fTH);

在Show的时候,再绑定到Tip的mouseout:

trigger.TouchHide && addEvent(this.Tip, "mouseout", this._fTH);

在ReadyShow绑定的原因同上,而Tip只需显示时绑定。

其中_fTH跟_fCH类似,也是在初始化时定义的一个属性,用于添加和移除触发隐藏事件:

this._fTH = BindAsEventListener(this, function(e) {
    if (this.Check(e.relatedTarget) && this.CheckHide()) {
        this.ReadyHide(this._trigger.TouchHideDelay);
    }
});

不同的是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;

再对照程序,应该就能理解整个流程了,当然可能还不是那么好理解。
这个流程也只是单例的情况,多例的时候还要多加一些判断。
可以说这个流程看似不难,但如果想做一个最优化的流程,那要考虑的细节地方可能会让人受不了。
点击方式跟触发方式的流程是差不多的,而且更简单,这里就不重复了。

 

元素定位

完成了显示隐藏,就到本程序另一个重点,元素定位。
程序包括这几个定位:预设定位,自定义定位,自适应定位。
而定位的最终效果是结合了这几个定位设置的效果,下面再一一分析。

预设定位和自定义定位

预设定位的意思是使用程序25个预设位置来定位。
25个位置是怎么来的呢?看下面的演示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

 

其中黑色框代表触发元素,红色框代表Tip。
一眼望去,要实现这么多的位置好像很复杂,这时要想找到最好的方法就要细心分析找出规律。
这25个位置其实都是由5个水平坐标和5个垂直坐标组合而来的,只要计算好这10个坐标,就能组合出这25个位置来了。
其中1,2,3,4,5代表的水平坐标,程序分别用left,clientleft,center,clientright,right来标识。
而1,6,11,16,21代表的垂直坐标,程序分别用top,clienttop,center,clientbottom,bottom来标识。
ps:词穷,只好加个client来充数。

下面说说如何获取这些坐标的值,首先通过getBoundingClientRect要获取触发元素的坐标对象。
ps:关于getBoundingClientRect的介绍请看 这里的元素位置
再利用这个坐标对像,通过GetLeft和GetTop来获取水平和垂直坐标。
GetLeft和GetTop里面都是些简单的获取坐标算法,具体请参考代码。

使用时,把水平坐标和垂直坐标的标识值(字符)分别赋给触发对象的Align和vAlign属性,系统就会自动设置对应的位置。
例如要设置位置14,那么Align设为"clientright",vAlign设为"center"就可以了。

至于自定义定位就是在预设定位得到的坐标基础上,根据Custom(形式如{ left: 50, top: -10 })的设置再进行left和top的修正。
自定义百分比定位是以触发元素的宽和高为基准,取百分比:

if (trigger.Percent.left) { iLeft += .01 * trigger.Percent.left * trigger.Elem.offsetWidth; };
if (trigger.Percent.top) { iTop += .01 * trigger.Percent.top * trigger.Elem.offsetHeight; };

注意数值单位是0.01。

 

自适应定位

自适应定位的作用是当Tip显示的范围超过浏览器可视范围的时候,自动修正到可视范围里面。
因为上面通过getBoundingClientRect获取的定位是以视窗为准的,所以可以直接通过clientWidth/clientHeight来判断是否超过视窗范围。
首先获取最大left和top值:

var maxLeft = this._doc.clientWidth - this.Tip.offsetWidth,
    maxTop = this._doc.clientHeight - this.Tip.offsetHeight;

最小值是0就不用计算了。

如果Reset属性是true会使用重新定位的方法。
理想的效果是能自动从25个预设定位中找到适合的定位位置。
但这个需求实在变化太多,要全部实现估计要长长的代码,程序仅仅做了简单的修正:

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;
};

实际应用的话估计要按需求重写这部分才行。

如果不是用Reset重新定位,只需要根据这几个值获取适合的值就行了:

iLeft = Math.max(Math.min(iLeft, maxLeft), 0);
iTop = Math.max(Math.min(iTop, maxTop), 0);

隐藏select

又是ie6的隐藏select问题,这里用的是iframe遮盖法。

首先初始化时插入iframe:

this._iframe = document.createElement(" <iframe style='position:absolute;filter:alpha(opacity=0);display:none;'>");
document.body.insertBefore(this._iframe, document.body.childNodes[0]);

在Show的时候,参照Tip设置好样式,再显示:

this._iframe.style.left = iLeft + this._docScroll.scrollLeft + "px";
this._iframe.style.top = iTop + this._docScroll.scrollTop + "px";
this._iframe.style.width = this.Tip.offsetWidth + "px";
this._iframe.style.height = this.Tip.offsetHeight + "px";
this._iframe.style.display = "";

其实就是要垫在Tip的下面。

在Hidde时隐藏就可以了。

使用说明

实例化时,第一个必要参数是Tip对象:

var ft = new FixedTips("idTip");

第二个可选参数用来设置触发对象属性的统一默认值。

然后用Add方法添加触发对象:

var trigger1 = ft.Add("idTrigger1");

第二个可选参数用来设置该触发对象属性。

要添加多个触发对象时只需继续用Add添加就行了。

程序源码

代码拷贝框

[Ctrl+A 全部选择 然后拷贝]