A General Zepto Source Analysis (3) - Evet Module

Keywords: Javascript Attribute IE JQuery

A General Zepto Source Analysis (3) - Evet Module

Ordinary passers-by, ordinary look. The latest version 1.2.0 is used in the analysis.

zepto can be made up of many modules. The default modules include zepto core module and event, ajax, form, ie. Event module is also one of the more important modules. We can use the method provided by zepto to implement event monitoring, custom event dispatch and so on. Most importantly, we have done some event compatibility and simplified our coding.

event module

This module has fewer lines of code than ajax. Still the old routine, a static analysis of the function call relationship yields a group of magical lines (...).

Fortunately, some functions can be skipped directly.

  $.fn.bind = function(event, data, callback){
    return this.on(event, data, callback)
  }
  $.fn.unbind = function(event, callback){
    return this.off(event, callback)
  }
  $.fn.one = function(event, selector, data, callback){
    return this.on(event, selector, data, callback, 1)
  }
  $.fn.delegate = function(selector, event, callback){
    return this.on(event, selector, callback)
  }
  $.fn.undelegate = function(selector, event, callback){
    return this.off(event, selector, callback)
  }

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

Except for $. fn.one(), the other functions have been discarded and can be replaced by $. fn.on() and $. fn.off(). But will unification of $. fn.on() violate the single responsibility principle? Different opinions, I think it will, the cost of development efficiency is a little high.

Publish/Subscribe Mode

After simplifying $. fn.on() and $. fn.off(), we can see that their final destination is the closure functions of add() and remove():

  $.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
    }

    ... // Handling of overload of incoming parameters

    return $this.each(function(_, element){
      if (one) autoRemove = function(e){...}

      if (selector) delegator = function(e){...}

      add(element, event, callback, data, selector, delegator || autoRemove)
    })
  }
  $.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
    }

    ... // Handling of overload of incoming parameters

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

Events here can be either a space-separated event type string or an enumerable event type/callback k-v object. So at the beginning, each k-v calls itself in $. each().

Regardless of the processing logic inside add() and remove(), you can see that browser methods are finally invoked:

  function add(element, events, fn, data, selector, delegator, capture){
    ...
      if ('addEventListener' in element)
        element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
    ...
  }
  function remove(element, events, fn, selector, capture){
    ...
      if ('removeEventListener' in element)
        element.removeEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
    ...
  }

The event monitoring method of browser BOM is used to maintain subscribers'subscriptions to events.

The browser itself produces events (such as user actions), and it is a publisher. In addition, we also have customizable publisher interfaces $. fn.trigger() and $. fn.triggerHandler() for publishing custom types of events, the former can dispatch events through the BOM interface dispatchEvent(), and the latter can trigger event callbacks once through the internal proxy closure.

Some people think that Event module itself is similar to proxy, but I don't think it's appropriate. It simply provides the interface of registration, de-registration, and sending events. There is no clear control mechanism. Even the event namespace (mentioned later) can be regarded as different events. In fact, both jQuery and Zepto are window s objects (see Event Flow Graph of w3.org ) As event bus, each listener subscribes to messages within its own scope. Event scheduling (dispatching, bubbling) is accomplished through DOM event streams, which of course is a solid publish/subscribe model.

event bus-based event mechanism

We mentioned above that window s objects are used as event bus es to implement event mechanism. Because of the mechanism of event dispatch and bubbling, the path of event propagation forms a DOM event flow. For a specific event target, the path of event propagation is uniquely determined, because only one parent node in the DOM tree. And from Event Flow Graph of w3.org We can see that once the propagation path is determined, the event process can be divided into three stages: capture stage, target stage and bubbling stage. The ancestors above the target element can choose to capture and bubbles, or cancel the entire subsequent propagation. Of course, the default behavior of the target element can also be cancelled at the target stage.

Although event formation, propagation and triggering are all done by browsers, addEventListener() is registered on the event target. We can imagine an event flow as a virtual branch line:

