JQuery

1个成员

jQuery event事件对象用法浅析

发表于 2017-01-26 2663 次查看
本人对jquery的event不懂,搜索了很多关于jquery event事件介绍,下面我来记录一下,有需要了解jquery event事件用法的朋友可参考,希望此文件对各位有所帮助。

最终传入事件处理程序的 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 时则会阻止当前事件下该节点的后续事件处理程序执行。

最后来个简单的

 代码如下 复制代码

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="gbk">
    <title>jQuery event 对象浅析</title>
</head>
<body>
    <a id="J_btn" href="javascript:;">点我吧!</a>
    <script type="text/javascript" src="../js/lib/jquery.js"></script>
    <script type="text/javascript">

        $("body").delegate("#J_btn", "click", function() {
            alert("冒泡至 BODY");
        });

        var $btn = $("#J_btn");

        $btn.click(function(event) {
            event.stopImmediatePropagation();
            alert("第一次点击");
        });

        $btn.click(function() {
            alert("第二次点击");
        });

    </script>
</body>
</html>

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

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() 方法实现事件的冒泡。

本文完。

发表回复
你还没有登录,请先登录注册