Read the event module of zepto source code

Time:2020-6-2

Event module is one of the necessary modules for zepto. Because of the lack of familiarity with event API and the complexity of event object, at first glance, the source code of event module is a bit muddled. If you look at it carefully, it is not too complex.

Reading the zepto source series has been put on GitHub. Welcome to star: reading zepto

Source version

The source code of this article is zepto1.2.0

Prepare knowledge

Event simulation of focus / blur

Why is it rightfocusandblurWhat about simulation of events? As you can see from MDN,focusEvents andblurEvents do not support event bubbling. The direct consequence of not supporting event bubbling is that event delegation cannot be performed, so you need tofocusandblurEvents are simulated.

exceptfocusEvents andblurIn addition to events, modern browsers also supportfocusinEvents andfocusoutEvents, they andfocusEvents andblurThe main difference between events is to support event bubbling. So you can usefocusinAnd simulationfocusBubbling of events, usingfocusoutEvents to simulateblurThe bubbling of events.

We can determine the execution order of these four events by the following code:

<input id="test" type="text" />
const target = document.getElementById('test')
target.addEventListener('focusin', () => {console.log('focusin')})
target.addEventListener('focus', () => {console.log('focus')})
target.addEventListener('blur', () => {console.log('blur')})
target.addEventListener('focusout', () => {console.log('focusout')})

staychrome59Next,inputWhen focusing and defocusing, the console will print the following results:

'focus'
'focusin'
'blur'
'focusout'

As you can see, in this browser, the sequence of events should befocus > focusin > blur > focusout

For a more detailed description of these events, you can see: talk about the focus / focus in / focus out / blur event

With regard to the execution order of events, my test results are a little different from what the article said. If you are interested, please click this link to test http://jsbin.com/nizugazamo/edit?html ,js,console,output。 But I don’t think it’s necessary to go into the order of executionfocusinAsfocusBubble version of the event.

Event simulation of mouseenter / mouseleave

FollowfocusandblurSame,mouseenterandmouseleaveIt doesn’t support the bubbling of events, butmouseoverandmouseoutEvent bubbling is supported, so the bubbling processing of these two events can also be used separatelymouseoverandmouseoutTo simulate.

In the mouse eventeventObject, there is onerelatedTargetProperties of, from MDN:MouseEvent.relatedTarget In the document, you can see,mouseoverOfrelatedTargetPoints to the node left when moving to the target node(exited from ),mouseoutOfrelatedTargetIt refers to the node entered after leaving the node( entered to )。

in additionmouseoverEvents are triggered as the mouse moves, butmouseenterThe event will only trigger once when it enters the node. If the mouse is already on the target node, thenmouseoverEvent triggeredrelatedTargetIs the current node.

Therefore, to simulatemouseenterormouseleaveEvent, just determine the triggermouseoverormouseoutOn the eventrelatedTargetDoes not exist, orrelatedTargetIt is not the current node, and it is not a child node of the current node, so as to avoid the influence of child node event bubbling.

aboutmouseenterandmouseleaveQian Long’s article, “Why are mouseenter and mouseover so entangled?”? 》It’s very clear. I suggest reading it.

The core of event module

takeEventThe module is simplified as follows:

;(function($){})(Zepto)

In fact, it’s to pass in the closureZeptoObject, and thenZeptoObject.

stayEventIn the module, the main tasks are as follows:

  • Provide concise API

  • Unify different browserseventobject

  • The event handle cache pool is convenient for manually triggering and unbinding events.

  • Event delegation

Internal approach

zid

var _zid = 1
function zid(element) {
  return element._zid || (element._zid = _zid++)
}

Get parameterselementObject’s_zidProperty, if the property does not exist, the global variable_zidincrease1, aselementOf_zidProperty value of. This method is used to mark the elements that have been bound to the event for easy searching.

parse

function parse(event) {
  var parts = ('' + event).split('.')
  return {e: parts[0], ns: parts.slice(1).sort().join(' ')}
}

stayzeptoThe namespace that supports events. You can use theeventType.ns1.ns2...To add one or more namespaces to an event.

parseFunction to decompose the event name and namespace.

'' + eventYeseventBecome a string, and then.Split into arrays.

