easydialog.js

时间:2024-08-21 20:36:08
/**
* easyDialog v2.2
* Url : http://stylechen.com/easydialog-v2.0.html
* Author : chenmnkken@gmail.com
* Date : 2012-04-22
*/
(function( win, undefined ){ var doc = win.document,
docElem = doc.documentElement; var easyDialog = function(){ var body = doc.body,
isIE = !-[1,], // 判断IE6/7/8 不能判断IE9
isIE6 = isIE && /msie 6/.test( navigator.userAgent.toLowerCase() ), // 判断IE6
uuid = 1,
expando = 'cache' + ( +new Date() + "" ).slice( -8 ), // 生成随机数
cacheData = {
/**
* 1 : {
* eclick : [ handler1, handler2, handler3 ];
* clickHandler : function(){ //... };
* }
*/
}; var Dialog = function(){}; Dialog.prototype = {
// 参数设置
getOptions : function( arg ){
var i,
options = {},
// 默认参数
defaults = {
container: null, // string / object 弹处层内容的id或内容模板
overlay: true, // boolean 是否添加遮罩层
drag: true, // boolean 是否绑定拖拽事件
fixed: true, // boolean 是否静止定位
follow: null, // string / object 是否跟随自定义元素来定位
followX: 0, // number 相对于自定义元素的X坐标的偏移
followY: 0, // number 相对于自定义元素的Y坐标的偏移
autoClose: 0, // number 自动关闭弹出层的时间
lock: false, // boolean 是否允许ESC键来关闭弹出层
callback: null // function 关闭弹出层后执行的回调函数
/**
* container为object时的参数格式
* container : {
* header : '弹出层标题',
* content : '弹出层内容',
* yesFn : function(){}, // 确定按钮的回调函数
* noFn : function(){} / true, // 取消按钮的回调函数
* yesText : '确定', // 确定按钮的文本,默认为‘确定’
* noText : '取消' // 取消按钮的文本,默认为‘取消’
* }
*/
}; for( i in defaults ){
options[i] = arg[i] !== undefined ? arg[i] : defaults[i];
}
Dialog.data( 'options', options );
return options;
}, // 防止IE6模拟fixed时出现抖动
setBodyBg : function(){
if( body.currentStyle.backgroundAttachment !== 'fixed' ){
body.style.backgroundImage = 'url(about:blank)';
body.style.backgroundAttachment = 'fixed';
}
}, // 防止IE6的select穿透
appendIframe : function(elem){
elem.innerHTML = '<iframe style="position:absolute;left:0;top:0;width:100%;height:100%;z-index:-1;border:0 none;filter:alpha(opacity=0)"></iframe>';
}, /**
* 设置元素跟随定位
* @param { Object } 跟随的DOM元素
* @param { String / Object } 被跟随的DOM元素
* @param { Number } 相对于被跟随元素的X轴的偏移
* @param { Number } 相对于被跟随元素的Y轴的偏移
*/
setFollow : function( elem, follow, x, y ){
follow = typeof follow === 'string' ? doc.getElementById( follow ) : follow;
var style = elem.style;
style.position = 'absolute';
style.left = Dialog.getOffset( follow, 'left') + x + 'px';
style.top = Dialog.getOffset( follow, 'top' ) + y + 'px';
}, /**
* 设置元素固定(fixed) / 绝对(absolute)定位
* @param { Object } DOM元素
* @param { Boolean } true : fixed, fasle : absolute
*/
setPosition : function( elem, fixed ){
var style = elem.style;
style.position = isIE6 ? 'absolute' : fixed ? 'fixed' : 'absolute';
if( fixed ){
if( isIE6 ){
style.setExpression( 'top','fuckIE6=document.documentElement.scrollTop+document.documentElement.clientHeight/2+"px"' );
}
else{
style.top = '50%';
}
style.left = '50%';
}
else{
if( isIE6 ){
style.removeExpression( 'top' );
}
style.top = docElem.clientHeight/2 + Dialog.getScroll( 'top' ) + 'px';
style.left = docElem.clientWidth/2 + Dialog.getScroll( 'left' ) + 'px';
}
}, /**
* 创建遮罩层
* @return { Object } 遮罩层
*/
createOverlay : function(){
var overlay = doc.createElement('div'),
style = overlay.style; style.cssText = 'margin:0;padding:0;border:none;width:100%;height:100%;background:#333;opacity:0.6;filter:alpha(opacity=60);z-index:9999;position:fixed;top:0;left:0;'; // IE6模拟fixed
if(isIE6){
body.style.height = '100%';
style.position = 'absolute';
style.setExpression('top','fuckIE6=document.documentElement.scrollTop+"px"');
} overlay.id = 'overlay';
return overlay;
}, /**
* 创建弹出层
* @return { Object } 弹出层
*/
createDialogBox : function(){
var dialogBox = doc.createElement('div');
dialogBox.style.cssText = 'margin:0;padding:0;border:none;z-index:10000;';
dialogBox.id = 'easyDialogBox';
return dialogBox;
}, /**
* 创建默认的弹出层内容模板
* @param { Object } 模板参数
* @return { Object } 弹出层内容模板
*/
createDialogWrap : function( tmpl ){
// 弹出层标题
var header = tmpl.header ?
'<h4 class="easyDialog_title" id="easyDialogTitle"><a href="javascript:void(0)" title="关闭窗口" class="close_btn" id="closeBtn">&times;</a>' + tmpl.header + '</h4>' :
'',
// 确定按钮
yesBtn = typeof tmpl.yesFn === 'function' ?
'<button class="btn_highlight" id="easyDialogYesBtn">' + ( typeof tmpl.yesText === 'string' ? tmpl.yesText : '确定' ) + '</button>' :
'',
// 取消按钮
noBtn = typeof tmpl.noFn === 'function' || tmpl.noFn === true ?
'<button class="btn_normal" id="easyDialogNoBtn">' + ( typeof tmpl.noText === 'string' ? tmpl.noText : '取消' ) + '</button>' :
'',
// footer
footer = yesBtn === '' && noBtn === '' ? '' :
'<div class="easyDialog_footer">' + noBtn + yesBtn + '</div>', dialogTmpl = [
'<div class="easyDialog_content">',
header,
'<div class="easyDialog_text">' + tmpl.content + '</div>',
footer,
'</div>'
].join(''), dialogWrap = doc.getElementById( 'easyDialogWrapper' ),
rScript = /<[\/]*script[\s\S]*?>/ig; if( !dialogWrap ){
dialogWrap = doc.createElement( 'div' );
dialogWrap.id = 'easyDialogWrapper';
dialogWrap.className = 'easyDialog_wrapper';
}
dialogWrap.innerHTML = dialogTmpl.replace( rScript, '' );
return dialogWrap;
}
}; /**
* 设置并返回缓存的数据 关于缓存系统详见:http://stylechen.com/cachedata.html
* @param { String / Object } 任意字符串或DOM元素
* @param { String } 缓存属性名
* @param { Anything } 缓存属性值
* @return { Object }
*/
Dialog.data = function( elem, val, data ){
if( typeof elem === 'string' ){
if( val !== undefined ){
cacheData[elem] = val;
}
return cacheData[elem];
}
else if( typeof elem === 'object' ){
// 如果是window、document将不添加自定义属性
// window的索引是0 document索引为1
var index = elem === win ? 0 :
elem.nodeType === 9 ? 1 :
elem[expando] ? elem[expando] :
(elem[expando] = ++uuid), thisCache = cacheData[index] ? cacheData[index] : ( cacheData[index] = {} ); if( data !== undefined ){
// 将数据存入缓存中
thisCache[val] = data;
}
// 返回DOM元素存储的数据
return thisCache[val];
}
}; /**
* 删除缓存
* @param { String / Object } 任意字符串或DOM元素
* @param { String } 要删除的缓存属性名
*/
Dialog.removeData = function( elem, val ){
if( typeof elem === 'string' ){
delete cacheData[elem];
}
else if( typeof elem === 'object' ){
var index = elem === win ? 0 :
elem.nodeType === 9 ? 1 :
elem[expando]; if( index === undefined ) return;
// 检测对象是否为空
var isEmptyObject = function( obj ) {
var name;
for ( name in obj ) {
return false;
}
return true;
},
// 删除DOM元素所有的缓存数据
delteProp = function(){
delete cacheData[index];
if( index <= 1 ) return;
try{
// IE8及标准浏览器可以直接使用delete来删除属性
delete elem[expando];
}
catch ( e ) {
// IE6/IE7使用removeAttribute方法来删除属性(document会报错)
elem.removeAttribute( expando );
}
}; if( val ){
// 只删除指定的数据
delete cacheData[index][val];
if( isEmptyObject( cacheData[index] ) ){
delteProp();
}
}
else{
delteProp();
}
}
}; // 事件处理系统
Dialog.event = { bind : function( elem, type, handler ){
var events = Dialog.data( elem, 'e' + type ) || Dialog.data( elem, 'e' + type, [] );
// 将事件函数添加到缓存中
events.push( handler );
// 同一事件类型只注册一次事件,防止重复注册
if( events.length === 1 ){
var eventHandler = this.eventHandler( elem );
Dialog.data( elem, type + 'Handler', eventHandler );
if( elem.addEventListener ){
elem.addEventListener( type, eventHandler, false );
}
else if( elem.attachEvent ){
elem.attachEvent( 'on' + type, eventHandler );
}
}
}, unbind : function( elem, type, handler ){
var events = Dialog.data( elem, 'e' + type );
if( !events ) return; // 如果没有传入要删除的事件处理函数则删除该事件类型的缓存
if( !handler ){
events = undefined;
}
// 如果有具体的事件处理函数则只删除一个
else{
for( var i = events.length - 1, fn = events[i]; i >= 0; i-- ){
if( fn === handler ){
events.splice( i, 1 );
}
}
}
// 删除事件和缓存
if( !events || !events.length ){
var eventHandler = Dialog.data( elem, type + 'Handler' );
if( elem.addEventListener ){
elem.removeEventListener( type, eventHandler, false );
}
else if( elem.attachEvent ){
elem.detachEvent( 'on' + type, eventHandler );
}
Dialog.removeData( elem, type + 'Handler' );
Dialog.removeData( elem, 'e' + type );
}
}, // 依次执行事件绑定的函数
eventHandler : function( elem ){
return function( event ){
event = Dialog.event.fixEvent( event || win.event );
var type = event.type,
events = Dialog.data( elem, 'e' + type ); for( var i = 0, handler; handler = events[i++]; ){
if( handler.call(elem, event) === false ){
event.preventDefault();
event.stopPropagation();
}
}
}
}, // 修复IE浏览器支持常见的标准事件的API
fixEvent : function( e ){
// 支持DOM 2级标准事件的浏览器无需做修复
if ( e.target ) return e;
var event = {}, name;
event.target = e.srcElement || document;
event.preventDefault = function(){
e.returnValue = false;
};
event.stopPropagation = function(){
e.cancelBubble = true;
};
// IE6/7/8在原生的window.event中直接写入自定义属性
// 会导致内存泄漏,所以采用复制的方式
for( name in e ){
event[name] = e[name];
}
return event;
}
}; /**
* 首字母大写转换
* @param { String } 要转换的字符串
* @return { String } 转换后的字符串 top => Top
*/
Dialog.capitalize = function( str ){
var firstStr = str.charAt(0);
return firstStr.toUpperCase() + str.replace( firstStr, '' );
}; /**
* 获取滚动条的位置
* @param { String } 'top' & 'left'
* @return { Number }
*/
Dialog.getScroll = function( type ){
var upType = this.capitalize( type );
return docElem['scroll' + upType] || body['scroll' + upType];
}; /**
* 获取元素在页面中的位置
* @param { Object } DOM元素
* @param { String } 'top' & 'left'
* @return { Number }
*/
Dialog.getOffset = function( elem, type ){
var upType = this.capitalize( type ),
client = docElem['client' + upType] || body['client' + upType] || 0,
scroll = this.getScroll( type ),
box = elem.getBoundingClientRect(); return Math.round( box[type] ) + scroll - client;
}; /**
* 拖拽效果
* @param { Object } 触发拖拽的DOM元素
* @param { Object } 要进行拖拽的DOM元素
*/
Dialog.drag = function( target, moveElem ){
// 清除文本选择
var clearSelect = 'getSelection' in win ? function(){
win.getSelection().removeAllRanges();
} : function(){
try{
doc.selection.empty();
}
catch( e ){};
}, self = this,
event = self.event,
isDown = false,
newElem = isIE ? target : doc,
fixed = moveElem.style.position === 'fixed',
_fixed = Dialog.data( 'options' ).fixed; // mousedown
var down = function( e ){
isDown = true;
var scrollTop = self.getScroll( 'top' ),
scrollLeft = self.getScroll( 'left' ),
edgeLeft = fixed ? 0 : scrollLeft,
edgeTop = fixed ? 0 : scrollTop; Dialog.data( 'dragData', {
x : e.clientX - self.getOffset( moveElem, 'left' ) + ( fixed ? scrollLeft : 0 ),
y : e.clientY - self.getOffset( moveElem, 'top' ) + ( fixed ? scrollTop : 0 ),
// 设置上下左右4个临界点的位置
// 固定定位的临界点 = 当前屏的宽、高(下、右要减去元素本身的宽度或高度)
// 绝对定位的临界点 = 当前屏的宽、高 + 滚动条卷起部分(下、右要减去元素本身的宽度或高度)
el : edgeLeft, // 左临界点
et : edgeTop, // 上临界点
er : edgeLeft + docElem.clientWidth - moveElem.offsetWidth, // 右临界点
eb : edgeTop + docElem.clientHeight - moveElem.offsetHeight // 下临界点
}); if( isIE ){
// IE6如果是模拟fixed在mousedown的时候先删除模拟,节省性能
if( isIE6 && _fixed ){
moveElem.style.removeExpression( 'top' );
}
target.setCapture();
} event.bind( newElem, 'mousemove', move );
event.bind( newElem, 'mouseup', up ); if( isIE ){
event.bind( target, 'losecapture', up );
} e.stopPropagation();
e.preventDefault(); }; event.bind( target, 'mousedown', down ); // mousemove
var move = function( e ){
if( !isDown ) return;
clearSelect();
var dragData = Dialog.data( 'dragData' ),
left = e.clientX - dragData.x,
top = e.clientY - dragData.y,
et = dragData.et,
er = dragData.er,
eb = dragData.eb,
el = dragData.el,
style = moveElem.style; // 设置上下左右的临界点以防止元素溢出当前屏
style.marginLeft = style.marginTop = '0px';
style.left = ( left <= el ? el : (left >= er ? er : left) ) + 'px';
style.top = ( top <= et ? et : (top >= eb ? eb : top) ) + 'px';
e.stopPropagation();
}; // mouseup
var up = function( e ){
isDown = false;
if( isIE ){
event.unbind( target, 'losecapture', arguments.callee );
}
event.unbind( newElem, 'mousemove', move );
event.unbind( newElem, 'mouseup', arguments.callee );
if( isIE ){
target.releaseCapture();
// IE6如果是模拟fixed在mouseup的时候要重新设置模拟
if( isIE6 && _fixed ){
var top = parseInt( moveElem.style.top ) - self.getScroll( 'top' );
moveElem.style.setExpression('top',"fuckIE6=document.documentElement.scrollTop+" + top + '+"px"');
}
}
e.stopPropagation();
};
}; var timer, // 定时器
// ESC键关闭弹出层
escClose = function( e ){
if( e.keyCode === 27 ){
extend.close();
}
},
// 清除定时器
clearTimer = function(){
if( timer ){
clearTimeout( timer );
timer = undefined;
}
}; var extend = {
open : function(){
var $ = new Dialog(),
options = $.getOptions( arguments[0] || {} ), // 获取参数
event = Dialog.event,
docWidth = docElem.clientWidth,
docHeight = docElem.clientHeight,
self = this,
overlay,
dialogBox,
dialogWrap,
boxChild; clearTimer(); // ------------------------------------------------------
// ---------------------插入遮罩层-----------------------
// ------------------------------------------------------ // 如果页面中已经缓存遮罩层,直接显示
if( options.overlay ){
overlay = doc.getElementById( 'overlay' );
if( !overlay ){
overlay = $.createOverlay();
body.appendChild( overlay );
if( isIE6 ){
$.appendIframe( overlay );
}
}
overlay.style.display = 'block';
} if(isIE6){
$.setBodyBg();
} // ------------------------------------------------------
// ---------------------插入弹出层-----------------------
// ------------------------------------------------------ // 如果页面中已经缓存弹出层,直接显示
dialogBox = doc.getElementById( 'easyDialogBox' );
if( !dialogBox ){
dialogBox = $.createDialogBox();
body.appendChild( dialogBox );
} if( options.follow ){
var follow = function(){
$.setFollow( dialogBox, options.follow, options.followX, options.followY );
}; follow();
event.bind( win, 'resize', follow );
Dialog.data( 'follow', follow );
if( overlay ){
overlay.style.display = 'none';
}
options.fixed = false;
}
else{
$.setPosition( dialogBox, options.fixed );
}
dialogBox.style.display = 'block'; // ------------------------------------------------------
// -------------------插入弹出层内容---------------------
// ------------------------------------------------------ // 判断弹出层内容是否已经缓存过
dialogWrap = typeof options.container === 'string' ?
doc.getElementById( options.container ) :
$.createDialogWrap( options.container ); boxChild = dialogBox.getElementsByTagName('*')[0]; if( !boxChild ){
dialogBox.appendChild( dialogWrap );
}
else if( boxChild && dialogWrap !== boxChild ){
boxChild.style.display = 'none';
body.appendChild( boxChild );
dialogBox.appendChild( dialogWrap );
} dialogWrap.style.display = 'block'; var eWidth = dialogWrap.offsetWidth,
eHeight = dialogWrap.offsetHeight,
widthOverflow = eWidth > docWidth,
heigthOverflow = eHeight > docHeight; // 强制去掉自定义弹出层内容的margin
dialogWrap.style.marginTop = dialogWrap.style.marginRight = dialogWrap.style.marginBottom = dialogWrap.style.marginLeft = '0px'; // 居中定位
if( !options.follow ){
dialogBox.style.marginLeft = '-' + (widthOverflow ? docWidth/2 : eWidth/2) + 'px';
dialogBox.style.marginTop = '-' + (heigthOverflow ? docHeight/2 : eHeight/2) + 'px';
}
else{
dialogBox.style.marginLeft = dialogBox.style.marginTop = '0px';
} // 防止select穿透固定宽度和高度
if( isIE6 && !options.overlay ){
dialogBox.style.width = eWidth + 'px';
dialogBox.style.height = eHeight + 'px';
} // ------------------------------------------------------
// --------------------绑定相关事件----------------------
// ------------------------------------------------------
var closeBtn = doc.getElementById( 'closeBtn' ),
dialogTitle = doc.getElementById( 'easyDialogTitle' ),
dialogYesBtn = doc.getElementById('easyDialogYesBtn'),
dialogNoBtn = doc.getElementById('easyDialogNoBtn'); // 绑定确定按钮的回调函数
if( dialogYesBtn ){
event.bind( dialogYesBtn, 'click', function( event ){
if( options.container.yesFn.call(self, event) !== false ){
self.close();
}
});
} // 绑定取消按钮的回调函数
if( dialogNoBtn ){
var noCallback = function( event ){
if( options.container.noFn === true || options.container.noFn.call(self, event) !== false ){
self.close();
}
};
event.bind( dialogNoBtn, 'click', noCallback );
// 如果取消按钮有回调函数 关闭按钮也绑定同样的回调函数
if( closeBtn ){
event.bind( closeBtn, 'click', noCallback );
}
}
// 关闭按钮绑定事件
else if( closeBtn ){
event.bind( closeBtn, 'click', self.close );
} // ESC键关闭弹出层
if( !options.lock ){
event.bind( doc, 'keyup', escClose );
}
// 自动关闭弹出层
if( options.autoClose && typeof options.autoClose === 'number' ){
timer = setTimeout( self.close, options.autoClose );
}
// 绑定拖拽(如果弹出层内容的宽度或高度溢出将不绑定拖拽)
if( options.drag && dialogTitle && !widthOverflow && !heigthOverflow ){
dialogTitle.style.cursor = 'move';
Dialog.drag( dialogTitle, dialogBox );
} // 确保弹出层绝对定位时放大缩小窗口也可以垂直居中显示 if( !options.follow && !options.fixed ){
var resize = function(){
$.setPosition( dialogBox, false );
};
// 如果弹出层内容的宽度或高度溢出将不绑定resize事件
if( !widthOverflow && !heigthOverflow ){
event.bind( win, 'resize', resize );
}
Dialog.data( 'resize', resize );
} // 缓存相关元素以便关闭弹出层的时候进行操作
Dialog.data( 'dialogElements', {
overlay : overlay,
dialogBox : dialogBox,
closeBtn : closeBtn,
dialogTitle : dialogTitle,
dialogYesBtn : dialogYesBtn,
dialogNoBtn : dialogNoBtn
});
}, close : function(){
var options = Dialog.data( 'options' ),
elements = Dialog.data( 'dialogElements' ),
event = Dialog.event; clearTimer();
// 隐藏遮罩层
if( options.overlay && elements.overlay ){
elements.overlay.style.display = 'none';
}
// 隐藏弹出层
elements.dialogBox.style.display = 'none';
// IE6清除CSS表达式
if( isIE6 ){
elements.dialogBox.style.removeExpression( 'top' );
} // ------------------------------------------------------
// --------------------删除相关事件----------------------
// ------------------------------------------------------
if( elements.closeBtn ){
event.unbind( elements.closeBtn, 'click' );
} if( elements.dialogTitle ){
event.unbind( elements.dialogTitle, 'mousedown' );
} if( elements.dialogYesBtn ){
event.unbind( elements.dialogYesBtn, 'click' );
} if( elements.dialogNoBtn ){
event.unbind( elements.dialogNoBtn, 'click' );
} if( !options.follow && !options.fixed ){
event.unbind( win, 'resize', Dialog.data('resize') );
Dialog.removeData( 'resize' );
} if( options.follow ){
event.unbind( win, 'resize', Dialog.data('follow') );
Dialog.removeData( 'follow' );
} if( !options.lock ){
event.unbind( doc, 'keyup', escClose );
}
// 执行callback
if(typeof options.callback === 'function'){
options.callback.call( extend );
}
// 清除缓存
Dialog.removeData( 'options' );
Dialog.removeData( 'dialogElements' );
}
}; return extend; }; // ------------------------------------------------------
// ---------------------DOM加载模块----------------------
// ------------------------------------------------------
var loaded = function(){
win.easyDialog = easyDialog();
}, doScrollCheck = function(){
if ( doc.body ) return; try {
docElem.doScroll("left");
} catch(e) {
setTimeout( doScrollCheck, 1 );
return;
}
loaded();
}; (function(){
if( doc.body ){
loaded();
}
else{
if( doc.addEventListener ){
doc.addEventListener( 'DOMContentLoaded', function(){
doc.removeEventListener( 'DOMContentLoaded', arguments.callee, false );
loaded();
}, false );
win.addEventListener( 'load', loaded, false );
}
else if( doc.attachEvent ){
doc.attachEvent( 'onreadystatechange', function(){
if( doc.readyState === 'complete' ){
doc.detachEvent( 'onreadystatechange', arguments.callee );
loaded();
}
});
win.attachEvent( 'onload', loaded );
var toplevel = false;
try {
toplevel = win.frameElement == null;
} catch(e) {} if ( docElem.doScroll && toplevel ) {
doScrollCheck();
}
}
}
})(); })( window, undefined ); // 2012-04-12 修复跟随定位缩放浏览器时无法继续跟随的BUG
// 2012-04-22 修复弹出层内容的尺寸大于浏览器当前屏尺寸的BUG

相关文章