mass Framework event模块
此模块此用于屏蔽DOM事件的浏览器差异的,并提供强大的事件代理机制(delegate,undelegate,live,die):
//========================================== // 事件模块 by 司徒正美 2011.8.21 //========================================== (function(global,DOC){ var dom = global[DOC.URL.replace(/(#.+|\W)/g,'')]; dom.define("event", "emitter,travel",function(){ dom.log("加载dom.event模块成功") var types = "contextmenu,click,dblclick,mouseout,mouseover,mouseenter,mouseleave,mousemove,mousedown,mouseup,mousewheel," + "abort,error,load,unload,resize,scroll,change,input,select,reset,submit,"+"blur,focus,focusin,focusout,"+"keypress,keydown,keyup"; dom.eventSupport = function( eventName,el ) { el = el || DOC.createElement("div"); eventName = "on" + eventName; var ret = eventName in el; if (el.setAttribute && !ret ) { el.setAttribute(eventName, "return;"); ret = typeof el[eventName] === "function"; } el = null; return ret; }; var events = dom.events, specials = events.special, rword = dom.rword; //用于在标准浏览器下模拟mouseenter与mouseleave //现在除了IE系列支持mouseenter/mouseleave/focusin/focusout外 //opera11也支持这四个事件,同时它们也成为w3c DOM3 Event的规范 //详见http://www.filehippo.com/pl/download_opera/changelog/9476/ //http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html var brokenMouseEnter = !dom.eventSupport("mouseenter"); "mouseenter_mouseover,mouseleave_mouseout".replace(rword,function(types){ types = types.split("_"); var obj = specials[ types[0] ] = { setup:function(target){ dom.bind(target, types[1],function(){ obj[uuid(target)+"_handle"] = arguments.callee; e = events.fix(e); var parent = e.relatedTarget; try { while ( parent && parent !== target ) { parent = parent.parentNode; } if ( parent !== target ) { e.type = types[0]; events.handle.call(target,e); } } catch(e) { }; }); }, teardown :function(target){ events.teardown(target, types[1],obj[uuid(target)+"_handle"]); delete obj[uuid(target)+"_handle"] } }; obj.liveSetup = obj.setup; obj.liveTeardown = obj.teardown; if(!brokenMouseEnter){ delete obj.setup; delete obj.teardown; } }); if(!dom.eventSupport("focusin")){ "focusin_focus,focusout_blur".replace(rword,function(types){ types = types.split("_"); var attaches = 0; function handler(e) { events.fire(e.target, types[0]); } specials[ types[0] ] = { type: types[1], setup:function(){ if ( attaches++ === 0 ) { DOC.addEventListener( types[1], handler, true ); } }, teardown: function() { if ( --attaches === 0 ) { DOC.removeEventListener( types[1], handler, true ); } } } }); } //http://www.w3help.org/zh-cn/causes/SD9013 //https://prototype.lighthouseapp.com/projects/8886/tickets/697-eventfire-to-support-all-events-not-only-custom-ones //http://www.quirksmode.org/dom/events/scroll.html if(!dom.eventSupport("mousewheel")){ specials.mousewheel = { type : "DOMMouseScroll"//FF } } //取得事件发送器的uniqueID function uuid(target){ return dom.data(target,"uuid") } //submit事件的冒泡情况----IE6-9 :form ;FF: document; chrome: window;safari:window;opera:window if(!dom.eventSupport("submit") && !DOC.dispatchEvent ){ var submitCode = dom.oneObject("13,108"); var submitButton = dom.oneObject("submit,image"); var submitInput = dom.oneObject("text,password,textarea"); var submitRoom = specials.submit = { liveSetup: function(target){ //将事件回调函数保存到一个在卸载时可以找到它的地方 submitRoom[uuid(target)+"_handle"] = function(){ var e = events.fix(event), el = e.target, type = el.type; if( el.form && ( submitButton[type] || submitCode[ e.which ] && submitInput[type]) ){ dom.log("模拟IE下的submit事件冒泡 "); e.type = "submit"; events.handle.call(target, e); } } "onclick,onkeypress".replace(rword,function(type){ target.attachEvent(type ,submitRoom[uuid(target)+"_handle"] ); }) }, liveTeardown: function(target){ "onclick,onkeypress".replace(rword,function(type){ target.detachEvent(type ,submitRoom[uuid(target)+"_handle"]||dom.noop ); }); delete submitRoom[uuid(target)+"_handle"]; } } } //reset事件的冒泡情况----FF与opera能冒泡到document,其他浏览器只能到form if(!dom.eventSupport("reset") ){ var resetRoom = specials.reset = { liveSetup:DOC.dispatchEvent ? 0 : function(target){ resetRoom[uuid(target)+"_handle"] = function(){ var e = events.fix(event), el = e.target; if( el.form && (e.which === 27 || el.type == "reset") ){ dom.log("模拟reset事件冒泡 "); e.type = "reset"; events.handle.call(target, e); } } "onclick,onkeypress".replace(rword,function(type){ target.attachEvent(type , resetRoom[uuid(target)+"_handle"]); }); }, liveTeardown:DOC.dispatchEvent ? 0 : function(target){ "onclick,onkeypress".replace(rword,function(type){ target.dettachEvent(type ,resetRoom[uuid(target)+"_handle"]); }) delete resetRoom[uuid(target)+"_handle"]; } } } //change事件的冒泡情况----FF与opera能冒泡到document,其他浏览器完全不冒泡 if(!dom.eventSupport("change") ){ function getVal( node ) { var type = node.type, val = node.value; if ( type === "radio" || type === "checkbox" ) { val = node.checked; } else if ( type === "select-multiple" ) { val = node.selectedIndex === -1 ? "": dom.lang( node.options).map(function( node ) { return node.selected; }).join("-") ; } else if ( type === "select-one" ) { val = node.selectedIndex; } return val; } function changeHandle( e ) { var eType = e.type,node = e.srcElement,nType = node.type //如果不是表单元素,又或者处于只读状态下,直接返回 if ( !node.form || node.readOnly ) { return; } if ( eType === "keydown" ) { var flag = false; if ( (e.keyCode === 13 && nType !== "textarea" ) || (e.keyCode === 32 && ( nType === "checkbox" || nType === "radio")) || nType === "select-multiple" ) { flag = true; } if(!flag){ return; } } //取得之前的数据 var oldData = dom.data( node, "_change_data" ); //获取现在的数据 var newData = getVal(node); // focusout是不用重设数据的 if ( eType !== "focusout" || nType !== "radio" ) { dom.data( node, "_change_data", newData ); } if ( newData === undefined || newData === oldData ) { return; } if ( oldData != null || newData ) { e = events.fix(e); e.type = "change"; events.handle.call(node, e); } } var changeRoom = specials.change = { liveSetup: function(node){ //此事件用于收集数据 node.attachEvent("onbeforeactivate", function( ) { changeRoom[uuid(node)+event.type+"_handler"] = arguments.callee; dom.data( node, "_change_data", getVal(node) ); }); ////当点击目标元素时,再点击页面的其他位置时,依次会发生如下事件beforeactivate click | beforedeactivate focusout "onfocusout,onbeforedeactivate,onclick,onkeydown".replace(rword,function(type){ node.attachEvent(type ,function(){ changeRoom[uuid(node)+event.type+"_handler"] = arguments.callee; changeHandle.call(node, event); }); }); }, liveTeardown:function(node){ var uid = uuid(node); "focusout,beforedeactivate,click,keydown,beforeactivate".replace(rword,function(type){ node.detachEvent("on"+type,changeRoom[uid+type+"_handler"]||dom.noop ); delete changeRoom[uid+type+"_handler"] }); } } } //当一个元素,或者其内部任何一个元素获得焦点的时候会触发这个事件。 //这跟focus事件区别在于,他可以在父元素上检测子元素获取焦点的情况。 //================================= dom.include({ toggleClick:function(/*fn1,fn2,fn3*/){ var fns = [].slice.call(arguments), i = 0; return this.click(function(e){ var fn = fns[i++] || fns[i = 0, i++]; fn.call(this,e); }) }, hover: function( fnOver, fnOut ) { return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); } }); "bind,unbind,fire".replace(rword, function(method){ dom.fn[method] = function(){ var args = arguments; return this.each(function(target){ events[method].apply(target, args); }); } }); //事件代理的原理就是把事件侦听器绑定在顶层元素上,通过判定下层冒泡或捕获上来的事件源元素的特定,执行相应的回调 "delegate,undelegate".replace(rword,function(method){ dom.fn[method] = function(expr,type,callback){ var special = events.special[ type ] || {}; var bag = { live: expr, type: type, callback:callback } bag.uuid = callback.uuid = dom.uuid++; //选取可用的事件类型,如mouseenter在标准浏览器下就不能用,需要用mouseover冒充 type = special.type || type return this.each(function(node){ events[method === "delegate" ? "bind" : "unbind"].apply(node, [type, bag, true]); }); } }); "live,die".replace(rword,function(method){ dom.fn[method] = function(type,callback){ var expr = this.selector; var doc = this.doc || DOC; if(!expr){ expr = dom.noop; } return dom(doc)[method === "live" ? "delegate" : "undelegate"](expr, type, callback); } }); types.replace(rword,function(type){ dom.fn[type] = function(callback, phase, trust){ return this.bind(type, callback, phase, trust); } }); }); })(this,this.document);
机器瞎学/数据掩埋/模式混淆/人工智障/深度遗忘/神经掉线/计算机幻觉/专注单身二十五年