/*! * jquery-powerFloat.js * jQuery 万能浮动层插件 * http://www.zhangxinxu.com/wordpress/?p=1328 * © by zhangxinxu * 2010-12-06 v1.0.0 插件编写,初步调试 * 2010-12-30 v1.0.1 限定尖角字符字体,避免受浏览器自定义字体干扰 * 2011-01-03 v1.1.0 修复连续获得焦点显示后又隐藏的bug 修复图片加载正则判断不准确的问题 * 2011-02-15 v1.1.1 关于居中对齐位置判断的特殊处理 * 2011-04-15 v1.2.0 修复浮动层含有过高select框在IE下点击会隐藏浮动层的问题,同时优化事件绑定 * 2011-09-13 v1.3.0 修复两个菜单hover时间间隔过短隐藏回调不执行的问题 * 2012-01-13 v1.4.0 去除ajax加载的存储 修复之前按照ajax地址后缀判断是否图片的问题 修复一些脚本运行出错 修复hover延时显示时,元素没有显示但鼠标移出依然触发隐藏回调的问题 * 2012-02-27 v1.5.0 为无id容器创建id逻辑使用错误的问题 修复无事件浮动出现时同页面点击空白区域浮动层不隐藏的问题 修复点击与hover并存时特定时候o.trigger报为null错误的问题 * 2012-03-29 v1.5.1 修复连续hover时候后面一个不触发显示的问题 * 2012-05-02 v1.5.2 点击事件 浮动框再次点击隐藏的问题修复 * 2012-11-02 v1.6.0 兼容jQuery 1.8.2 * 2012-01-28 v1.6.1 target参数支持funtion类型,以实现类似动态Ajax地址功能 */ (function ($) { $.fn.powerFloat = function (options) { return $(this).each(function () { var s = $.extend({}, defaults, options || {}) var init = function (pms, trigger) { if (o.target && o.target.css('display') !== 'none') { o.targetHide() } o.s = pms o.trigger = trigger }; var hoverTimer switch (s.eventType) { case 'hover': { $(this).hover(function () { if (o.timerHold) { o.flagDisplay = true } var numShowDelay = parseInt(s.showDelay, 10) init(s, $(this)) // 鼠标hover延时 if (numShowDelay) { if (hoverTimer) { clearTimeout(hoverTimer) } hoverTimer = setTimeout(function () { o.targetGet.call(o) }, numShowDelay) } else { o.targetGet() } }, function () { if (hoverTimer) { clearTimeout(hoverTimer) } if (o.timerHold) { clearTimeout(o.timerHold) } o.flagDisplay = false o.targetHold() }) if (s.hoverFollow) { // 鼠标跟随 $(this).mousemove(function (e) { o.cacheData.left = e.pageX o.cacheData.top = e.pageY o.targetGet.call(o) return false }) } break } case 'click': { $(this).click(function (e) { if (o.display && o.trigger && e.target === o.trigger.get(0)) { o.flagDisplay = false o.displayDetect() } else { init(s, $(this)) o.targetGet() if (!$(document).data('mouseupBind')) { $(document).bind('mouseup', function (e) { var flag = false if (o.trigger) { var idTarget = o.target.attr('id') if (!idTarget) { idTarget = 'R_' + Math.random() o.target.attr('id', idTarget) } $(e.target).parents().each(function () { if ($(this).attr('id') === idTarget) { flag = true } }) if (s.eventType === 'click' && o.display && e.target != o.trigger.get(0) && !flag) { o.flagDisplay = false o.displayDetect() } } return false }).data('mouseupBind', true) } } }) break } case 'focus': { $(this).focus(function () { var self = $(this) setTimeout(function () { init(s, self) o.targetGet() }, 200) }).blur(function () { o.flagDisplay = false setTimeout(function () { o.displayDetect() }, 190) }) break } default: { init(s, $(this)) o.targetGet() // 放置页面点击后显示的浮动内容隐掉 $(document).unbind('mouseup').data('mouseupBind', false) } } }) } var o = { targetGet: function () { // 一切显示的触发来源 if (!this.trigger) { return this } var attr = this.trigger.attr(this.s.targetAttr); var target = typeof this.s.target === 'function' ? this.s.target.call(this.trigger) : this.s.target switch (this.s.targetMode) { case 'common': { if (target) { var type = typeof (target) if (type === 'object') { if (target.size()) { o.target = target.eq(0) } } else if (type === 'string') { if ($(target).size()) { o.target = $(target).eq(0) } } } else { if (attr && $('#' + attr).length) { o.target = $('#' + attr) } } if (o.target) { o.targetShow() } else { return this } break } case 'ajax': { // ajax元素,如图片,页面地址 var url = target || attr this.targetProtect = false if (!url) { return } if (!o.cacheData[url]) { o.loading() } // 优先认定为图片加载 var tempImage = new Image() tempImage.onload = function () { var w = tempImage.width; var h = tempImage.height var winw = $(window).width(); var winh = $(window).height() var imgScale = w / h; var winScale = winw / winh if (imgScale > winScale) { // 图片的宽高比大于显示屏幕 if (w > winw / 2) { w = winw / 2 h = w / imgScale } } else { // 图片高度较高 if (h > winh / 2) { h = winh / 2 w = h * imgScale } } var imgHtml = '' o.cacheData[url] = true o.target = $(imgHtml) o.targetShow() } tempImage.onerror = function () { // 如果图片加载失败,两种可能,一是100%图片,则提示;否则作为页面加载 if (/(\.jpg|\.png|\.gif|\.bmp|\.jpeg)$/i.test(url)) { o.target = $('
图片加载失败。
') o.targetShow() } else { $.ajax({ url: url, success: function (data) { if (typeof (data) === 'string') { o.cacheData[url] = true o.target = $('
' + data + '
') o.targetShow() } }, error: function () { o.target = $('
数据没有加载成功。
') o.targetShow() } }) } } tempImage.src = url break } case 'list': { // 下拉列表 var targetHtml = '' o.target = $(targetHtml) this.targetProtect = false o.targetShow() break } case 'remind': { // 内容均是字符串 var strRemind = target || attr this.targetProtect = false if (typeof (strRemind) === 'string') { o.target = $('' + strRemind + '') o.targetShow() } break } default: { var objOther = target || attr; var type = typeof (objOther) if (objOther) { if (type === 'string') { // 选择器 if (/^.[^:#\[\.,]*$/.test(objOther)) { if ($(objOther).size()) { o.target = $(objOther).eq(0) this.targetProtect = true } else if ($('#' + objOther).size()) { o.target = $('#' + objOther).eq(0) this.targetProtect = true } else { o.target = $('
' + objOther + '
') this.targetProtect = false } } else { o.target = $('
' + objOther + '
') this.targetProtect = false } o.targetShow() } else if (type === 'object') { if (!$.isArray(objOther) && objOther.size()) { o.target = objOther.eq(0) this.targetProtect = true o.targetShow() } } } } } return this }, container: function () { // 容器(如果有)重装target var cont = this.s.container; var mode = this.s.targetMode || 'mode' if (mode === 'ajax' || mode === 'remind') { // 显示三角 this.s.sharpAngle = true } else { this.s.sharpAngle = false } // 是否反向 if (this.s.reverseSharp) { this.s.sharpAngle = !this.s.sharpAngle } if (mode !== 'common') { // common模式无新容器装载 if (cont === null) { cont = 'plugin' } if (cont === 'plugin') { if (!$('#floatBox_' + mode).size()) { $('
').appendTo($('body')).hide() } cont = $('#floatBox_' + mode) } if (cont && typeof (cont) !== 'string' && cont.size()) { if (this.targetProtect) { o.target.show().css('position', 'static') } o.target = cont.empty().append(o.target) } } return this }, setWidth: function () { var w = this.s.width if (w === 'auto') { if (this.target.get(0).style.width) { this.target.css('width', 'auto') } } else if (w === 'inherit') { this.target.width(this.trigger.width()) } else { this.target.css('width', w) } return this }, position: function () { if (!this.trigger || !this.target) { return this } var pos; var tri_h = 0; var tri_w = 0; var cor_w = 0; var cor_h = 0; var tri_l; var tri_t; var tar_l; var tar_t; var cor_l; var cor_t var tar_h = this.target.data('height'); var tar_w = this.target.data('width') var st = $(window).scrollTop() var off_x = parseInt(this.s.offsets.x, 10) || 0; var off_y = parseInt(this.s.offsets.y, 10) || 0 var mousePos = this.cacheData // 缓存目标对象高度,宽度,提高鼠标跟随时显示性能,元素隐藏时缓存清除 if (!tar_h) { tar_h = this.target.outerHeight() if (this.s.hoverFollow) { this.target.data('height', tar_h) } } if (!tar_w) { tar_w = this.target.outerWidth() if (this.s.hoverFollow) { this.target.data('width', tar_w) } } pos = this.trigger.offset() tri_h = this.trigger.outerHeight() tri_w = this.trigger.outerWidth() tri_l = pos.left tri_t = pos.top var funMouseL = function () { if (tri_l < 0) { tri_l = 0 } else if (tri_l + tri_h > $(window).width()) { tri_l = $(window).width() - tri_w } }; var funMouseT = function () { if (tri_t < 0) { tri_t = 0 } else if (tri_t + tri_h > $(document).height()) { tri_t = $(document).height() - tri_h } } // 如果是鼠标跟随 if (this.s.hoverFollow && mousePos.left && mousePos.top) { if (this.s.hoverFollow === 'x') { // 水平方向移动,说明纵坐标固定 tri_l = mousePos.left funMouseL() } else if (this.s.hoverFollow === 'y') { // 垂直方向移动,说明横坐标固定,纵坐标跟随鼠标移动 tri_t = mousePos.top funMouseT() } else { tri_l = mousePos.left tri_t = mousePos.top funMouseL() funMouseT() } } var arrLegalPos = ['4-1', '1-4', '5-7', '2-3', '2-1', '6-8', '3-4', '4-3', '8-6', '1-2', '7-5', '3-2'] var align = this.s.position; var alignMatch = false; var strDirect $.each(arrLegalPos, function (i, n) { if (n === align) { alignMatch = true } }) if (!alignMatch) { align = '4-1' } var funDirect = function (a) { var dir = 'bottom' // 确定方向 switch (a) { case '1-4': case '5-7': case '2-3': { dir = 'top' break } case '2-1': case '6-8': case '3-4': { dir = 'right' break } case '1-2': case '8-6': case '4-3': { dir = 'left' break } case '4-1': case '7-5': case '3-2': { dir = 'bottom' break } } return dir } // 居中判断 var funCenterJudge = function (a) { if (a === '5-7' || a === '6-8' || a === '8-6' || a === '7-5') { return true } return false } var funJudge = function (dir) { var totalHeight = 0; var totalWidth = 0; var flagCorner = !!((o.s.sharpAngle && o.corner)) if (dir === 'right') { totalWidth = tri_l + tri_w + tar_w + off_x if (flagCorner) { totalWidth += o.corner.width() } if (totalWidth > $(window).width()) { return false } } else if (dir === 'bottom') { totalHeight = tri_t + tri_h + tar_h + off_y if (flagCorner) { totalHeight += o.corner.height() } if (totalHeight > st + $(window).height()) { return false } } else if (dir === 'top') { totalHeight = tar_h + off_y if (flagCorner) { totalHeight += o.corner.height() } if (totalHeight > tri_t - st) { return false } } else if (dir === 'left') { totalWidth = tar_w + off_x if (flagCorner) { totalWidth += o.corner.width() } if (totalWidth > tri_l) { return false } } return true } // 此时的方向 strDirect = funDirect(align) if (this.s.sharpAngle) { // 创建尖角 this.createSharp(strDirect) } // 边缘过界判断 if (this.s.edgeAdjust) { // 根据位置是否溢出显示界面重新判定定位 if (funJudge(strDirect)) { // 该方向不溢出 (function () { if (funCenterJudge(align)) { return } var obj = { top: { right: '2-3', left: '1-4' }, right: { top: '2-1', bottom: '3-4' }, bottom: { right: '3-2', left: '4-1' }, left: { top: '1-2', bottom: '4-3' } } var o = obj[strDirect]; var name if (o) { for (name in o) { if (!funJudge(name)) { align = o[name] } } } })() } else { // 该方向溢出 (function () { if (funCenterJudge(align)) { var center = { '5-7': '7-5', '7-5': '5-7', '6-8': '8-6', '8-6': '6-8' } align = center[align] } else { var obj = { top: { left: '3-2', right: '4-1' }, right: { bottom: '1-2', top: '4-3' }, bottom: { left: '2-3', right: '1-4' }, left: { bottom: '2-1', top: '3-4' } } var o = obj[strDirect]; var arr = [] for (name in o) { arr.push(name) } if (funJudge(arr[0]) || !funJudge(arr[1])) { align = o[arr[0]] } else { align = o[arr[1]] } } })() } } // 已确定的尖角 var strNewDirect = funDirect(align); var strFirst = align.split('-')[0] if (this.s.sharpAngle) { // 创建尖角 this.createSharp(strNewDirect) cor_w = this.corner.width(), cor_h = this.corner.height() } // 确定left, top值 if (this.s.hoverFollow) { // 如果鼠标跟随 if (this.s.hoverFollow === 'x') { // 仅水平方向跟随 tar_l = tri_l + off_x if (strFirst === '1' || strFirst === '8' || strFirst === '4') { // 最左 tar_l = tri_l - (tar_w - tri_w) / 2 + off_x } else { // 右侧 tar_l = tri_l - (tar_w - tri_w) + off_x } // 这是垂直位置,固定不动 if (strFirst === '1' || strFirst === '5' || strFirst === '2') { tar_t = tri_t - off_y - tar_h - cor_h // 尖角 cor_t = tri_t - cor_h - off_y - 1 } else { // 下方 tar_t = tri_t + tri_h + off_y + cor_h cor_t = tri_t + tri_h + off_y + 1 } cor_l = pos.left - (cor_w - tri_w) / 2 } else if (this.s.hoverFollow === 'y') { // 仅垂直方向跟随 if (strFirst === '1' || strFirst === '5' || strFirst === '2') { // 顶部 tar_t = tri_t - (tar_h - tri_h) / 2 + off_y } else { // 底部 tar_t = tri_t - (tar_h - tri_h) + off_y } if (strFirst === '1' || strFirst === '8' || strFirst === '4') { // 左侧 tar_l = tri_l - tar_w - off_x - cor_w cor_l = tri_l - cor_w - off_x - 1 } else { // 右侧 tar_l = tri_l + tri_w - off_x + cor_w cor_l = tri_l + tri_w + off_x + 1 } cor_t = pos.top - (cor_h - tri_h) / 2 } else { tar_l = tri_l + off_x tar_t = tri_t + off_y } } else { switch (strNewDirect) { case 'top': { tar_t = tri_t - off_y - tar_h - cor_h if (strFirst == '1') { tar_l = tri_l - off_x } else if (strFirst === '5') { tar_l = tri_l - (tar_w - tri_w) / 2 - off_x } else { tar_l = tri_l - (tar_w - tri_w) - off_x } cor_t = tri_t - cor_h - off_y - 1 cor_l = tri_l - (cor_w - tri_w) / 2 break } case 'right': { tar_l = tri_l + tri_w + off_x + cor_w if (strFirst == '2') { tar_t = tri_t + off_y } else if (strFirst === '6') { tar_t = tri_t - (tar_h - tri_h) / 2 + off_y } else { tar_t = tri_t - (tar_h - tri_h) + off_y } cor_l = tri_l + tri_w + off_x + 1 cor_t = tri_t - (cor_h - tri_h) / 2 break } case 'bottom': { tar_t = tri_t + tri_h + off_y + cor_h if (strFirst == '4') { tar_l = tri_l + off_x } else if (strFirst === '7') { tar_l = tri_l - (tar_w - tri_w) / 2 + off_x } else { tar_l = tri_l - (tar_w - tri_w) + off_x } cor_t = tri_t + tri_h + off_y + 1 cor_l = tri_l - (cor_w - tri_w) / 2 break } case 'left': { tar_l = tri_l - tar_w - off_x - cor_w if (strFirst == '2') { tar_t = tri_t - off_y } else if (strFirst === '6') { tar_t = tri_t - (tar_w - tri_w) / 2 - off_y } else { tar_t = tri_t - (tar_h - tri_h) - off_y } cor_l = tar_l + cor_w cor_t = tri_t - (tar_w - cor_w) / 2 break } } } // 尖角的显示 if (cor_h && cor_w && this.corner) { this.corner.css({ left: cor_l, top: cor_t, zIndex: this.s.zIndex + 1 }) } // 浮动框显示 this.target.css({ position: 'absolute', left: tar_l, top: tar_t, zIndex: this.s.zIndex }) return this }, createSharp: function (dir) { var bgColor; var bdColor; var color1 = ''; var color2 = '' var objReverse = { left: 'right', right: 'left', bottom: 'top', top: 'bottom' }; var dirReverse = objReverse[dir] || 'top' if (this.target) { bgColor = this.target.css('background-color') if (parseInt(this.target.css('border-' + dirReverse + '-width')) > 0) { bdColor = this.target.css('border-' + dirReverse + '-color') } if (bdColor && bdColor !== 'transparent') { color1 = 'style="color:' + bdColor + ';"' } else { color1 = 'style="display:none;"' } if (bgColor && bgColor !== 'transparent') { color2 = 'style="color:' + bgColor + ';"' } else { color2 = 'style="display:none;"' } } var html = '
' + '' + '' + '
' if (!$('#floatCorner_' + dir).size()) { $('body').append($(html)) } this.corner = $('#floatCorner_' + dir) return this }, targetHold: function () { if (this.s.hoverHold) { var delay = parseInt(this.s.hideDelay, 10) || 200 if (this.target) { this.target.hover(function () { o.flagDisplay = true }, function () { if (o.timerHold) { clearTimeout(o.timerHold) } o.flagDisplay = false o.targetHold() }) } o.timerHold = setTimeout(function () { o.displayDetect.call(o) }, delay) } else { this.displayDetect() } return this }, loading: function () { this.target = $('
') this.targetShow() this.target.removeData('width').removeData('height') return this }, displayDetect: function () { // 显示与否检测与触发 if (!this.flagDisplay && this.display) { this.targetHide() this.timerHold = null } return this }, targetShow: function () { o.cornerClear() this.display = true this.container().setWidth().position() this.target.show() if ($.isFunction(this.s.showCall)) { this.s.showCall.call(this.trigger, this.target) } return this }, targetHide: function () { this.display = false this.targetClear() this.cornerClear() if ($.isFunction(this.s.hideCall)) { this.s.hideCall.call(this.trigger) } this.target = null this.trigger = null this.s = {} this.targetProtect = false return this }, targetClear: function () { if (this.target) { if (this.target.data('width')) { this.target.removeData('width').removeData('height') } if (this.targetProtect) { // 保护孩子 this.target.children().hide().appendTo($('body')) } this.target.unbind().hide() } }, cornerClear: function () { if (this.corner) { // 使用remove避免潜在的尖角颜色冲突问题 this.corner.remove() } }, target: null, trigger: null, s: {}, cacheData: {}, targetProtect: false } $.powerFloat = {} $.powerFloat.hide = function () { o.targetHide() } var defaults = { width: 'auto', // 可选参数:inherit,数值(px) offsets: { x: 0, y: 0 }, zIndex: 999, eventType: 'hover', // 事件类型,其他可选参数有:click, focus showDelay: 0, // 鼠标hover显示延迟 hideDelay: 0, // 鼠标移出隐藏延时 hoverHold: true, hoverFollow: false, // true或是关键字x, y targetMode: 'common', // 浮动层的类型,其他可选参数有:ajax, list, remind target: null, // target对象获取来源,优先获取,如果为null,则从targetAttr中获取。 targetAttr: 'rel', // target对象获取来源,当targetMode为list时无效 container: null, // 转载target的容器,可以使用"plugin"关键字,则表示使用插件自带容器类型 reverseSharp: false, // 是否反向小三角的显示,默认ajax, remind是显示三角的,其他如list和自定义形式是不显示的 position: '4-1', // trigger-target edgeAdjust: true, // 边缘位置自动调整 showCall: $.noop, hideCall: $.noop } })(jQuery)