利用jq实现自适应边缘情况的气泡Tip

由于项目已经接近尾声,所以打算把此次项目中频繁使用的Tip浮窗拎出来分享下。

需求简述

项目中会经常出现超出长度显示省略号的情况,而这种情况老项目都是使用title来显示全部信息。本次项目产品觉得title的显示太过缓慢,需要移上等待一段时间才显示,所以希望可以自己实现一个小tip,能够更加符合项目的需求变化。

然而这个需求提出之后,大家都提出了很多问题,其中问题最大的是:

如何处理Tip超出屏幕范围,被遮住大部分内容的情况?

于是,必须实现一个能够根据边缘情况,自动调整显示方式的Tip。本文主要围绕这个需求点,抛开一些业务上的逻辑实现一个demo。

技术点

1. jq委托事件

在实际项目中为了方便调用,需要能像使用title一样方便的实现效果,可能是个人对jq的使用更加熟悉一点,所以并没有打算将这个功能封装成Angular的directive(会抽空封装起来,毕竟使用jq委托的形式,有点脱离了整个架构体系)。所以采用了将其委托于body上,这样即使是动态生成的dom也可以响应事件。(本文主要实现一个小demo,所以并没有这么做)

2. css3伪类

使用:beforecss伪类,来实现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事件。

下面这张图片,主要说明了需要使用到的获取布局的方法。

Tip布局说明

于是我们可以在代码中先定义几个可能经常会用到的变量:

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链接 我提供了几个例子,可以自行修改显示框的大小,来调试代码的情况。如果发现了问题,欢迎跟我沟通!

posted @ 2017-04-06 19:54  Cydiacen  阅读(721)  评论(0编辑  收藏  举报