Window s Bus - targetA. click - targetB. Event0 - ------> >
                      |
Event streams created for targetA -- Document -... -- parent -- targetA -- parent -... - > >
                            ^                          ^  ^        ^
                            |                          |  |        |
                        Listener captures possible multiple Listener bubbles
                                                     Listener

Of course, the flow of events that logically pass through may be different. In fact, the Liistener is still hanging on the DOM tree node.

compatible() compatible correction function

Before we start the concrete analysis, we must first make clear several functions with higher degree of entry. First of all, the compatible() function, which is called by four methods within the Event module, is the highest in the module. Static analysis shows that there are two kinds of calls:

  // Assuming that proxyEvent is a proxy event and nativeEvent is a native event
  compatible(nativeEvent)
  compatible(proxyEvent, nativeEvent)

The proxy function is as follows:

  var 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)
  }

Event [key]!=== undefined is a common operation for...in, which ensures that only the attributes on the object are inherited and avoids taking all the prototypes.

This ignoreProperties is strange. I flipped to a submission of 18ba1f0 in v1.0 (adding [A-Z]|layer[XY]$), and it said:

silence "layerX/Y" Webkit warnings for events

This means we can't just blindly extend all event properties onto the
event proxy object.

A v1.1.0 submission of b0eaeb7 (with an increase in returnValue$) said that Chrome had abandoned the method and that even replication would warn.

A v1.1.7 submission of c89e705 (adding WebKit movement [XY]$) was also said to eliminate Chrome warnings.

It seems that compatibility problems can cure obsessive-compulsive disorder. As for the browser to do scrap checks and code first check which performance wastage cost, I have not compared, intuition is the former bigger, after all, we have to prepare for the warning work.

Okay, now let's look at the implementation of compatible():

  var returnTrue = function(){return true},
      returnFalse = function(){return false},
      /* Focus 1 (proxy function names correspond to assertion tables) */
      eventMethods = {
        preventDefault: 'isDefaultPrevented',
        stopImmediatePropagation: 'isImmediatePropagationStopped',
        stopPropagation: 'isPropagationStopped'
      }
  function compatible(event, source) {
    if (source || !event.isDefaultPrevented) {
      source || (source = event)
      // Focus 1 (proxy function names correspond to assertion tables)
      $.each(eventMethods, function(name, predicate) {
        var sourceMethod = source[name]
        event[name] = function(){
          // Focus 2 (Setting Conditional Pile Functions)
          this[predicate] = returnTrue
          return sourceMethod && sourceMethod.apply(source, arguments)
        }
        event[predicate] = returnFalse
      })

      try {
        event.timeStamp || (event.timeStamp = Date.now())
      } catch (ignored) { }
      // Focus 3 (to support cross-platform, sequential: new browsers, old methods, very early abandoned API s)
      if (source.defaultPrevented !== undefined ? source.defaultPrevented :
          'returnValue' in source ? source.returnValue === false :
          source.getPreventDefault && source.getPreventDefault())
        event.isDefaultPrevented = returnTrue
    }
    return event
  }

First, make sure that the proxy call or the isDefaultPrevented property is not set, otherwise you don't need to deal with the direct return. That is to say, many people think it will be multi-packaged, but it does not exist. Here you can see that events are bound to be processed by this function.

Then it encapsulates a proxy of three native functions, so that each call will set a conditional pile function for the corresponding assertion (as the function name), and then call back to the original function. The default stake function always returns false to indicate that the corresponding method has not been invoked. Finally, if the default action of the event has been cancelled, the corresponding condition pile should always return true.

According to 4f3d4a8, event.timeStamp on safari may be read-only, and the setting of timestamp can only be ignored.

In summary, compatible() is a compatible modifier that decorates the three conditional pile functions provided by Event plug-ins.

Generate custom events with $. Event()

