利用jq实现自适应边缘情况的气泡Tip
由于项目已经接近尾声,所以打算把此次项目中频繁使用的Tip浮窗拎出来分享下。
需求简述
项目中会经常出现超出长度显示省略号的情况,而这种情况老项目都是使用title来显示全部信息。本次项目产品觉得title的显示太过缓慢,需要移上等待一段时间才显示,所以希望可以自己实现一个小tip,能够更加符合项目的需求变化。
然而这个需求提出之后,大家都提出了很多问题,其中问题最大的是:
如何处理Tip超出屏幕范围,被遮住大部分内容的情况?
于是,必须实现一个能够根据边缘情况,自动调整显示方式的Tip。本文主要围绕这个需求点,抛开一些业务上的逻辑实现一个demo。
技术点
1. jq委托事件
在实际项目中为了方便调用,需要能像使用title一样方便的实现效果,可能是个人对jq的使用更加熟悉一点,所以并没有打算将这个功能封装成Angular的directive(会抽空封装起来,毕竟使用jq委托的形式,有点脱离了整个架构体系)。所以采用了将其委托于body上,这样即使是动态生成的dom也可以响应事件。(本文主要实现一个小demo,所以并没有这么做)
2. css3伪类
使用:before
css伪类,来实现tip上的气泡箭头。
顺便贴上css代码:
.box{
background: #cd0200;
width: 100px;
height: 100px;
}
.tip{
background: #fff;
position: fixed;
max-width: 200px;
height: auto;
box-shadow: 1px 1px 9px #ccc;
top:-10px;
left: 10px;
padding: 10px;
border-radius: 3px;
z-index: 2;
}
.tip:before{
content: " ";
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
position: absolute;
}
body{
background: #666;
height: 2000px;
font-size: 10px;
}
3. jq对象的offset属性和一些dom对象属性
因为需要根据边缘情况,动态修改布局。所以需要检测Tip出现时的位置情况,然后做响应的布局响应。
功能实现
第一步自然是事件的定义,由于项目中dom可能是动态生成的,所以不能使用普通的mouseenter
事件。这里我们用到了上面提到的事件委托:
$('body').delegate('[gint-title]', 'mouseenter', function () {
//...
})
选择器选择属性的写法是 [gint-title]
,至于为什么用mouseenter
而不是mouseover
,有兴趣的同学可以自行测试下两者的区别,这里就不继续讨论了。
本文主要代码基于实现一个demo,所以上面的观点只是个人认为项目中重要的知识点,所以顺带说一下,demo的实现还是脱离事件委托,使用jq自带的hover
。
$(".box").hover(function(target){
//...
},function(){
$(".tip").hide();
})
$(window).scroll(function() {
$(".tip").hide();
});
由于Tip需要一直显示在屏幕中,所以干脆让他的position
定义为fixed
。但是fixed
定位会使Tip在滚动的时候依然屹立于屏幕之中,所以添加了上面代码中的scroll
事件。
下面这张图片,主要说明了需要使用到的获取布局的方法。
于是我们可以在代码中先定义几个可能经常会用到的变量:
var dom = $(this);
var left = dom.offset().left - $(document).scrollLeft();
//由于Tip默认是显示在下方的
//所以这里的top需要加上dom的高度 再减去箭头间隔
var top = dom.offset().top + dom.height() - $(document).scrollTop() + 7;
var css = "";
这里声明一个css
变量,主要用来动态的修改伪类的定义,让箭头可以根据情况变化(比如:Tip出现在上方时,箭头朝下,出现在下方时,箭头朝上)。
由于伪类无法使用jq封装好的方法来修改,所以需要添加一个style
标签,用来动态变化伪类的定义。
//判断页面是否存在自定义style
if(!$("#mystyle").length){
var style = $("<style id='mystyle'>.tip:before{left:10px}</style>");
$("body").append(style);
}
接下来就是对边缘条件的判断:
//当Tip没有被浏览器底部遮住的时候
if($(window).height() - top > $(".tip").height()){
css+="border-bottom: 7px solid #fff;top:-7px;"
}else{
css+="border-top: 7px solid #fff;bottom:-6px;"
top = dom.offset().top - $(document).scrollTop() - $(".tip").height()-20-7;
}
//当Tip被浏览器左侧遮住的时候
if(left<0){
left = 0
css +="left:15px;"
//当Tip没被右侧遮住
}else if(($(window).width() - left - $(this).width()) > $(".tip").width()-50){
css +="left:15px;"
}else{
left = $(window).width() - $(".tip").width()+20;
//将箭头跟着gint-title的位置 动态变化
var _leftpx = $(window).width() - (dom.offset().left - $(document).scrollLeft()) - $(this).width()+15;
css +="right:"+_leftpx+"px;"
}
//重写style的伪类定义
$("#mystyle").html(".tip:before{"+css+"}");
//动态改变tip的布局,并显示tip
$(".tip").css({"left":left+"px","top":top+"px"}).show();
好了,基本的页面逻辑都已经涉及到了,还有些偏向于计算方面的逻辑需要你仔细对照我提供的布局图,进行抽象理解。还有代码计算中出现的一些数字,是为了让布局更加美观所添加的边界值,可以自行去掉,体会下我添加的用意就清楚了。
再丢上一个 在线demo链接 我提供了几个例子,可以自行修改显示框的大小,来调试代码的情况。如果发现了问题,欢迎跟我沟通!