In the returned object,eIs the event name,nsAfter sorting, a namespace string connected by spaces, such asns1 ns2 ns3 ...Form.

matcherFor

function matcherFor(ns) {
  return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
}

Generate expressions that match the namespace, for example, parameters passed innsbyns1 ns2 ns3, the resulting regularity is/(?:^| )ns1.* ?ns2.* ?ns3(?: |$)/。 As for the use, I’ll talk about it soon.

Findhandlers, find cached handles

handlers = {}
function findHandlers(element, event, fn, selector) {
  event = parse(event)
  if (event.ns) var matcher = matcherFor(event.ns)
  return (handlers[zid(element)] || []).filter(function(handler) {
    return handler
      && (!event.e  || handler.e == event.e)
      && (!event.ns || matcher.test(handler.ns))
      && (!fn       || zid(handler.fn) === zid(fn))
      && (!selector || handler.sel == selector)
  })
}

Find the event handle for the element.

event = parse(event)

callparseFunctions, separating outeventThe event name and namespace of the parameter.

if (event.ns) var matcher = matcherFor(event.ns)

If a namespace exists, generate a regular expression that matches that namespacematcher

return (handlers[zid(element)] || []).filter(function(handler) {
    ...
  })

What’s back ishandlers[zid(element)]Handle functions that meet the criteria in.handlersIs a cached handle container, usingelementOf_zidProperty value askey

Return handler // condition 1
     &&(! Event. E | handler. E = = event. E) // condition 2
     && (! event.ns  Wei matcher.test ( handler.ns ))// condition 3
     && (!fn       || zid( handler.fn )= = = Zid (FN)) // condition 4
     && (!selector ||  handler.sel  ==Selector) // condition 5

The returned handle must satisfy five conditions:

  1. Handle must exist

  2. Ifevent.eThe event name of the handle must match theeventConsistent event names for

  3. If a namespace exists, the namespace of the handle must match the namespace of the event(matcherForRole of)

  4. If you specify that the matching event handle isfn, the current handlehandlerOf_zidMust match the specified handlefnConsistent

  5. If you specify a selectorselector, the selector in the current handle must be the same as the specified selector

From the above comparison, we can see that the form of the cached handle object is as follows:

{
  FN: '', // function
  e: ', // event name
  NS: '', // namespace
  SEL: '', // selector
  //In addition, there are
  i: ', // function index
  Del: '', // delegate function
  Proxy: '', // proxy function
  //The following attributes will cover
}

Realevent, return the corresponding bubble event

focusinSupported = 'onfocusin' in window,
focus = { focus: 'focusin', blur: 'focusout' },
hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
function realEvent(type) {
  return hover[type] || (focusinSupported && focus[type]) || type
}

This function is actuallyfocus/blurconvert tofocusin/focusout, willmouseenter/mouseleaveconvert tomouseover/mouseoutevent.

becausefocusin/focusoutThe event browser support level is not very good, so we need to do a test for browser support. If the browser supports it, return it; otherwise, return the original event name.

Compatible, modify the event object

returnTrue = function(){return true},
returnFalse = function(){return false},
eventMethods = {
  preventDefault: 'isDefaultPrevented',
  stopImmediatePropagation: 'isImmediatePropagationStopped',
  stopPropagation: 'isPropagationStopped'
}

function compatible(event, source) {
  if (source || !event.isDefaultPrevented) {
    source || (source = event)

    $.each(eventMethods, function(name, predicate) {
      var sourceMethod = source[name]
      event[name] = function(){
        this[predicate] = returnTrue
        return sourceMethod && sourceMethod.apply(source, arguments)
      }
      event[predicate] = returnFalse
    })

    try {
      event.timeStamp || (event.timeStamp = Date.now())
    } catch (ignored) { }

    if (source.defaultPrevented !== undefined ? source.defaultPrevented :
        'returnValue' in source ? source.returnValue === false :
        source.getPreventDefault && source.getPreventDefault())
      event.isDefaultPrevented = returnTrue
      }
  return event
}

compatibleFunction to modifyeventBrowser differences for objects, toeventObject addedisDefaultPreventedisImmediatePropagationStoppedisPropagationStoppedSeveral methods, not supported fortimeStampBrowser for, toeventAdd to objecttimeStampProperty.