Allows us to specify custom event types, create an event object, and trigger it (such as trigger()).

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

  $.Event = function(type, props) {
    // Parameter overloading
    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)
  }

Event constructors are now encouraged to use for new events, such as new Event('xxx'), new CustomEvent('xxx', {...}), new MouseEvent('click', {...}) and so on. But early browsers only supported the createEvent() method to create events, with parameters such as UIEvents, MouseEvents, Mutation Events, HTMLEvents, and other non-standard events (such as Gecko's own defined event type). It's a little exaggerated that initEvent() has been abandoned == Well, anyway, here are some black technologies.

Finally, an event object with compatible modifications is returned.

Proxy function proxy

This function provides a simple proxy for the incoming context environment or function so that its this pointer points to the incoming context object when it is called, which is a bit like the bind() function of ES6; or it has a second form, assigning the function to an attribute of the incoming context object and passing in the name of the attribute.

  $.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) }
      // Focus 1 (proxy functions and primitive functions are treated as the same callback)
      proxyFn._zid = zid(fn)
      return proxyFn
    } else if (isString(context)) {
      if (args) {
        // Focus 2 (Simple overloading)
        args.unshift(fn[context], fn)
        return $.proxy.apply(null, args)
      } else {
        return $.proxy(fn[context], fn)
      }
    } else {
      throw new TypeError("expected function")
    }
  }

First, proxyFn is a closure function. But the operation proxyFn._zid = zid(fn) is a bit strange. Find the reference to zid() and find a judgment of Zid (handler. fn) ==== Zid (fn). It seems that when it is treated as an event callback function, it will be considered the same function, that is to say, as long as the original function is passed in at $. fn.off(), the event callback of the proxy function will be released. Even if the original function is proxied again (such as replacing the context object many times), it will be found and released together.

For the second form of invocation, it is simple and rough, and it is a kind of overload.

Go deep into the add() function

Binding callback handle and its collection

Looking at the two functions that are called directly, I mean, functions that are used to process parameters in the first place, they may be important:

  var _zid = 1,
      handlers = {}
  function zid(element) {
    return element._zid || (element._zid = _zid++)
  }
  function parse(event) {
    var parts = ('' + event).split('.')
    return {e: parts[0], ns: parts.slice(1).sort().join(' ')}
  }
  function add(element, events, fn, data, selector, delegator, capture){
    // Focus 1 (Binding callback handles and their collections)
    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)
      ...
      // Focus 2 (Recorded in Collection)
      handler.i = set.length
      set.push(handler)
      ...
    })
  }

Instead of binding Zepto objects, a self-incremental _zid is bound for native objects, because each encapsulated object $() gets a new one. In addition, DOM itself is a huge multi-level table. It's good to add attributes directly to elements in DOM. In turn, we can use this id as an index of elements.

With the ID bound, you can see that set = handlers [id] | (handlers [id] = []) maintains a collection of event handle handler objects. Later, the array length is used as the ordinal marker of the newly added object.

The parse() function is used to parse the namespace of a single event, which is not mentioned in Zepto's document. The event.namespace of jQuery is used to find the explanation, mainly according to different namespaces, to perform different responses to the same event. At this point, the event type will probably grow like this (passed in by the user):

    test.somethingA
    ErrorEvent.otherthingB.orPluginC.orSubscriberD

Simulate mouseenter and mouseleave events

Many people think that simulation of these two events is to support ancestor bubbles, emmm.. Maybe, but I do support non-bubbles, God knows why there are father elements to know the needs of child elements to be entered. I think it's more about compatibility. Early browsers didn't support these two events.

  var hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
  function add(element, events, fn, data, selector, delegator, capture){
    ...
      var handler   = parse(event)
      handler.fn    = fn
      handler.sel   = selector
      // emulate mouseenter, mouseleave
      if (handler.e in hover) fn = function(e){
        // Focus 1 (Use of this attribute)
        var related = e.relatedTarget
        // Focus 2 (Inclusive lookup)
        if (!related || (related !== this && !$.contains(this, related)))
          return handler.fn.apply(this, arguments)
      }
    ...
  }

