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 类型的事件向上冒泡。
代码如下 复制代码