if (source || !event.isDefaultPrevented) {
  source || (source = event)

  $.each(eventMethods, function(name, predicate) {
    var sourceMethod = source[name]
    event[name] = function(){
      this[predicate] = returnTrue
      return sourceMethod && sourceMethod.apply(source, arguments)
    }
    event[predicate] = returnFalse
  })

The judgment condition is that the original event object exists, or the eventeventOfisDefaultPreventedEstablished when it does not exist.

IfsourceIf not, theeventAssign tosource, as the original event object.

ergodiceventMethods, get the corresponding method name of the original event objectsourceMethod

event[name] = function(){
  this[predicate] = returnTrue
  return sourceMethod && sourceMethod.apply(source, arguments)
}

rewriteeventWhen executing the corresponding method, first assign the new method corresponding to the method in the event asreturnTrueFunctions, such as executionpreventDefaultMethod,isDefaultPreventedThe return value of the method istrue

event[predicate] = returnFalse

This is to initialize the newly added property asreturnFalsemethod

try {
  event.timeStamp || (event.timeStamp = Date.now())
} catch (ignored) { }

This paragraph does not supporttimeStampProperties in the browsertimeStampProperty.

if (source.defaultPrevented !== undefined ? source.defaultPrevented :
    'returnValue' in source ? source.returnValue === false :
    source.getPreventDefault && source.getPreventDefault())
  event.isDefaultPrevented = returnTrue
  }

This is for browserspreventDefaultCompatibility of different implementations.

source.defaultPrevented  !== undefined ?  source.defaultPrevented  : 'ternary expression'

If the browser supportsdefaultPrevented, returndefaultPreventedValue of

'returnValue' in source ?  source.returnValue  ===False: 'next judgment'

returnValueDefault istrue, if the browser’s default behavior is blocked,returnValueWill becomefalse

source.getPreventDefault && source.getPreventDefault()

If the browser supportsgetPreventDefaultMethod, callgetPreventDefault()Method to get whether to block the default behavior of the browser.

Judged astrueWhenisDefaultPreventedSet toreturnTruemethod.

Createproxy, create proxy object

ignoreProperties = /^([A-Z]|returnValue$|layer[XY]$|webkitMovement[XY]$)/,
function createProxy(event) {
  var key, proxy = { originalEvent: event }
  for (key in event)
    if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key]

    return compatible(proxy, event)
}

zeptoWhen the event is triggered, theeventIt’s not nativeeventObjects are all proxy objects. This is how to create proxy objects.

ignorePropertiesTo excludeA-ZStart, that is, all properties that start with uppercase letters, andreturnValueending,layerX/layerYwebkitMovementX/webkitMovementYNonstandard property at the end.

for (key in event)
  if (!ignoreProperties.test(key) && event[key] !== undefined) proxy[key] = event[key]

Traverse the native event object, excluding unnecessary properties and values asundefinedCopy properties and values to the proxy object.

Finally, the modified proxy object is returned

eventCapture

function eventCapture(handler, captureSetting) {
  return handler.del &&
    (!focusinSupported && (handler.e in focus)) ||
    !!captureSetting
}

returntrueIndicates that the event handle is executed in the capture phase, otherwise it is executed in the bubbling phase.

If there is an event agent and the event isfocus/blurEvent, not supported in browserfocusin/focusoutEvent, set totrueIn the capture phase, the event is processed to achieve the purpose of bubbling indirectly.

Otherwise, it can be customizedcaptureSettingSet the time of event execution.

Add, the core method of event module

function add(element, events, fn, data, selector, delegator, capture){
  var id = zid(element), set = (handlers[id] || (handlers[id] = []))
  events.split(/\s/).forEach(function(event){
    if (event == 'ready') return $(document).ready(fn)
    var handler   = parse(event)
    handler.fn    = fn
    handler.sel   = selector
    // emulate mouseenter, mouseleave
    if (handler.e in hover) fn = function(e){
      var related = e.relatedTarget
      if (!related || (related !== this && !$.contains(this, related)))
        return handler.fn.apply(this, arguments)
        }
    handler.del   = delegator
    var callback  = delegator || fn
    handler.proxy = function(e){
      e = compatible(e)
      if (e.isImmediatePropagationStopped()) return
      e.data = data
      var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))
      if (result === false) e.preventDefault(), e.stopPropagation()
      return result
    }
    handler.i = set.length
    set.push(handler)
    if ('addEventListener' in element)
      element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
      })
}

