简要:本文主要介绍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