JQuery源码解读__8__事件

154 阅读4分钟

1、实例

1、普通的事件绑定

<div id = 'box'>box</div>
$("#box").on("click",{name:'sunlike'},function(ev){   
 // 参数1 type 事件类型    
// 参数2 data 传入的参数
    // 参数3 fn 回调函数
     console.log(ev.data.name)
})
// 点击的时候输出sunlike
2、事件委托delegate()

<div id = 'box'>box</div>
$("body").delegate("#box","click",function(ev){  
  // 参数1 obj 实际要触发事件的节点  
  // 参数2 type 事件类型    
  // 参数3 fn 回调函数   
   console.log(123)})// 点击的时候输出123// 实际上是调用的是on// 
delegate:function(seletor,types,data,fn){// 
   return this.on(types,selector,data,fn)//
 }
3、事件委托on()

<div id = 'box'>box</div>
$("body").on("click","#box",{name:"sunlike"},function(ev){ 
    // 参数1 obj 实际要触发事件的节点
    // 参数2 type 事件类型
    // 参数3 data 传入参数
    // 参数4 fn 回调函数
     console.log(ev.data.name)
})
// 点击的时候输出sunlike

 4、同时绑定多个事件on()

$("#box").on({
    "click", function() {
        console.log(123)
    }
    },
    {
    "mouseover", function() {
        console.log(456)
    }
})
// 鼠标移入输出456
// 点击的时候输出123
5、只绑定一次事件one()
<div id = 'box'>box</div>
$("#box").one("click",{name:'sunlike'},function(ev){  
    // 参数1 type 事件类型  
    // 参数2 data 传入的参数
    // 参数3 fn 回调函数
     console.log(ev.data.name)
})
// 点击的时候输出sunlike
 6、trigger()主动触发事件

<input id = 'name'/>
$("#name").focus(function(){
    console.log(123)
})
$("#name").trigger("focus");
// 输出123
// 光标的的聚焦行为也会被触发
 7、triggerHandler主动触发事件

<input id = 'name'/>
$("#name").focus(function(){
    console.log(123)
})
$("#name").triggerHandler("focus");
// 输出123
// 光标聚焦行为没有触发
// 添加了阻止默认事件和冒泡事件
 8、trigger()主动触发自定义事件

<input id = 'name'/>
$("#name").show(function(){
    console.log(123)
})
$("#name").trigger("show");
// 输出123
9、实现简单的事件绑定、事件解绑

<div id = 'div1'>
    <span id = 'span1'></span>
</div>
window.onload = function(){
    let mDiv = document.getElementById("div1");
    let mSpan  =document.getElementById("span1");
    var bar = function(){
        console.log("bar");
    }
     var foo = function(){
        console.log("foo")
    }
    add(mDiv,"show",bar);
    add(mSpan,"click",foo);
    trigger(mDiv,"show");
}

//实现
function add(obj,types,fn){
    // 将事件添加到对应的事件列表里
    // 并且给节点添加事件
    obj.listeners = obj.listeners||{};
    obj.listeners[types] = obj.listeners[types]||[];
    obj.listeners[types].push(fn);
    obj.addEventListener(types,fn,false)
}
function remove(obj,types,fn){
    // 移除节点事件
    // 并且删除事件列表里对应的事件
    obj.removeEventListener(types,fn,false);
    delete obj.listeners[types];}function trigger(obj,types){
    // 循环执行事件列表里面的绑定的函数
    var arr  =obj.listeners[types];
    for(var i=0;i<arr.length;i++){ 
       arr[i]();
    }
}
// 大量事件挂载到DOM节点可能会造成内存泄漏
// 所以jq使用data数据缓存来保存事件数据
10、事件缓存的结构

// 代码
<div id = 'box'>
    <span id = 'span1'></span>