handler.sel will be used when looking for handler, regardless of here.

How to simulate? Obviously, if only we could know which element the pointer points to when the mouse moves, we could at most monitor whether the pointer has changed.

Fortunately, we have the. relatedTarget read-only property. According to the introduction of MouseEvent.relatedTarget on MDN, relatedTarget of mouseover event points to "where to come" element, and mouseout event points to "where to go" element. In the case of mouseover events, we may enter from the outside or from the sub-elements, and events entering from the sub-elements will bubbled up. We can use $. contains() to judge that this sub-element is under the target element of our event.

It should be noted that the target attribute is the reverse, or take the mouseover event as an example. Whether it is triggered from outside or bubbling up from sub-elements, the target attribute always points to the target element of our event, so it is impossible to distinguish the two.

Event Principal and Agent

We need to see what delegator parameter delegator delegator delegator delegator delegator delegator delegator delegator delegator delegator delegator delegator delegator delegator delegator delegator delegates.

  $.fn.on = function(event, selector, data, callback, one){
    ...
    return $this.each(function(_, element){
      // Focus 1 (Auto-unbundling delegation)
      if (one) autoRemove = function(e){
        remove(element, e.type, callback)
        return callback.apply(this, arguments)
      }
      // Focus 1 (delegates matching selectors)
      if (selector) delegator = function(e){
        // Focus 2 (Find the nearest node up)
        var evt, match = $(e.target).closest(selector, element).get(0)
        if (match && match !== element) {
          evt = $.extend(createProxy(e), {currentTarget: match, liveFired: element})
          // Concern 3
          return (autoRemove || callback).apply(match, [evt].concat(slice.call(arguments, 1)))
        }
      }

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

autoRemove is well understood as an automatic unbundling delegation: when called, remove the corresponding one-time callback function, and then perform its historical mission. Why remove it first? The larger possibility is that the callback function is user-defined, and if an uncovered exception occurs, code execution will be interrupted and cannot be removed properly.

delegator is a conditional delegate, and events can only be responded to if the event source element matches a given CSS selector. Of course, because bubbles exist, the elements on the bubbles path are event source elements, so each time the first element matching the CSS selector is looked up from the event source until it exceeds the given element range (that is, not its descendant node).

Next, a proxy is created for the event, and two attributes are added, one for the target element and the other for the trigger event. Then the delegate task is completed, and if the autoRemove delegate exists, it is left to execute it, otherwise the callback function is called by itself. Finally, the add() function is passed in for further processing.~

Now look back at the event proxy in the add() function:

      handler.del   = delegator
      var callback  = delegator || fn
      handler.proxy = function(e){
        e = compatible(e)
        // Concern 1
        if (e.isImmediatePropagationStopped()) return
        // Concern 2
        e.data = data
        var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))
        // Concern 3
        if (result === false) e.preventDefault(), e.stopPropagation()
        return result
      }

Because stopImmediatePropagation() not only prevents event bubbling, but also prevents the response of other callbacks, it is necessary to judge whether the function has been executed in the proxy, and only if it is allowed to execute, the callback function will be executed. The result of the callback function also affects the subsequent bubbles.

Registered monitoring

This is relatively simple, with only two minor operations to be noted.

  // Focus 2 (Compatible Conversion for Fousin/Fousout)
  var focusinSupported = 'onfocusin' in window,
      focus = { focus: 'focusin', blur: 'focusout' },
      hover = { mouseenter: 'mouseover', mouseleave: 'mouseout' }
  // Focus 3 (Pseudo-compatibility support.)
  function eventCapture(handler, captureSetting) {
    return handler.del &&
      (!focusinSupported && (handler.e in focus)) ||
      !!captureSetting
  }
  // Concern 2
  function realEvent(type) {
    return hover[type] || (focusinSupported && focus[type]) || type
  }
  function add(element, events, fn, data, selector, delegator, capture){
    ...
    events.split(/\s/).forEach(function(event){
      ...
      var handler   = parse(event)
      ...
      if (handler.e in hover) fn = function(e){...}
      ...
      handler.proxy = function(e){...}
      ...
      // Concern 1
      if ('addEventListener' in element)
        element.addEventListener(realEvent(handler.e), handler.proxy, eventCapture(handler, capture))
    })
  }

The first is a compatibility process, which has been explored before for the simulation of mouseenter and mouseout. There is also a problem that quite a number of browsers do not support focusin / focusout events, such as (mainly) FF up to January this year. So we can only use the older event type name focus / blur. realEvent() is the conversion of event type names.

In addition, the former is produced before the element gains or loses focus, and will bubbles; the latter is triggered after the focus shifts, and will not bubbles. For this compatibility problem, there is no good simulation scheme, which can only be triggered at the capture stage, resulting in a bubble illusion. In fact, the order of triggering is still the order of downward propagation, and stopPropagation() will break the entire propagation path, so be careful when using it. This operation is embodied in eventCapture().

Finally, there's a captureSetting parameter that can't find any way to get in from users. Maybe Zepto's Event module doesn't provide an interface for registering callbacks during the capture phase.

Deep remove function

Find the callback handle object

First, look at the description of the $.fn.off() function:

  1. To pass in the same function as when calling $. fn.on();
  2. If only the event type name is passed in, all event callbacks of that type are unbound.
  3. If nothing is passed, unbind all event callbacks for the current element.

The first point may be doubtful. How do you know if it's the same function? We know above that each element binds a _zid, which maintains a collection of handlers with each id as an index, and the callback function we pass in is bound to handler.fn. It seems that there is a way to find these handlers.

  function zid(element) {
    return element._zid || (element._zid = _zid++)
  }
  function findHandlers(element, event, fn, selector) {
    event = parse(event)
    // Focus 1 (Generating rules for matching namespaces)
    if (event.ns) var matcher = matcherFor(event.ns)
    return (handlers[zid(element)] || []).filter(function(handler) {
      // Focus 3 (Screening eligible handler s)
      return handler
        && (!event.e  || handler.e == event.e)
        && (!event.ns || matcher.test(handler.ns))
        && (!fn       || zid(handler.fn) === zid(fn))
        && (!selector || handler.sel == selector)
    })
  }
  function parse(event) {
    var parts = ('' + event).split('.')
    // Focus 2 (Namespace dictionary order)
    return {e: parts[0], ns: parts.slice(1).sort().join(' ')}
  }
  function matcherFor(ns) {
    // Concern 1
    return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)')
  }

