/** * @TODO: */ ;(function($, window, document, undefined) { 'use strict'; var Dialog = (function() { /** * 弹窗构造函数 * @param {dom obj} element 调用对象 * @param {json obj} options 弹窗配置项 */ function Dialog(element, options) { this.$element = $(element); this.settings = $.extend({}, $.fn.dialog.defaults, options); } Dialog.prototype = { /** * 初始化弹窗 */ _init: function() { var self = this; clearTimeout(self.autoCloseTimer); self.isHided = false; // 是否已经隐藏 self.tapBug = self._hasTapBug(); // 是否有点透 BUG self.platform = mobileUtil.platform; // 访问设备平台 self.dislogStyle = self.settings.style==='default' ? self.platform : self.settings.style; // 弹窗风格, 默认自动判断平台; 否则, 为指定平th === 0) { var styleContent = '.body-no-scroll { position: absolute; overflow: hidden; width: 100%; }'; $('head').append(''); } self._renderDOM(); self._bindEvents(); }, /** * 渲染弹窗 DOM 结构 */ _renderDOM: function() { var self = this; self.settings.onBeforeShow(); self._createDialogDOM(self.settings.type); self.settings.onShow(); }, /** * 绑定弹窗相关事件 */ _bindEvents: function() { var self = this; // 确定按钮关闭弹窗 self.$confirmBtn.bind('click', function() { var callback = self.settings.onClickConfirmBtn(); if (callback || callback === undefined) { self.closeDialog(); } }); // 取消按钮关闭弹窗 self.$cancelBtn.bind('click', function(ev) { var callback = self.settings.onClickCancelBtn(); if (callback || callback === undefined) { self.closeDialog(); } }); // 关闭按钮关闭弹窗 self.$closeBtn.on(mobileUtil.tapEvent, function(ev) { var callback = self.settings.onClickCloseBtn(); if (callback || callback === undefined) { self.closeDialog(); } }).on('touchend', function(ev) { ev.preventDefault(); }); // 遮罩层关闭弹窗 if (self.settings.overlayClose) { $(document).on(mobileUtil.tapEvent, '.dialog-overlay', function(ev) { self.closeDialog(); }); } // 自动关闭弹窗 if(self.settings.autoClose > 0){ self._autoClose(); } // 删除弹窗和 tap 点透 BUG 遮罩层, 在隐藏弹窗的动画结束后执行 $(document).on('webkitAnimationEnd MSAnimationEnd animationend', '.dialog-content', function() { if (self.isHided) { self.removeDialog(); if (self.tapBug) { self._removeTapOverlayer(); } } }); // 为自定义按钮组绑定回调函数 if (self.settings.buttons.length) { $.each(self.settings.buttons, function(index, item) { self.$dialogContentFt.children('button').eq(index).on(mobileUtil.tapEvent, function(ev) { ev.preventDefault(); var callback = item.callback(); if (callback || callback === undefined) { self.closeDialog(); } }); }); } // 如果弹窗有最大高度设置项, 在窗口大小改变时, 重新设置弹窗最大高度 $(window).on("onorientationchange" in window ? "orientationchange" : "resize", function() { if (self.settings.contentScroll) { setTimeout(function() { self._resetDialog(); }, 200); } }); // 阻止 body 内容滑动 $(document).on('touchmove', function(e) { if (self.$dialog.find($(e.target)).length){ return false; } else { return true; } }); // 弹窗有最大高度设置项, 设置提示内容滑动 if (self.settings.contentScroll) { self._contentScrollEvent(); } }, /** * 根据弹窗类型, 创建弹窗 DOM 结构 * @param {string} dialogType 弹窗类型 */ _createDialogDOM: function(dialogType) { var self = this; self.$dialog = $('
'); self.$dialogOverlay = $('
'); self.$dialogContent = $('
'); self.$dialogTitle = $('

'+ self.settings.titleText +'

'); self.$dialogContentFt = $('
'); self.$dialogContentBd = $('
'); self.$closeBtn = $('
close
'); self.$confirmBtn = $(''); self.$cancelBtn = $(''); switch(dialogType) { case 'alert': // 添加 alert 类型弹窗标识 self.$dialog.addClass('dialog-modal'); // 显示遮罩层 if (self.settings.overlayShow) { self.$dialog.append(self.$dialogOverlay); } // 显示标题 if (self.settings.titleShow) { self.$dialogContent.append(self.$dialogTitle); } // 显示关闭按钮 if (self.settings.closeBtnShow) { self.$dialogTitle.append(self.$closeBtn); } self.$dialogContentBd.html(self.settings.content); self.$dialogContentFt.append(self.$confirmBtn); self.$dialogContent.append(self.$dialogContentBd).append(self.$dialogContentFt); self.$dialog.append(self.$dialogContent); $('body').append(self.$dialog); if (self.settings.bodyNoScroll) { $('body').addClass('body-no-scroll'); } // 设置弹窗提示内容最大高度 if (self.settings.contentScroll) { self._setDialogContentHeight(); } break; case 'confirm': // 添加 confirm 类型弹窗标识 self.$dialog.addClass('dialog-modal'); // 显示遮罩层 if (self.settings.overlayShow) { self.$dialog.append(self.$dialogOverlay); } // 显示标题 if (self.settings.titleShow) { self.$dialogContent.append(self.$dialogTitle); } // 显示关闭按钮 if (self.settings.closeBtnShow) { self.$dialogTitle.append(self.$closeBtn); } // 按钮: 如果有设置自定义按钮组, 则用自定义按钮组; 否则用默认的"确定"与"取消"按钮 if (self.settings.buttons.length) { var buttonGroupHtml = ''; $.each(self.settings.buttons, function(index, item) { buttonGroupHtml += ''; }); self.$dialogContentFt.append(buttonGroupHtml).addClass(self.settings.buttonStyle); } else { self.$dialogContentFt.append(self.$cancelBtn).append(self.$confirmBtn).addClass(self.settings.buttonStyle); } self.$dialogContentBd.html(self.settings.content); self.$dialogContent.append(self.$dialogContentBd).append(self.$dialogContentFt); self.$dialog.append(self.$dialogContent); $('body').append(self.$dialog); // 设置弹窗提示内容最大高度 if (self.settings.contentScroll) { self._setDialogContentHeight(); } if (self.settings.bodyNoScroll) { $('body').addClass('body-no-scroll'); } break; case 'toast': // 添加 toast 类型弹窗标识 self.$dialog.addClass('dialog-toast'); // 显示遮罩层 if (self.settings.overlayShow) { self.$dialog.append(self.$dialogOverlay); } // 弹窗内容 HTML, 默认为 content; 如果设置 icon 与 text, 则覆盖 content 的设置 var toastContentHtml = $(self.settings.content); if (self.settings.infoIcon !== '' && self.settings.infoText !== '') { toastContentHtml = $(''+ self.settings.infoText +''); } else if (self.settings.infoIcon === '' && self.settings.infoText !== '') { toastContentHtml = $(''+ self.settings.infoText +''); } else if (self.settings.infoIcon !== '' && self.settings.infoText === '') { toastContentHtml = $(''); } self.$dialogContentBd.append(toastContentHtml); self.$dialogContent.append(self.$dialogContentBd); self.$dialog.append(self.$dialogContent); $('body').append(self.$dialog); if (self.settings.bodyNoScroll) { $('body').addClass('body-no-scroll'); } break; case 'notice': // 添加 toast 类型弹窗标识 self.$dialog.addClass('dialog-notice'); // 底部显示的 toast if (self.settings.position==='bottom') { self.$dialog.addClass('dialog-notice-bottom'); } // 显示遮罩层 if (self.settings.overlayShow) { self.$dialog.append(self.$dialogOverlay); } // 弹窗内容 HTML, 默认为 content; 如果设置 icon 与 text, 则覆盖 content 的设置 var noticeContentHtml = $(self.settings.content); if (self.settings.infoIcon !== '' && self.settings.infoText !== '') { noticeContentHtml = $(''+ self.settings.infoText +''); } else if (self.settings.infoIcon === '' && self.settings.infoText !== '') { noticeContentHtml = $(''+ self.settings.infoText +''); } else if (self.settings.infoIcon !== '' && self.settings.infoText === '') { noticeContentHtml = $(''); } self.$dialogContentBd.append(noticeContentHtml); self.$dialogContent.append(self.$dialogContentBd); self.$dialog.append(self.$dialogContent); $('body').append(self.$dialog); if (self.settings.bodyNoScroll) { $('body').addClass('body-no-scroll'); } break; default: break; } }, /** * 设置弹窗内容最大高度 * 延迟执行, 避免获取相关尺寸不正确 */ _setDialogContentHeight: function() { var self = this; setTimeout(function() { var dialogDefaultContentHeight = self.$dialogContentBd.height(); var dialogContentMaxHeight = self._getDialogContentMaxHeight(); self.$dialogContentBd.css({ 'max-height': dialogContentMaxHeight, }).addClass('content-scroll'); // 提示内容大于最大高度时, 添加底部按钮顶部边框线标识 class; 反之, 删除 if (dialogDefaultContentHeight > dialogContentMaxHeight) { self.$dialogContentFt.addClass('dialog-content-ft-border'); } else { self.$dialogContentFt.removeClass('dialog-content-ft-border'); } }, 80); }, /** * 获取弹窗内容最大高度 * @return height */ _getDialogContentMaxHeight: function() { var self = this; var winHeight = $(window).height(), dialogContentHdHeight = self.$dialogTitle.height(), dialogContentFtHeight = self.$dialogContentFt.height(), dialogContentBdHeight = winHeight - dialogContentHdHeight - dialogContentFtHeight - 60; // 最大高度取偶数 dialogContentBdHeight = dialogContentBdHeight%2===0 ? dialogContentBdHeight : dialogContentBdHeight - 1; return dialogContentBdHeight; }, /** * 重置弹窗, 在窗口大小发生变化时触发 */ _resetDialog: function() { var self = this; self._setDialogContentHeight(); }, /** * 有最大高度弹窗的提示内容滑动 */ _contentScrollEvent: function() { var self = this; var isTouchDown = false; // 初始位置 var position = { x: 0, y: 0, top: 0, left: 0 }; // 监听滑动相关事件 $(document) .on('touchstart mousedown', '.content-scroll', function(ev) { var touch = ev.changedTouches ? ev.changedTouches[0] : ev; isTouchDown = true; position.x = touch.clientX; position.y = touch.clientY; position.top = $(this).scrollTop(); position.left = $(this).scrollLeft(); return false; }) .on('touchmove mousemove', '.content-scroll', function(ev) { var touch = ev.changedTouches ? ev.changedTouches[0] : ev; if (!isTouchDown) { // 未按下 return false; } else { // 要滑动的距离 = 已经滑动的距离 - (当前坐标 - 按下坐标) var moveTop = position.top - (touch.clientY - position.y); var moveLeft = position.left - (touch.clientX - position.x); $(this).scrollTop(moveTop).scrollLeft(moveLeft); } }) .on('touchend mouseup', '.content-scroll', function(ev) { ev.preventDefault(); isTouchDown = false; }); }, /** * 自动关闭弹窗 */ _autoClose: function() { var self = this; self.autoCloseTimer = setTimeout(function(){ self.closeDialog(); }, self.settings.autoClose); }, /** * 关闭弹窗 */ closeDialog: function() { var self = this; self.isHided = true; self.settings.onBeforeClosed(); self.$dialog.addClass('dialog-close').removeClass('dialog-open'); if (self.tapBug) { self._appendTapOverlayer(); } }, /** * 删除弹窗 * @public method */ removeDialog: function() { var self = this; self.$dialog.remove(); self.isHided = false; self.settings.onClosed(); // 重新初始化默认配置 self.settings = $.fn.dialog.defaults; if (self.settings.bodyNoScroll) { $('body').removeClass('body-no-scroll'); } }, /** * 更改 toast 和 notice 类型弹窗内容 * @public method * @param {string} content 弹窗内容, 可以是HTML * @param {string} infoIcon 弹窗提示图标 * @param {string} infoText 弹窗提示文字 * @param {int} autoClose 自动关闭的延迟时间 * @param {fn} onBeforeClosed 关闭前回调函数 * @param {fn} onClosed 关闭后回调函数 */ update: function (settings) { var self = this; clearTimeout(self.autoCloseTimer); // 设置默认值,并且指向给对象的默认值 self.settings = $.extend({}, $.fn.dialog.defaults, settings); // 通过 content 更改弹窗内容 if (self.settings.content !== '') { self.$dialogContentBd.html(self.settings.content); } // 通过设置 infoIcon 与 infoText 更改弹窗内容, 会覆盖 content 的设置 var $infoIcon = self.$dialogContentBd.find('.info-icon'); var $infoText = self.$dialogContentBd.find('.info-text'); $infoIcon.attr({'src': self.settings.infoIcon}); $infoText.html(self.settings.infoText); // 重新为更改后的 DOM 元素绑定事件 self._bindEvents(); }, /** * 是否有点透 BUG * 条件: 安卓手机并且版本号小于4.4 * @return Boolean */ _hasTapBug: function() { return mobileUtil.isAndroid && (mobileUtil.version < 4.4); }, /** * 添加点透遮罩层, 解决点透 BUG */ _appendTapOverlayer: function() { var self = this; self.$tapBugOverlayer = $('.solve-tap-bug'); if (!self.$tapBugOverlayer.length) { self.$tapBugOverlayer = $('
'); $('body').append(self.$tapBugOverlayer); } }, /** * 删除点透遮罩层, 延迟执行的时间大于移动端的 click 触发时间 */ _removeTapOverlayer: function() { var self = this; setTimeout(function() { self.$tapBugOverlayer.remove(); }, 350); } }; return Dialog; })(); /**---------------------------- * 私有方法 ----------------------------*/ /** * 移动端相关数据 =>> mobileUtil 对象 * 是否是安卓 : isAndroid * 是否是IOS : isIOS * 是否是移动端: isMobile * 设备平台 : platform [ ios 或 android ] * 事件类型 : tapEvent [ tapEvent 或 click ] * 系统版本号 : version [ 如: ios 9.1 或 andriod 6.0 ] * 是否支持 touch 事件: isSupportTouch */ var mobileUtil = (function(window) { var UA = window.navigator.userAgent, isAndroid = /android|adr/gi.test(UA), isIOS = /iphone|ipod|ipad/gi.test(UA) && !isAndroid, isMobile = isAndroid || isIOS, platform = isIOS ? 'ios' : (isAndroid ? 'android' : 'default'), isSupportTouch = "ontouchend" in document ? true : false; var reg = isIOS ? (/os [\d._]*/gi):(/android [\d._]*/gi), verinfo = UA.match(reg), version = (verinfo+"").replace(/[^0-9|_.]/ig,"").replace(/_/ig,"."); return { isIOS: isIOS, isAndroid: isAndroid, isMobile: isMobile, platform: platform, version: parseFloat(version), isSupportTouch: isSupportTouch, tapEvent: isMobile && isSupportTouch ? 'tapEvent' : 'click' }; })(window); $.fn.dialog = function(options) { var self = this; return this.each(function(){ var $this = $(this), instance = window.jQuery ? $this.data('dialog') : $.fn.dialog.lookup[$this.data('dialog')]; if (!instance) { var obj = new Dialog(this, options); obj._init(); if (window.jQuery) { $this.data('dialog', obj); } else { $.fn.dialog.lookup[++$.fn.dialog.lookup.i] = obj; $this.data('dialog', $.fn.dialog.lookup.i); instance = $.fn.dialog.lookup[$this.data('dialog')]; } } else { var obj = new Dialog(this, options); obj._init(); } if (typeof options === 'string') { instance[options](); } // 提供外部调用公共方法 self.close = function(){ obj.closeDialog(); }; self.update = function(settings){ obj.update(settings); }; }); }; if (!window.jQuery) { $.fn.dialog.lookup = {i: 0}; } /** * 插件默认值 */ $.fn.dialog.defaults = { type : 'notice', // 弹窗的类型 [ alert: 确定; confirm: 确定/取消; toast: 状态提示; notice: 提示信息 ] style : 'default', // alert 与 confirm 弹窗的风格 [ default: 根据访问设备平台; ios: ios 风格; android: MD design 风格 ] titleShow : true, // 是否显示标题 titleText : '提示', // 标题文字 bodyNoScroll : false, // body内容不可以滚动 closeBtnShow : false, // 是否显示关闭按钮 content : '', // 弹窗提示内容, 值可以是 HTML 内容 contentScroll: true, // alert 与 confirm 弹窗提示内容是否限制最大高度, 使其可以滚动 dialogClass : '', // 弹窗自定义 class autoClose : 2000, // 弹窗自动关闭的延迟时间(毫秒)。0: 不自动关闭; 大于0: 自动关闭弹窗的延迟时间 overlayShow : true, // 是否显示遮罩层 overlayClose : false, // 是否可以点击遮罩层关闭弹窗 buttonStyle : 'side', // 按钮排版样式 [ side: 并排; stacked: 堆叠 ] buttonTextConfirm : 'yes', // 确定按钮文字 buttonTextCancel : 'no', // 取消按钮文字 buttonClassConfirm: '', // 确定按钮自定义 class buttonClassCancel : '', // 取消按钮自定义 class buttons : [], // confirm 弹窗自定义按钮组, 会覆盖"确定"与"取消"按钮; 单个 button 对象可设置 name [ 名称 ]、class [ 自定义class ]、callback [ 点击执行的函数 ] infoIcon: '', // toast 与 notice 弹窗的提示图标, 值为图标的路径。不设置=不显示 infoText: '', // toast 与 notice 弹窗的提示文字, 会覆盖 content 的设置 position: 'center', // notice 弹窗的位置, [ center: 居中; bottom: 底部 ] onClickConfirmBtn: function(){}, // “确定”按钮的回调函数 onClickCancelBtn : function(){}, // “取消”按钮的回调函数 onClickCloseBtn : function(){}, // “关闭”按钮的回调函数 onBeforeShow : function(){}, // 弹窗显示前的回调函数 onShow : function(){}, // 弹窗显示后的回调函数 onBeforeClosed : function(){}, // 弹窗关闭前的回调函数 onClosed : function(){} // 弹窗关闭后的回调函数 }; })(window.jQuery || window.Zepto, window, document);