前言:
仅仅是简单模拟了$().on()和$().trigger(),仅支持id选择器,事件冒泡与事件委托。

代码:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>模拟jQuery的事件绑定到触发过程</title>
</head>

<body>

<div id="A" style="background-color: deeppink">
  这是A

  <div id="B" style="background-color: aqua">
    这是B
  </div>

</div>

<script>
  //数据缓存
  let events={}

  function $(elemId){
    //只考虑理想情况
    const element=document.querySelector(elemId)
    // console.log(element,'element27')

    function returnTrue(){
      return true
    }

    function returnFalse(){
      return false
    }

    $.event={
      //不考虑用户的自定义事件
      add:function (elemId,type,selectorReal,callbackReal) {
        let elemData=events[elemId]
        
        if(!elemData){
          events[elemId]=elemData={}
        }

        elemData.handle=function(nativeEvent){
          //锁定this
          return $.event.dispatch.call(this,nativeEvent)
        }

        if(!elemData[type]){
          elemData[type]=[]
          elemData[type].delegateCount=0
          //addEventListener只绑定一次
          document.querySelector(elemId).addEventListener(type,elemData.handle)
        }

        let handlersCount=elemData[type].length

        let handlerObj={
          type:type,
          handler:callbackReal,
          guid:++handlersCount,
          selector:selectorReal,
        }

        if ( selectorReal ) {
          //在下标为handlers.delegateCount++的位置插入委托事件
          elemData[type].splice( elemData[type].delegateCount++, 0, handlerObj);
        } else {
          elemData[type].push(handlerObj)
        }

      },

      dispatch:function (nativeEvent,) {
        let event=$.event.fix(nativeEvent)
        
        let handlers=events['#'+this.id][event.type]
        
        
        //继续锁定this
        let handlerQueue=$.event.handlers.call(this, event, handlers )
        //为什么要用变量代替,因为循环的时候,需要保留该值
        let matched,handleObj
        let i=0
        while((matched=handlerQueue[i++])&&!event.isPropagationStopped()){
          let j=0
          while((handleObj=matched.handlers[j++])){
            event.handleObj=handleObj
            handleObj.handler(event)
          }
        }
        // return event
      },
      
      fix:function (nativeEvent,) {
        let $event={}
        //就是MouseEvent
        $event.originalEvent=nativeEvent
        $event.target=nativeEvent.target
        $event.type=nativeEvent.type
          // delegateTarget: div#A,
          // currentTarget: div#A,
        $event.timeStamp=Date.now()
        $event.stopPropagation=function() {
          this.isPropagationStopped = returnTrue;
          nativeEvent.stopPropagation()
        }
        $event.isPropagationStopped=returnFalse
        //fix 的标志
        $event['chen'+(new Date()).valueOf()]=true

        return $event
      },

      handlers:function (event,handlers) {
        let delegateCount = handlers.delegateCount
        let cur=event.target
        let handlerQueue=[]

        for(;cur!==this;cur=cur.parentNode||this){
          let matchedHandlers = []

          for(let i=0;i<delegateCount;i++){
            let handleObj=handlers[i]

            matchedHandlers.push( handleObj )

            handlerQueue.push( { elem: cur, handlers: matchedHandlers } )
          }
        }

        cur=this

        if ( delegateCount < handlers.length ) {
          handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } )
        }

        return handlerQueue
      },

      trigger:function (elemId,type) {
        let element=document.querySelector(elemId)
        let eventPath=[]
        let cur=element
        let event={}
        event.target=cur
        event.type=type

        for(;cur;cur=cur.parentNode){
          eventPath.push( cur );
        }
        let i=0
        //不考虑阻止冒泡的情况
        while((cur=eventPath[i++])){
          let handle=events['#'+cur.id]&&events['#'+cur.id].handle
          if(handle){
            handle.call(cur,event)
          }
        }
      },

    }

    return {
      on:function (type,selector,callback) {
        let callbackReal,selectorReal
        
        if(!type){
          return
        }
        //如果selector是funcion的话,就没有委托元素了
        if(typeof selector==='function'&&!callback){
          
          selectorReal=undefined
          callbackReal=selector
          
        }else if(typeof selector==='string'&&callback){
          
          selectorReal=selector
          callbackReal=callback
        
        }
        
        return $.event.add(elemId,type,selectorReal,callbackReal)

      },


      trigger:function (type) {
        return $.event.trigger(elemId,type)
      },

    }

  }


  //仅支持id选择器,事件冒泡与事件委托

  //=========test1===============
  $("#A").on("click" ,function (event) {
    console.log(event,"A被点击了")
  })
  $("#A").on("click" ,function (event) {
    console.log(event,"A又被点击了")
  })

  //=========test2===============
  // $("#A").on("click" ,function (event) {
  //   console.log(event,"A被点击了")
  // })
  // $("#A").on("click" ,"#B",function (event) {
    // event.stopPropagation()
    // console.log(event,"B委托A被点击了")
  // })
  

  //=========test3===============
  // $("#A").on("click" ,function (event) {
  //   console.log(event,"A被点击了")
  // })
  // $("#B").on("click",function (event) {
  //   // event.stopPropagation()
  //   console.log(event,"B被点击了")
  // })

  //==========test4==============
  // $("#A").on("click" ,function (event) {
  //   console.log(event,"A被点击了")
  // })
  // $("#A").on("click" ,function (event) {
  //   console.log(event,"A又被点击了")
  // })
  // $("#A").trigger("click")


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

根据上篇的流程图写出即可。

思路请看:


(完)


小进进
113 声望9 粉丝