addThe method is to add events and event responses to elements. There are many parameters. First, let’s look at the meaning of each parameter:

Element // the element bound by the event
Events // list of events to bind
FN // handle to event execution
Data // the data passed to the event object when the event is executed
Selector // the selector of the event binding element
Delegator // event delegate function 
Capture // the stage to execute the event handle
var id = zid(element), set = (handlers[id] || (handlers[id] = []))

Get or setidsetIs the event handle container.

events.split(/\s/).forEach(function(event){})

Process each event

if (event == 'ready') return $(document).ready(fn)

If isreadyEvent, callreadyMethod, suspending subsequent execution

var handler   = parse(event)
handler.fn    = fn
handler.sel   = selector
// emulate mouseenter, mouseleave
if (handler.e in hover) fn = function(e){
  var related = e.relatedTarget
  if (!related || (related !== this && !$.contains(this, related)))
    return handler.fn.apply(this, arguments)
    }
handler.del   = delegator
var callback  = delegator || fn

This code is setuphandlerSome of the properties on are cached.

It’s mainly right heremouseenterandmouseleaveThe specific principle of event simulation has been mentioned above. Only when the condition is established can the event handle be executed.

handler.proxy = function(e){
  e = compatible(e)
  if (e.isImmediatePropagationStopped()) return
  e.data = data
  var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))
  if (result === false) e.preventDefault(), e.stopPropagation()
  return result
}

The proxy function for the event handle.

eNative for event executioneventObject, so callcompatibleYeseMake corrections.

callisImmediatePropagationStoppedMethod to see if it has been implementedstopImmediatePropagationMethod, if executed, aborts execution of subsequent programs.

Re expansioneObject, changingdataDeposit toeOfdataProperty.

Execute event handle, willeObject as the first parameter of the handle.

If after execution, return explicitlyfalse, prevents browser default behavior and event bubbling.

set.push(handler)
if ('addEventListener' in element)
  element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))

Store handle into handle container

Calling element’saddEventListenerMethod, add event, the callback function of event is the proxy function of handle,eventCapture(handler, capture)To specify whether to execute during the capture phase.

Remove, delete event

function remove(element, events, fn, selector, capture){
  var id = zid(element)
  ;(events || '').split(/\s/).forEach(function(event){
    findHandlers(element, event, fn, selector).forEach(function(handler){
      delete handlers[id][handler.i]
      if ('removeEventListener' in element)
        element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
        })
  })
}

First get the_zid

;(events || '').split(/\s/).forEach(function(event){})

Traverse theevents

findHandlers(element, event, fn, selector).forEach(function(handler){})

callfindHandlersMethod, findingeventEvent handle to be deleted

delete handlers[id][handler.i]

Delete the corresponding event in the handle container, andaddHandle object in functioniProperty is used here to find the handle to be deleted.

element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))

callremoveEventListenerMethod, delete the corresponding event.

Tool functions

$.event

$.event = { add: add, remove: remove }

takeaddMethods andremoveMethod exposure should be convenient for third-party plug-ins to extend

$.proxy

$.proxy = function(fn, context) {
  var args = (2 in arguments) && slice.call(arguments, 2)
  if (isFunction(fn)) {
    var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) }
    proxyFn._zid = zid(fn)
    return proxyFn
  } else if (isString(context)) {
    if (args) {
      args.unshift(fn[context], fn)
      return $.proxy.apply(null, args)
    } else {
      return $.proxy(fn[context], fn)
    }
  } else {
    throw new TypeError("expected function")
  }
}

Proxy function, which functions a bit like JSbindMethod, which returns a function that changes the execution context after proxy.

var args = (2 in arguments) && slice.call(arguments, 2)

If more than 3 parameters are provided, the first two parameters are removed and the later parameters are used as the execution functionfnParameters for.