</div>
  $(function(){
     $("#div").on("click",function(a){
         console.log("普通点击事件a");
     })
     $("#div").on("click",function(b){
         console.log("普通事件b")
     })
     $("body").on("click","#box",function(c){
        console.log("body代理事件c")
    })
    $("#div").on("click","#span1",function(d){
        console.log("div代理事件e")
    })
    $("#div").on("click.aaa",function(e){
        console.log("命名空间aaa事件")
    })
     $("#div").on("mouseover",function(f){
         console.log("mouseover事件")
     })
     $("#div").on("mouseenter",function(g){
        console.log("mouseenter事件")
    })
})
// 数据缓存结构
var elemData  = {
    events:{
        'click':[
             // arr  有arr.length=2,arr.delegateCount = 1属性
             // delegate 事件委托的个数
            {
                // 委托的项放到首位
                data: undefined,
                guid:3, // 当前事件的唯一标识
                handler:function(d){},// 绑定的事件的事件函数
                namespace:"",//命名空间
                needsContext: false, 
               origType:"click", 
               selector:'span',
                type:"click"
            }, 
           { 
               data: undefined,
                guid:3,
                handler:function(){},
                namespace:"",
                needsContext: false,
                origType:"click",
                selector:undefined,
                type:"click"
            },
            {},
            {},
            {} 
       ] 
       'mouseover':[
            {},
            {
                data: undefined,
                guid:3,
                handler:function(){},
                namespace:"",
                needsContext: false,
                origType:"mouseenter",// 原始类型,
                selector:undefined, 
               type:"mouseover",//真正的已经模拟成mouseover
            }
        ],
         handle:function(e){
            //  真正的事件函数
         }
    }}

 11、命名空间

$("#div").on("click",function(){
    // 指定一个命名空间aaa
    console.log(1);})
$("#div").on("click.aaa",function(){
    // 指定一个命名空间aaa
    console.log(2)})
$("#div").trigger("click.aaa");
// 只输出1,因为只触发了aaa命名空间绑定的事件

12、同时绑定多个事件

<div id = 'box'>
    <span id = 'span1'></span>
</div>  
$("#div").on("click mouseover",function(){
    console.log("同时给节点绑定一个click和mouseover事件");
})
// 只要用空格隔开,事件类型的个数是没有限制的
13、pageX、pageY兼容

// 不是所有的浏览器都支持
event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 )
 - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
event.pageY = original.clientY + ( doc && doc.scrollTop  || body && body.scrollTop  || 0 )
 - ( doc && doc.clientTop  || body && body.clientTop  || 0 );
 14、keyCode、charCode

document.onkeyup(function(event){
    console.log(event.keyCode)
    console.log(event.charCode);
    // 兼容比较好的
})

2、源码实现

1、jQuery.Event = fucntion(src,props){}

// 事件对象(构造函数),采用的是面向对象的编程思想
jQuery.Event = function( src, props ) {
    // Allow instantiation without the 'new' keyword
    if ( !(this instanceof jQuery.Event) ) {
        return new jQuery.Event( src, props );
    }
    // Event object
    if ( src && src.type ) {
        this.originalEvent = src;
        this.type = src.type;
        // Events bubbling up the document may have been marked as prevented
        // by a handler lower down the tree; reflect the correct value.
        this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
            src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
    // Event type
    } else {
        this.type = src;
    } 
   // Put explicitly provided properties onto the event object
    // 将属性继承到当前对象
    if ( props ) {
        jQuery.extend( this, props );
    }
    // Create a timestamp if incoming event doesn't have one
    this.timeStamp = src && src.timeStamp || jQuery.now();
    // Mark it as fixed 
   // 设置缓存
    this[ jQuery.expando ] = true;
};

2、jQuery.Event.prototype = {}

// 事件对象原型(构造函数),采用的是面向对象的编程思想
jQuery.Event.prototype = {
    // 设置三个阻止事件的属性
    isDefaultPrevented: returnFalse,
    isPropagationStopped: returnFalse,
    isImmediatePropagationStopped: returnFalse,
    // 阻止冒泡事件
    preventDefault: function() {
        var e = this.originalEvent;
        this.isDefaultPrevented = returnTrue;
        if ( !e ) {
            return;
        }
        // preventDefault()函数存在
        if ( e.preventDefault ) {
            e.preventDefault();
        } else {
            // IE某些不支持preventDefault
            e.returnValue = false;
        }
    },
    // 阻止默认事件
    stopPropagation: function() {
        var e = this.originalEvent;
        this.isPropagationStopped = returnTrue;
        if ( !e ) {
            return;
        }
        //preventDefault函数存在
        if ( e.stopPropagation ) {
            e.stopPropagation();
        }
        // 兼容某些IE不支持preventDefault
        e.cancelBubble = true;
    },
    // 相同元素的其他事件会被阻止掉
    stopImmediatePropagation: function() {
        this.isImmediatePropagationStopped = returnTrue;
        this.stopPropagation();
    }};

3、jQuery.event = {}