Because of the existence of event namespaces, the search process takes a little more effort. It seems that matcherFor() is doing this.

We know that events with multiple namespaces can last like this: event.nsF.nsA.nsC, and eventually event.ns should be nsA nsC nsF (in dictionary order). What if I want to match it? It's not good to be equal directly, because something like nsA nsB nsC nsF has an inclusion relationship with it. We need a regular expression to filter out the namespace inserted on both sides and in the middle.

Filtering on both sides is easy because namespace strings are separated by spaces, which are the origin of two capture groups (?:^ |) and (?:|$).

It's also easier to filter in the middle, just match any character with a non-greedy match, that is, the function of *?

So regularities like (?: ^ |) nsA *? NsC *? NsF (?: |$) can match the following:

  nsA nsC nsF
  nsA nsB nsC nsF
  ns0 nsA nsB nsC nsF
  ns9 nsA nsB nsC nsD nsE nsF nsZ

However, it is obvious that.. Zepto has made another mistake,. replace(','. *?') will only match and replace the first occurrence of the space, which does not meet the requirements. The correct replacement should be. replace(/ g,'. *?').

After that, the process is relatively simple. Get the handlers[_zid] array and sift each bound handler handle object once. Since all four conditions are optional, a! xxx | | judgment is used to ensure that if there is an equal judgment, then it is skipped.

Implementation of remove() function

Then it's very simple.

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

After finding the corresponding handler (maybe more than one), delete directly and roughly. The performance is guaranteed, but the space is wasted, there will still be an undefined "pit" left there, and it will not be filled by the subsequent binding handler. However, since an element can't bind many event callbacks, this makes sense.

Earlier, I thought it would be a maintenance queue or chain call, but I didn't expect it.

As for listeners whose useCapture is true or false, they are considered to be two different types, so removing event listeners also takes this parameter into account.

Triggering of events

First, look at $. fn.triggerHandler(), which only triggers callbacks related to events and does not actually dispatch events.

  // triggers event handlers on current element just as if an event occurred,
  // doesn't trigger an actual event, doesn't bubble
  $.fn.triggerHandler = function(event, args){
    var e, result
    this.each(function(i, element){
      // Focus 1 (wrapping a layer of event agents)
      e = createProxy(isString(event) ? $.Event(event) : event)
      e._args = args
      e.target = element
      // Focus 2 (directly triggering all associated handle objects)
      $.each(findHandlers(element, event.type || event), function(i, handler){
        result = handler.proxy(e)
        if (e.isImmediatePropagationStopped()) return false
      })
    })
    return result
  }

First of all, we will do an event proxy to avoid modifying the original event object directly. The handler of the corresponding event type is found and the response is executed by its proxy function handler.proxy(). This direct trigger naturally does not bubbles without passing through the DOM event stream.

In the case of multiple handlers, if stopImmediatePropagation(), the traversal is terminated and subsequent handlers are no longer triggered (I always feel that the sequence of triggers is quite random). The return value of this function depends on the return value of the last handler response event.

Also remember the sentence in handler.proxy():

        var result = callback.apply(element, e._args == undefined ? [e] : [e].concat(e._args))

Only when the event is triggered manually will there be a parameter of _args, which is passed directly to the callback function.

Let's look at the last trigger function $. fn.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)
    })
  }

