乐趣区

模拟实现jQuery的on和trigger

前言:
仅仅是简单模拟了 $().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>

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

思路请看:


(完)

退出移动版