一聚教程网:一个值得你收藏的教程网站

热门教程

jQuery event事件对象用法浅析

时间:2022-11-14 22:03:27 编辑:袖梨 来源:一聚教程网

最终传入事件处理程序的 event 其实已经被 jQuery 做过标准化处理,

其原有的事件对象则被保存于 event 对象的 originalEvent 属性之中,

每个 event 都是 jQuery.Event 的实例,其原型链中保存有六个方法,

代码如下 复制代码

jQuery.Event.prototype = {
preventDefault: function() {
// 取消事件默认行为
},
stopPropagation: function() {
// 取消事件向上冒泡
},
stopImmediatePropagation: function() {
this.isImmediatePropagationStopped = returnTrue;
this.stopPropagation();
},
isDefaultPrevented: returnFalse,
isPropagationStopped: returnFalse,
isImmediatePropagationStopped: returnFalse
};

对于取消事件默认行为与向上冒泡大家肯定不陌生,让我们把重点放在第三个方法上,

从源码上我们知道其调用了stopPropagation方法,所以他第一个作用就是取消冒泡。

代码如下 复制代码

for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
matched = handlerQueue[ i ];
...
for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
...
}
}

让我先来简单阐述下 jQuery 的事件绑定机制,这样能让大家更好理解他的第二个作用。

使用 jQuery 绑定的事件处理程序会按其类型与绑定顺序存于节点相应的events对象中,

当事件触发时则使用 $._data 取出events中对应事件的处理程序列队以便后续遍历执行。

内层循环的 matched.matches 中保存事件触发时当前节点需执行的事件处理程序列队,

所以当 isImmediatePropagationStopped 为 true 时则会阻止当前事件下该节点的后续事件处理程序执行。

最后来个简单的

代码如下 复制代码





jQuery event 对象浅析


javascript:;">点我吧!



希望能帮助大家更好的理解!

jQuery.event 事件机制 focusin/ focusout 事件


首先来看 jQuery.simulate() 方法,该方法基于原生事件实现可冒泡事件。源码如下:

代码如下 复制代码
simulate: function(type, elem, event, bubble) {
var evt = jQuery.extend(new jQuery.Event(), event, {
type: type,
isSimulated: true,
originalEvent: {}
}
);

if (bubble) {
jQuery.event.trigger(evt, null, elem); // 直接程序触发,这里可冒泡
} else {
jQuery.event.dispatch.call(elem, evt);
}

if (evt.isDefaultPrevented()) {
event.preventDefault(); //由于模拟的缘故,基础对象应该同时更改
}
}

focusin/ focusout 可冒泡事件实现原理是,在事件捕获阶段监视特定元素的 focus/ blur 动作,捕获行为发生在 document 对象上,这样才能有效地实现所有元素都能可以冒泡的事件。 一旦程序监视到存在 focus/ blur 行为,就会触发绑定在 document 元素上的事件处理程序,该事件处理程序在内部调用 simulate 逻辑触发事件冒泡,以实现我们希望的可冒泡事件。源码如下:

代码如下 复制代码

jQuery.each({focus: "focusin", blur: "focusout"}, function(orig, fix) {
var attaches = 0,
handler = function(event) {
jQuery.event.simulate(fix, event.target, jQuery.event.fix(event), true);
};

jQuery.event.special[fix] = {
setup: function() {
if (attaches++ === 0) {
document.addEventListener(orig, handler, true);
// 在 focus/blur 事件的捕获阶段添加事件监听,直接在 document 上监视,同时防止过多绑定
}
},
teardown: function() {
if (--attaches === 0) {
document.removeEventListener(orig, handler, true);
}
}
};
});

下面我们来分析我们应用 focusin 的过程,以便更清楚地理解其行为。

代码如下 复制代码
$("p").focusin(function() {
alert('yes!');
});

当我们第一次给 focusin 事件添加事件处理程序时,jQuery 会在 document 上会添加 handler 函数。之后每次用户触发 focus 事件时,浏览器都将捕获该事件并第一时间触发 handler 函数, handler 函数在其内部模拟了事件冒泡过程(trigger),由此实现可冒泡事件。

关于 focusin/focus 事件对 在 trigger 方法中的相关逻辑。源码如下:

代码如下 复制代码