This is almost nothing to analyze, very simple logic. You can tangle this pointer. The outermost this points to the Zepto collection that calls trigger(), and after. each(), it usually points to a single DOM element. If the event type is focus / blur, the DOM element's native method can be invoked directly, and in other cases events can be dispatched through DOM. If this is not a DOM element, Zepto wraps it once to trigger the handle object associated with it directly.

A Fast Way to Common Events

For some common events, we prefer to use methods such as el.click(), el. error ()=> false), rather than write a series of el.on(...), el.bind(...).

  // shortcut methods for `.bind(event, fn)` for each event type
  ;('focusin focusout focus blur load resize scroll unload click dblclick '+
  'mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave '+
  'change select keydown keypress keyup error').split(' ').forEach(function(event) {
    $.fn[event] = function(callback) {
      return (0 in arguments) ?
        this.bind(event, callback) :
        this.trigger(event)
    }
  })

For these event names, the corresponding event methods are mounted on the Zepto prototype. If no parameters are passed, the triggering effect is achieved. A callback function is passed in, and a listener is registered for the event of the calling element.

Series correlation

A General Zepto Source Code Analysis (I) - ie and form Modules
A General Zepto Source Analysis (II) - ajax Module
A General Zepto Source Analysis (3) - Evet Module




This article is based on Knowledge Sharing Signature - Noncommercial Use - Sharing 4.0 International Licensing Agreement in the Same Way Release, citation, reprint or deduction are welcome, but the signature of this article must be retained BlackStorm And links to this article http://www.cnblogs.com/BlackStorm/p/Zepto-Analysing-For-Event-Module.html And it can not be used for commercial purposes without permission. If in doubt or authorized to negotiate, please Contact me .

Posted by koopkoop on Tue, 04 Jun 2019 14:32:36 -0700