jQuery.event = {
    global: {},
    add:function(){// 添加事件},
    remove:function(){// 事件移除},
    trigger:function(){ // 事件触发},
    dispatch:function(){// 分发事件},
    handlers:fucntion(){},
    fix:fucntion(){//对event事件进行兼容},
    fixHooks: {//事件兼容处理},
    keyHooks:{//键盘事件兼容处理},
    special:{//特殊事件处理}
    simulate:function(){// 事件模拟}
}

4、jQuery.fn.extend

// 暴露在外的实例函数
jQuery.fn.extend({
    on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
        var type, origFn;
        // Types can be a map of types/handlers
        if ( typeof types === "object" ) {
            // ( types-Object, selector, data ) 
           if ( typeof selector !== "string" ) {
                // ( types-Object, data )
                data = data || selector;
                selector = undefined;
            }
            for ( type in types ) {
                this.on( type, selector, data, types[ type ], one );
            }
            return this;
        }
        if ( data == null && fn == null ) {
            // ( types, fn )
            fn = selector;
            data = selector = undefined;
        } else if ( fn == null ) {
            if ( typeof selector === "string" ) {
                // ( types, selector, fn )
                fn = data;
                data = undefined;
            } else {
                // ( types, data, fn )
                fn = data;
                data = selector;
                selector = undefined;
            }
        }
        if ( fn === false ) {
            fn = returnFalse;
        } else if ( !fn ) {
            return this;
        }
        // one()事件绑定一次性事件
        if ( one === 1 ) {
           origFn = fn; 
           fn = function( event ) { 
               // 先把事件取消掉,再另外触发事件
                jQuery().off( event );
                return origFn.apply( this, arguments );
            };
            // Use same guid so caller can remove using origFn
            // 添加函数唯一标识,当方便移除函数
            fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
        }
        // 把事件列表使用add()函数进行进步一处理
        return this.each( function() {
            jQuery.event.add( this, types, fn, data, selector );
        });
    },
    one: function( types, selector, data, fn ) {
        return this.on( types, selector, data, fn, 1 );
    },
    off: function( types, selector, fn ) {
        var handleObj, type;
        if ( types && types.preventDefault && types.handleObj ) {
            // ( event )  dispatched jQuery.Event
            handleObj = types.handleObj;
            jQuery( types.delegateTarget ).off(
                handleObj.namespace ? handleObj.origType + "." + handleObj.namespace :
                handleObj.origType,
                handleObj.selector,
                handleObj.handler
            );
            return this;
        } 
       if ( typeof types === "object" ) {
            // ( types-object [, selector] )
            for ( type in types ) {
                this.off( type, selector, types[ type ] );
            }
            return this;
        }
        if ( selector === false || typeof selector === "function" ) {
            // ( types [, fn] )
            fn = selector;
            selector = undefined;
        } 
       if ( fn === false ) {
            fn = returnFalse;
        }
        // 对所有事件执行remove操作
        return this.each(function() {
            jQuery.event.remove( this, types, fn, selector );
        }); 
   },
    trigger: function( type, data ) {
        return this.each(function() { 
           jQuery.event.trigger( type, data, this );
        });
    },
    triggerHandler: function( type, data ) {
        var elem = this[0];
        if ( elem ) {
            return jQuery.event.trigger( type, data, elem, true );
        }
    }
});

5、jQuery.each();

// 分别调用方法on或trigger来实现对应的时间绑定
jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
    "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
    "change select submit keydown keypress keyup error contextmenu").split(" "), 
    function( i, name ) {
    // Handle event binding
    jQuery.fn[ name ] = function( data, fn ) {
        return arguments.length > 0 ?
           this.on( name, null, data, fn ) : 
           this.trigger( name );
    };
});

6、jQuery.fn.extend

// 暴露在外的实例奇函数jQuery.fn.extend({
    // hover事件
    hover: function( fnOver, fnOut ) {
        return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
    },
    // 事件绑定
    bind: function( types, data, fn ) {
        return this.on( types, null, data, fn );
    },
    // 事件解绑
    unbind: function( types, fn ) {
        return this.off( types, null, fn );
    },
    // 事件代理
    delegate: function( selector, types, data, fn ) {
        return this.on( types, selector, data, fn );
    },
    // 取消事件代理
    undelegate: function( selector, types, fn ) {
        return arguments.length === 1 ? this.off( selector, "**" ) :
                this.off( types, selector || "**", fn );
    }}
);