简要:本文主要介绍event.js中暴露出来的api,包括各种注册,移除事件方法和手动触发事件的方法,这些api都是调用了上一篇所讲到的底层api。
/** * 绑定事件,应直接采用on * 源自1.9版本前jquery的绑定事件的区分: bind()是直接绑定在元素上 .live()则是通过冒泡的方式来绑定到元素上的。更适合列表类型的,绑定到document DOM节点上。和.bind()的优势是支持动态数据。 .delegate()则是更精确的小范围使用事件代理,性能优于.live() .on()则是1.9版本整合了之前的三种方式的新事件绑定机制 * @param event * @param data * @param callback * @returns {*} */ $.fn.bind = function(event, data, callback){ return this.on(event, data, callback) } /** * 解绑事件,应直接用off * @param event * @param callback * @returns {*} */ $.fn.unbind = function(event, callback){ return this.off(event, callback) } /** * 绑定一次性事件 * @param event * @param selector * @param data * @param callback * @returns {*} */ $.fn.one = function(event, selector, data, callback){ return this.on(event, selector, data, callback, 1) } /** * 小范围冒泡绑定事件,应直接采用on */ $.fn.delegate = function(selector, event, callback){ return this.on(event, selector, callback) } /** * 解绑事件,应直接用off */ $.fn.undelegate = function(selector, event, callback){ return this.off(event, selector, callback) } /** * 冒泡到document.body绑定事件,应直接采用on * @param event * @param callback * @returns {*} */ $.fn.live = function(event, callback){ $(document.body).delegate(this.selector, event, callback) return this } /** * 在doument.body解绑事件,应直接用off */ $.fn.die = function(event, callback){ $(document.body).undelegate(this.selector, event, callback) return this } /** * 扩展Zepto on监听事件方法 * 元素上绑定一个或多个事件的事件处理函数 * 注意: 方法参数不应超过5个,超过5个,应该用arguments。5个是惯例。if或for或闭包嵌套层也不应超过5层 * @param event 事件集 字符串/ * @param selector 子选择器 * @param data event.data * @param callback 事件响应函数 * @param one 内部用, $.fn.one用。标记一次性事件 * @returns {*} */ //this.on(event, selector, data, callback, 1) $.fn.on = function(event, selector, data, callback, one){ var autoRemove, delegator, $this = this //event是对象{click:fn},支持这种方式我觉得没多大用 if (event && !isString(event)) { $.each(event, function(type, fn){ $this.on(type, selector, data, fn, one) }) return $this } //选择器非字符串 callback非方法 //未传data on('click','.ss',function(){}) // on('click',{ss:'ss'},function(){}) if (!isString(selector) && !isFunction(callback) && callback !== false) callback = data, data = selector, selector = undefined //data传了function 或未传 // on('click','.ss',function(){}) 或 on('click','.ss',false) if (callback === undefined || data === false) callback = data, data = undefined //callback传了false,转换成false函数 if (callback === false) callback = returnFalse //遍历元素, return $this.each(function(_, element){ //如果是一次性,先删掉事件,再执行事件 if (one) autoRemove = function(e){ remove(element, e.type, callback) return callback.apply(this, arguments) } //传递了选择器 if (selector) delegator = function(e){ //以element元素为容器,以事件源为起点,往上冒泡找到匹配selector的元素 // match 响应函数对应的事件源 var evt, match = $(e.target).closest(selector, element).get(0) // selector能找到,且不是容器,即不是绑定事件的上下文,即$('.parent').on('click','.son',fn)形式。开始处理委托。 if (match && match !== element) { //createProxy(e) 创建event代理对象 currentTarget指向selector元素,liveFired指向绑定事件的容器element evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) // 执行事件响应函数 // autoRemove触发一次事件响应函数后自动销毁。 callback触发事件响应函数 // [evt].concat(slice.call(arguments, 1))响应函数的参数数组 return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1))) } } add(element, event, callback, data, selector, delegator || autoRemove) }) } /** * 移除事件响应函数 * @param event * @param selector * @param callback * @returns {*} */ $.fn.off = function(event, selector, callback){ var $this = this //是对象,遍历移除 if (event && !isString(event)) { $.each(event, function(type, fn){ $this.off(type, selector, fn) }) return $this } // 是函数 // this.off("click",function(){}) if (!isString(selector) && !isFunction(callback) && callback !== false) callback = selector, selector = undefined if (callback === false) callback = returnFalse return $this.each(function(){ //元素遍历移除 remove(this, event, callback, selector) }) } /** * 触发事件 * @param event 事件类型 * @param args * @returns {*} */ $.fn.trigger = function(event, args){ //修正event为事件对象 event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event) //传参 event._args = args return this.each(function(){ // handle focus(), blur() by calling them directly //如果事件是focus blur if (event.type in focus && typeof this[event.type] == "function") this[event.type]() // items in the collection might not be DOM elements // 支持浏览器原生触发事件API //DOM源码 // /** // @param {Event} event // @return {boolean} // */ // EventTarget.prototype.dispatchEvent = function(event) {}; else if ('dispatchEvent' in this) this.dispatchEvent(event) //模拟触发事件 else $(this).triggerHandler(event, args) }) } // triggers event handlers on current element just as if an event occurred, // doesn't trigger an actual event, doesn't bubble /** * 触发事件,不能冒泡 * @param event event对象 * @param args 传参 * @returns {*} */ $.fn.triggerHandler = function(event, args){ var e, result this.each(function(i, element){ //修正事件对象 e = createProxy(isString(event) ? $.Event(event) : event) e._args = args e.target = element //找到此元素上此事件类型上的事件响应函数集,遍历,触发 $.each(findHandlers(element, event.type || event), function(i, handler){ //调用 handler.proxy执行事件 result = handler.proxy(e) //如果event调用了immediatePropagationStopped(),终止后续事件的响应 if (e.isImmediatePropagationStopped()) return false }) }) return result } // shortcut methods for `.bind(event, fn)` for each event type /** * 给常用事件生成便捷方法 * @param event * @param args * @returns {*} */ ;('focusin focusout focus blur load resize scroll unload click dblclick '+ 'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+ 'change select keydown keypress keyup error').split(' ').forEach(function(event) { $.fn[event] = function(callback) { return (0 in arguments) ? //有callback回调,是绑定事件,否则,触发事件 , // 不用on?on才通用啊 ,bind也是调用on //$.fn.bind = function(event, data, callback){ // return this.on(event, data, callback) // } this.bind(event, callback) : this.trigger(event) } })
on:
首先是针对不同的参数输入情况做出对应的重新赋值,这里是一个小的技巧:一般对于不同的参数做出判断是这样的:
input(arg1,null,arg3,null,arg5)
function input(arg1,arg2,arg3,arg4,arg5){
if(arg1 == null){......}
if(arg2 == null){......}
}
而event.js里面使用的是这种方式:input(arg1,arg3,arg5),这里我暂不知道这两种方式有什么区别,请知道的人不吝告知。
其次:是针对one,和selector的两种情况做出相应处理(其实是对程序员输入的响应函数做出相应的封装)
这里对含有shelector的情况做一下分析:
var callback = delegator || fn handler.proxy = function(e){ //修正event //这里对e的属性做出扩展, e = compatible(e) //如果是阻止所有事件触发 if (e.isImmediatePropagationStopped()) return e.data = data //缓存数据 //执行回调函数,context:element,arguments:event,e._args(默认是undefind,trigger()时传递的参数) var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args)) //当事件响应函数返回false时,阻止浏览器默认操作和冒泡 if (result === false) e.preventDefault(), e.stopPropagation() return result } //传递了选择器 if (selector) delegator = function(e){ //以element元素为容器,以事件源为起点,往上冒泡找到匹配selector的元素 // match 响应函数对应的事件源 var evt, match = $(e.target).closest(selector, element).get(0) // selector能找到,且不是容器,即不是绑定事件的上下文,即$('.parent').on('click','.son',fn)形式。开始处理委托。 if (match && match !== element) { //createProxy(e) 创建event代理对象 currentTarget指向selector元素,liveFired指向绑定事件的容器element evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element}) // 执行事件响应函数 // autoRemove触发一次事件响应函数后自动销毁。 callback触发事件响应函数 // [evt].concat(slice.call(arguments, 1))响应函数的参数数组 return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1))) } }
1:首先当事件触发的时候,原生事件对象e会被扩展3个属性(冒泡,阻止默认行为等),并且将事件注册时传入的数据data赋值给
2:调用delegator方法,并传入原生事件对象e(若事件由trigger来触发,则将trigger的形参_arg挂到e中)。
3:在delegator方法中,首先会获取事件源满足selector条件的父节点match,然后创建原生事件对象e的一个副本evt,并将evt作为参数传递给程序员自定义的事件响应函数去执行
4:所以,在我们自定义方法中的形参e并非原生事件对象,e.preventDefault(), e.stopPropagation()在这里是没有用的,可以通过return false来达到效果。
bind:
bind主要用于针对具体的dom绑定事件,响应函数中的this就是绑定的dom本身,这里的e就是原生的事件对象,如图:
live:
它先将事件绑定到document上,当某一时刻事件被触发,事件冒泡到document上,此时live将事件的响应权交到满足selector条件的节点上去执行。而满足selector条件的节点在事件被触发前可不必存在dom中,所以live相当于为将来的指定节点做出事件响应。此功能用在动态删除与插入的集合上效果会非常好。
delegate:
$('#container').delegate('a', 'click', function() { alert("That tickles!") });
此方法 是live的一般形式,live是绑定到document,而delegate绑定到指定的container,能够缩小事件捕获的范围,其效率要比live大一点。
关于bind,live,delegate有关的更详细的信息可参考:http://kb.cnblogs.com/page/94469/
来源:https://www.cnblogs.com/zhutao/p/5736749.html