l2Overlay: 原生 JavaScript 实现 Hoverpop 效果插件
最近想给自己做的项目加上类似于 Facebook 的 Namecard 效果,即鼠标移动到制定目标后浮现一个信息卡,在鼠标移开目标或者信息卡后则自动隐藏。
搜索了一下好像没有现成的插件可用,找到过几个仿 Facebook Style 的代码片段,但是感觉代码质量比我的还要糟糕,而且也不完全符合自己的要求,所以就花了一个下午的时间,用原生 JavaScript 写了一个类 Facebook Style 的 Namecard (hoverpop)插件。
参看演示效果可以到:l2Overlay 演示
l2Overlay 的 CSS 代码
l2Overlay 的样式主要是模仿 Facebook 的 Namecard,下面是根据我的 l2Overlay 编写的 CSS 样式:
- .l2OverlayWrapper {
- font-size:12px;
- color:#333333;
- line-height:1.8;
- background-color:#FFFFFF;
- border:1px solid #8C8C8C;
- box-shadow:0010px#C8C8C8;
- margin:9px0;
- position: relative;
- max-width:300px;
- _width:300px;
- }
- .l2OverlayArrow {
- position: absolute;
- width:16px;
- height:9px;
- background:url("arrow.png") no-repeat -50px0px transparent;
- }
- .arrow-lt .l2OverlayArrow {
- left:15px;
- top:-9px;
- }
- .arrow-rt .l2OverlayArrow {
- right:15px;
- top:-9px;
- }
- .arrow-lb .l2OverlayArrow {
- background-position:-17px0;
- left:15px;
- bottom:-9px;
- _bottom:-15px;
- }
- .arrow-rb .l2OverlayArrow {
- background-position:-17px0;
- right:15px;
- bottom:-9px;
- _bottom:-15px;
- }
- .l2OverlayContent {
- margin:10px;
- }
- .l2Action {
- padding:010px;
- margin:10px-10px-10px;
- background-color:#F2F2F2;
- border-top:1px solid #CCCCCC;
- line-height:34px;
- height:34px;
- }
- .l2Title {
- border-bottom:1px dotted #CCCCCC;
- font-size:14px;
- font-weight: bold;
- margin:0010px;
- }
l2Overlay 的 Javascript 代码
Js 代码不压缩只有 9.88 kb,YUI 压缩后只有 4.15 kb,对于不想只为了一个效果而引用一个库的来说,绝对是很好的选择。
- /**
- * Plugin Name: l2Overlay
- * Plugin Author: LOO2K
- * Plugin Address: http://loo2k.com/blog/l2overlay-a-javascript-based-hoverpop-plugin/
- * Plugin Edition: 0.1
- * Last updated: 2012.05.19
- */
- function l2Overlay(){
- this.initialize.apply(this, arguments);
- }
- l2Overlay.prototype ={
- /*
- * object 要应用 l2Overlay 的元素 对象或者数组
- * content 显示内容 'function|html|string|target'
- * option 选项
- * option = {
- * 'title' = 'string', // defalut undefied
- * 'pos' = 'auto|lt|lb|rt|tb', // default auto
- * 'cache' = 'true|false' // default false
- * }
- */
- initialize:function(object, content, option){
- this.id =(newDate()).getTime();
- this.createElement();
- var _this =this;
- this.object = object;
- this.content = content;
- this.option ={
- 'title': option.title ||undefined,
- 'pos': option.pos ||'auto',
- 'cache': option.cache ||false
- };
- this.hidel2Overlay;
- this.l2Overlay.onmouseover =function(){
- _this.cancleHide();
- }
- this.l2Overlay.onmouseout =function(){
- _this.hideOverlay();
- }
- if(typeofthis.object ==='object'&&typeofthis.object.length ==='number'){
- for(var i =0; i <this.object.length; i++){
- this.addEvent(this.object[i]);
- }
- }elseif(typeofthis.object =='object'&&!(this.object instanceof Array)){
- this.addEvent(this.object);
- }else{
- alert('传入对象错误');
- }
- },
- createElement:function(){
- // 生成基本模版
- if(!document.getElementById('l2Overlay_'+this.id)){
- var overlay = document.createElement('div');
- overlay.id ='l2Overlay_'+this.id;
- overlay.style.position ='absolute';
- overlay.style.zIndex ='9999';
- overlay.style.display ='none';
- var wrapper = document.createElement('div');
- wrapper.className ='l2OverlayWrapper';
- var arrow = document.createElement('div');
- arrow.className ='l2OverlayArrow';
- var content = document.createElement('div');
- content.className ='l2OverlayContent';
- wrapper.appendChild(arrow);
- wrapper.appendChild(content);
- overlay.appendChild(wrapper);
- document.getElementsByTagName('body')[0].appendChild(overlay);
- }
- this.l2Overlay = document.getElementById('l2Overlay_'+this.id);
- var wrapper =this.l2Overlay.getElementsByTagName('div')[0].getElementsByTagName('div');
- this.l2Content = wrapper[1];
- this.l2Arrow = wrapper[0];
- },
- getPos:function(el){
- var ua = navigator.userAgent.toLowerCase();
- var isOpera =(ua.indexOf('opera')!=-1);
- var isIE =(ua.indexOf('msie')!=-1&&!isOpera);
- if( el.parentNode ===null|| el.style.display =='none'){
- returnfalse;
- }
- var parent =null;
- var pos =[];
- var box;
- if( el.getBoundingClientRect ){
- // For IE
- box = el.getBoundingClientRect();
- var scrollTop =Math.max(document.documentElement.scrollTop, document.body.scrollTop);
- var scrollLeft =Math.max(document.documentElement.scrollLeft, document.body.scrollLeft);
- return{
- x: box.left + scrollLeft,
- y: box.top + scrollTop
- };
- }elseif( document.getBoxObjectFor ){
- // For FF
- box = document.getBoxObjectFor(el);
- var borderLeft =(el.style.borderLeftWidth)? parseInt(el.style.borderLeftWidth):0;
- var borderTop =(el.style.borderTopWidth)? parseInt(el.style.borderTopWidth):0;
- pos =[box.x - borderLeft, box.y - borderTop];
- }else{
- // For Safari & Opera
- pos =[el.offsetLeft, el.offsetTop];
- parent = el.offsetParent;
- if( parent != el ){
- while(parent){
- pos[0]+= parent.offsetLeft;
- pos[1]+= parent.offsetTop;
- parent = parent.offsetParent;
- }
- }
- if( ua.indexOf('opera')!=-1||(ua.indexOf('safari')!=-1&& el.style.position =='absolute')){
- pos[0]-= document.body.offsetLeft;
- pos[1]-= document.body.offsetTop;
- }
- }
- if( el.parentNode ){
- parent = el.parentNode;
- }else{
- parent =null;
- }
- // account for any scrolled ancestors
- while(parent && parent.tagName !='BODY'&& parent.tagName !='HTML'){
- pos[0]-= parent.scrollLeft;
- pos[1]-= parent.scrollTop;
- if(parent.parentNode){
- parent = parent.parentNode;
- }else{
- parent =null;
- }
- }
- return{
- x: pos[0],
- y: pos[1]
- };
- },
- getSize:function(element){
- if(element.offsetWidth !==0){
- /* 元素不是display:none的情况,这个时候是能得到尺寸的 */
- return{
- 'width': element.offsetWidth,
- 'height': element.offsetHeight
- };
- }
- var old ={};
- /* 将display:none元素设成visibility:hidden */
- var options ={
- position:"absolute",
- visibility:"hidden",
- display:"block"
- }
- for(var name in options ){
- old[ name ]= element.style[ name ];
- element.style[ name ]= options[ name ];
- }
- var temp ={
- 'width': element.offsetWidth,
- 'height': element.offsetHeight
- };
- for(var name in options ){
- element.style[ name ]= old[ name ];
- }
- return temp;
- },
- hideOverlay:function(){
- var _this =this;
- this.hidel2Overlay = setTimeout(function(){
- _this.l2Overlay.style.display ='none';
- },100);
- },
- cancleHide:function(){
- if(this.hidel2Overlay ){
- clearTimeout(this.hidel2Overlay);
- }
- },
- getBrowser:function(){
- var offset ={
- 'top': document.documentElement.scrollTop || document.body.scrollTop,
- 'left': document.documentElement.scrollLeft || document.body.scrollLeft
- }
- return offset;
- },
- addEvent:function(object){
- var _this =this;
- //var _object = this.object;
- var _object = object;
- _object.onmouseover =function(){
- _this.cancleHide();
- // 缓存模块
- if( _this.option.cache &&this.l2cache ){
- _this.l2Content.innerHTML =this.l2cache;
- }elseif(typeof _this.content ==='function'){
- _this.l2Content.innerHTML = _this.content.apply(this, arguments);;
- //console.log(_this.content());
- }else{
- _this.l2Content.innerHTML = _this.content;
- }
- if( _this.option.cache &&!this.l2cache ){
- this.l2cache = _this.l2Content.innerHTML;
- }
- // 定位模块
- var position = _this.getPos(this);
- var curTop = position.y;
- var curLeft = position.x;
- var offset = _this.getSize(_this.l2Overlay);
- var self = _this.getSize(this);
- var browser = _this.getBrowser();
- var pos ='';
- if( _this.option.pos ==='auto'){
- if((curLeft - browser.left - offset.width)>0){
- pos +='r';
- }else{
- pos +='l';
- }
- if((curTop - browser.top - offset.height)>0){
- pos +='b';
- }else{
- pos +='t';
- }
- }else{
- pos = _this.option.pos;
- }
- switch(pos){
- case'lt':
- _this.l2Overlay.className ='arrow-lt';
- oTop = curTop + self.height;
- oLeft = curLeft;
- break;
- case'rt':
- _this.l2Overlay.className ='arrow-rt';
- oTop = curTop + self.height;
- oLeft = curLeft - offset.width + self.width;
- break;
- case'rb':
- _this.l2Overlay.className ='arrow-rb';
- oTop = curTop - offset.height;
- oLeft = curLeft - offset.width + self.width;
- break;
- case'lb':
- default:
- _this.l2Overlay.className ='arrow-lb';
- oTop = curTop - offset.height;
- oLeft = curLeft;
- break;
- }
- _this.l2Overlay.style.top = oTop +'px';
- _this.l2Overlay.style.left = oLeft +'px';
- _this.l2Overlay.style.display ='';
- }
- _object.onmouseout =function(){
- _this.hideOverlay();
- }
- }
- }
l2Overlay 的使用方法
l2Overlay 创建了一个函数对象 l2Overlay
,接受三个参数。分别为:
object
: 传入的必须是 HTML 元素,或者是 HTML 元素数组;content
: 显示内容,可以是函数,HTML 代码,或者字符串,作用域为 object;option
: option 对象可以设置 l2Overlay 显示的位置以及是否启用缓存;
- var applyLinks = document.getElementById('demo').getElementsByTagName('a');
- var content =function(){
- var html ='<div class="l2Title">这是一个标题</div>';
- html +='Function 内的作用域为该链接,如执行 this.href 可以得到该链接的地址:';
- html +=this.href;
- html +='<div class="l2Action"><a href="#">添加</a> | <a href="#">修改</a> | <a href="#">删除</a></div>';
- return html;
- }
- var options ={'pos':'','cache':true};
- new l2Overlay(applyLinks, content, options);
参看演示效果可以到:l2Overlay 演示
下载插件可以到:l2Overlay 下载
小总结
第一次写 JavaScript 插件,功夫不到家,代码仅供参考 :P
其实这个用 jQuery 等库实现会简单很多,而且代码行数也会少很多,但是为了熟悉原生的 JavaScript 还是自己动手写了一遍。