if (isFunction(fn)) {
  var proxyFn = function(){ return fn.apply(context, args ? args.concat(slice.call(arguments)) : arguments) }
  proxyFn._zid = zid(fn)
  return proxyFn
}

proxyThere are two ways to transfer the execution function of. One is to pass in the first parameter directly. The other is to pass in the first parameter as the context object. The execution function is also passed in together with the context object.

Judge herefnWhether it is a function, i.e. the first parameter passing method, calledfnFunctionalapplyMethod, changing the context objectcontextAsapplyThe first parameter of, ifargsExisting, then withfnParameter merge for.

Add the_zidProperty to facilitate the search of functions.

else if (isString(context)) {
  if (args) {
    args.unshift(fn[context], fn)
    return $.proxy.apply(null, args)
  } else {
    return $.proxy(fn[context], fn)
  }

If the function is already included in the context object, the first parameterfnObject, second argumentcontextIs a string that specifies the name of the property in the context object that performs the function.

if (args) {
  args.unshift(fn[context], fn)
  return $.proxy.apply(null, args)
}

If the parameter exists, thefn[context], that is, executing functions andfn, that is, the context object is placed inargsAt the beginning of the array, modify the parameter to be the same as the first parameter passing method, and then call$.proxyFunction. Call hereapplyMethod, because I don’t know how many parameters there are, callapplyCan be passed in as an array.

IfargsWhen it does not exist, only two parameter items are determined, so it can be called directly$.proxymethod.

$.Event

specialEvents={},
specialEvents.click = specialEvents.mousedown = specialEvents.mouseup = specialEvents.mousemove = 'MouseEvents'

$.Event = function(type, props) {
  if (!isString(type)) props = type, type = props.type
  var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true
  if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])
  event.initEvent(type, bubbles, true)
  return compatible(event)
}

specialEventsIs to fix the mouse event toMouseEvents, which should deal with the compatibility of browsers. In some browsers, the event type of these events is notMouseEvents

$.EventMethod to manually create a specific type of event.

parametertypeCan be a string oreventObject.propsFor extensioneventObject of object.

if (!isString(type)) props = type, type = props.type

If it is not a string, it iseventObject, thetypeAssign topropstypeIs currenteventObjecttypeProperty value.

var event = document.createEvent(specialEvents[type] || 'Events'), bubbles = true

callcreateEventMethod to create a corresponding type ofeventEvent, and default event bubbling totrue

if (props) for (var name in props) (name == 'bubbles') ? (bubbles = !!props[name]) : (event[name] = props[name])

ergodicpropsProperty, if specifiedbubbles, the specified bubbling behavior is adopted, and other attributes are copied toeventObject, implement theeventObject.

event.initEvent(type, bubbles, true)
return compatible(event)

Initializes the newly created event and returns the corrected event object.

method

.on()

$.fn.on = function(event, selector, data, callback, one){
  var autoRemove, delegator, $this = this
  if (event && !isString(event)) {
    $.each(event, function(type, fn){
      $this.on(type, selector, data, fn, one)
    })
    return $this
  }

  if (!isString(selector) && !isFunction(callback) && callback !== false)
    callback = data, data = selector, selector = undefined
    if (callback === undefined || data === false)
      callback = data, data = undefined

      if (callback === false) callback = returnFalse

      return $this.each(function(_, element){
        if (one) autoRemove = function(e){
          remove(element, e.type, callback)
          return callback.apply(this, arguments)
        }

        if (selector) delegator = function(e){
          var evt, match = $(e.target).closest(selector, element).get(0)
          if (match && match !== element) {
            evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
            return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))
          }
        }

        add(element, event, callback, data, selector, delegator || autoRemove)
      })
}

onMethod is used to bind events to elements, and the final call isaddMethod, the previous logic is mainly to modify the parameters.

var autoRemove, delegator, $this = this
if (event && !isString(event)) {
  $.each(event, function(type, fn){
    $this.on(type, selector, data, fn, one)
  })
  return $this
}

autoRemoveRepresents a function that is automatically unbound after an event response is executed.

eventIt can be a string or an object. When it is an object, its property is an event type and its value is a handle.

This section is dealing witheventWhen it is an object, traverse the object, get the event type and handle, and then call againonMethod, continue to correct the subsequent parameters.