trigger: function(event, data, elem, onlyHandlers) {
// ....
// rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
// 这里用于避免: 触发 elem.focus 默认行为时,二次触发 focusin 行为,因为已执行过。
     if (rfocusMorph.test(type + jQuery.event.triggered)) {
        return;
}
// ....
// 特殊事件,主要是解决 focus 机制。如果 trigger 返回 false,则直接返回
special = jQuery.event.special[type] || {};
if (!onlyHandlers && special.trigger && special.trigger.apply(elem, data) === false) {
return;
}
     if (!onlyHandlers && !special.noBubble && !jQuery.isWindow(elem)) {
var bubbleType = special.delegateType || type;
if (!rfocusMorph.test(bubbleType + type)) {
       // type= focus/blur 这样可以在 elem 上同时触发 focus 和 focusin 事件
cur = cur.parentNode;
}
// ...
}
// ....
},

一: 避免二次触发 focusin 事件,当我们执行 elem.focus() 时,document 会再次捕获该事件并尝试再触发 focusin 事件。

代码如下 复制代码
if (rfocusMorph.test(type + jQuery.event.triggered)) { return; }

二: 当 trigger('focus') 时,尽可能地应用原生事件,但不会冒泡。

代码如下 复制代码

if (!onlyHandlers && special.trigger && special.trigger.apply(elem, data) === false) {
return;
}

三: 如果不满足应用原生事件的条件,则在当前元素上触发 focus 和 focusin 事件,并以 focusin 类型的事件向上冒泡。

代码如下 复制代码
var bubbleType = special.delegateType || type;
// type= focus/blur, 这样可以在 elem 上同时触发 focus 和 focusin 事件
if (!rfocusMorph.test(bubbleType + type)) { cur = cur.parentNode; }event.type = i > 1 ? bubbleType : special.bindType || type;

二、 mouseenter/ mouseleave 高阶事件
mouseenter/ leave 高阶事件依赖于 special['mouseenter'].handle 方法来实现。在该方法内部,会有程序的判断逻辑,如果程序判断当前的 mouseover 事件满足特定条件,该事件就成为 mouseenter 事件,才调用对应的事件处理程序。

mouseenter/ leave 高阶事件依赖于原生的 mouseover/mouseouter 事件,如图所示:

其 handle 方法的源码如下:

代码如下 复制代码


handle: function(event) {
var ret,
target = this,
related = event.relatedTarget,
handleObj = event.handleObj;

// if related is outside the target.
// No relatedTarget if the mouse left/entered the browser window // 如果通过 trigger('mouseenter') 或 trigger('mouseover') 也会满足条件   if ( !related || (related !== target && !jQuery.contains(target, related)) ) {
event.type = handleObj.origType; // 在执行事件处理程序前必须修正事件的类型
ret = handleObj.handler.apply(this, arguments);
event.type = fix;
}

return ret;
}// 如果这是一个高阶事件,调用高阶事件的方法。内部有判断逻辑
ret = ((jQuery.event.special[handleObj.origType] || {}).handle || handleObj.handler).apply(matched.elem, args);
event.type = i > 1 ? bubbleType : special.bindType || type;

handle = (jQuery._data(cur, "events") || {})[event.type] && jQuery._data(cur, "handle");
// 高阶事件依赖于基础事件(原生事件),其结构依赖于 delegateType 或 bindType
if (handle) {
handle.apply(cur, data);
}

观察源码我们可以发现,当通过程序触发高阶事件时,比如 trigger('mouseenter'),实际上需要触发的是对应的原生事件。因此,下面的代码的结果会令你耳目一新,因为这里绑定的 mouseenter 都会有所响应:

代码如下 复制代码

var $inner = $('#inner');
var $outer = $('#outer');
$inner.on('mouseover', function(){
$(this).css('backgroundColor', '#444');
});
$inner.on('mouseenter', function(event){
alert(event.relatedTarget);
});
$outer.on('mouseenter', function(){
var $this = $(this);
$this.css('backgroundColor', '#000');
});
$("p").click(function () { $inner.trigger('mouseenter');} );

三、浏览器兼容性修正
依赖于 postDispatch 逻辑,调用 simulate() 方法实现事件的冒泡。

本文完。

热门栏目