if (!isString(selector) && !isFunction(callback) && callback !== false)
  callback = data, data = selector, selector = undefined
if (callback === undefined || data === false)
  callback = data, data = undefined

if (callback === false) callback = returnFalse

Let’s start with the first oneifselectorNot forstringcallbackIs not a function, andcallbackNot forfalseTime.

It can be determined hereselectorNo delivery, becauseselectorIs not a required parameter.

So here we willdataAssign tocallbackselectorAssign todata, willselectorSet toundefinedBecauseselectorThere is no passing, so the position of the corresponding parameter is moved forward one bit.

Let’s see the second oneif, ifcallback(originaldata)ForundefineddatabyfalseWhen, indicatesselectorNo delivery, anddataNo delivery, sodataAssign tocallback, willdataSet toundefined, move the parameter forward one more bit.

Thirdif, ifcallback === false, usingreturnFalseFunction instead, if notreturnFalseInstead, it will report an error.

return $this.each(function(_, element){
  add(element, event, callback, data, selector, delegator || autoRemove)
})

As you can see, this is a collection of traversal elements called for each elementaddMethod, binding event.

if (one) autoRemove = function(e){
  remove(element, e.type, callback)
  return callback.apply(this, arguments)
}

If called only once, setautoRemoveIs a function that calls theremoveMethod to unbind the corresponding event on the element.

if (selector) delegator = function(e){
  var evt, match = $(e.target).closest(selector, element).get(0)
  if (match && match !== element) {
    evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
    return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))
  }
}

IfselectorExists, indicating that event agent is needed.

callclosestMethod from the target element of the evente.targetStart looking up, return the first matchselectorElement of. aboutclosestMethods, see “read zepto source set element search” analysis.

IfmatchExists, andmatchIf it is not the current element, callcreateProxyMethod, create a proxy object for the current event object, and then call$.extendMethod, extending the proxy objectcurrentTargetandliveFiredProperty to save the agent element and the element that triggered the event to the event object.

Finally, execute the handle function to proxy the elementmatchAs the context of the handle, theeventobjectevtReplace the first parameter of the original handle function.

Assign this function todelegator, passed as a proxy function toaddmethod.

.off()

$.fn.off = function(event, selector, callback){
  var $this = this
  if (event && !isString(event)) {
    $.each(event, function(type, fn){
      $this.off(type, selector, fn)
    })
    return $this
  }

  if (!isString(selector) && !isFunction(callback) && callback !== false)
    callback = selector, selector = undefined

    if (callback === false) callback = returnFalse

    return $this.each(function(){
      remove(this, event, callback, selector)
    })
}

Unbind event

if (event && !isString(event)) {
  $.each(event, function(type, fn){
    $this.off(type, selector, fn)
  })
  return $this
}

This logic is connected withonThe similarity in the method can be corrected without further details.

if (!isString(selector) && !isFunction(callback) && callback !== false)
  callback = selector, selector = undefined
if (callback === false) callback = returnFalse

firstifIt’s processingselectorIf the parameter is not passed,selectorIt’s really about locationcallback

the secondifIs to judge ifcallbackbyfalse, willcallbackAssigned asreturnFalseFunction.

return $this.each(function(){
  remove(this, event, callback, selector)
})

Finally, traverse all elements, callremoveFunction to unbind events for each element.

.bind()

$.fn.bind = function(event, data, callback){
  return this.on(event, data, callback)
}

bindMethod is called internallyonmethod.

.unbind()

$.fn.unbind = function(event, callback){
  return this.off(event, callback)
}

unbindMethod is called internallyoffmethod.

.one()

$.fn.one = function(event, selector, data, callback){
  return this.on(event, selector, data, callback, 1)
}

oneMethod is also called internallyonMethod, but passed by defaultoneParameters are1, indicating that the bound event is only executed once.

.delegate()

$.fn.delegate = function(selector, event, callback){
  return this.on(event, selector, callback)
}

Event delegation, also calledonMethod, justselectorIt must be delivered.

.undelegate()

$.fn.undelegate = function(selector, event, callback){
  return this.off(event, selector, callback)
}

Cancel event delegation, which is called internallyoffmethod,selectorMust be delivered.

.live()

$.fn.live = function(event, callback){
  $(document.body).delegate(this.selector, event, callback)
  return this
}

Dynamically created nodes can also respond to events. In fact, events are bound tobodyAnd then delegate to the current node. Internally calleddelegatemethod.

.die()

$.fn.die = function(event, callback){
  $(document.body).undelegate(this.selector, event, callback)
  return this
}

Will beliveBind tobodyThe event on is destroyed. The internal call isundelegatemethod.

.triggerHandler()

$.fn.triggerHandler = function(event, args){
  var e, result
  this.each(function(i, element){
    e = createProxy(isString(event) ? $.Event(event) : event)
    e._args = args
    e.target = element
    $.each(findHandlers(element, event.type || event), function(i, handler){
      result = handler.proxy(e)
      if (e.isImmediatePropagationStopped()) return false
        })
  })
  return result
}

Trigger the event callback function directly.

parametereventCan be event type string oreventObject.

e = createProxy(isString(event) ? $.Event(event) : event)

IfeventWhen string, call$.EventTool function to initialize an event object and callcreateProxyTo create aeventProxy object.

$.each(findHandlers(element, event.type || event), function(i, handler){
  result = handler.proxy(e)
  if (e.isImmediatePropagationStopped()) return false
    })

callfindHandlersMethod to find all handles to the event, callproxyMethod, that is, the callback function that is actually bound to the event (seeaddGet the result returned by the methodresult, and viewisImmediatePropagationStoppedWhether the returned result istrue, if so, suspend subsequent execution immediately.

If the result returnedresultbyfalse, and immediately suspend the subsequent execution.

becausetriggerHandlerThe callback function is triggered directly, so the event does not bubble.

.trigger()

$.fn.trigger = function(event, args){
  event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event)
  event._args = args
  return this.each(function(){
    // handle focus(), blur() by calling them directly
    if (event.type in focus && typeof this[event.type] == "function") this[event.type]()
    // items in the collection might not be DOM elements
    else if ('dispatchEvent' in this) this.dispatchEvent(event)
    else $(this).triggerHandler(event, args)
      })
}

Trigger the event manually.

event = (isString(event) || $.isPlainObject(event)) ? $.Event(event) : compatible(event)

eventEvent types, objects, andeventObject.

If you are passing a string or a pure object, call$.EventMethod to initialize the event, otherwise callcompatibleMethod to correcteventObject, due to$.EventMethod has been called internallycompatibleMethod modificationeventObject, so the external does not need to call again.

if (event.type in focus && typeof this[event.type] == "function") this[event.type]()

If it isfocus/blurMethod, thethis.focus()orthis.blur()Methods, both of which are supported native to the browser.

IfthisbyDOMElement, i.e. existingdispatchEventMethod, usedispatchEventTo trigger an event, aboutdispatchEvent, refer to MDN: EventTarget.dispatchEvent ()。

Otherwise, call directlytriggerHandlerMethod to trigger the event’s callback function.

becausetriggerThe event handle is executed by triggering the event, so the event bubbles.

Series of articles

  1. Reading the code structure of zepto source code

  2. Internal method of reading zepto source code

  3. Read the tool function of zepto source code

  4. The magic of reading zepto source code$

  5. Set operation of reading zepto source code

  6. Reading the set element of zepto source code

  7. Read the operation dom of zepto source code

  8. Reading zepto source code style operation

  9. Read the property operation of zepto source code

reference resources

  • Why are mouseenter and mouseover so entangled?

  • towards zepto.js Learn how to trigger DOM events manually

  • Who says you just “use” jQuery?

  • Zepto source code analysis event module

  • Zepto source code event.js

  • Talk about the focus / focus in / focus out / blur event

  • MDN:mouseenter

  • MDN:mouseleave

  • MDN:MouseEvent.relatedTarget

  • MDN:Event reference

  • MDN:Document.createEvent()

  • MDN:EventTarget.dispatchEvent()

  • MDN:event.stopImmediatePropagation

License

Finally, all articles will be sent to WeChat official account simultaneously. Welcome to our attention. Welcome to comment:Read the event module of zepto source code

Author: opposite corner