jQuery Source Series (XIV) Custom Events

Keywords: Javascript JQuery github

Welcome. My column Check out a series of articles.

Previously, I only knew that click events would only start when clicking on elements in browsers, as would other events, requiring human mouse operations.

Later, with the continuous deepening of learning, it was known that the original JS can write functions to control the execution of events, so that it is interesting to write code. I remember a long time ago, some malicious websites, clearly the mouse did not click, but was forced to click on a link by the website, probably in this way.

Native event

In fact, the native events of JS have been well done, but jQuery encapsulates them and makes them better.

about document.createEvent Here is a simple example of event clicks:

var fn = function(){
  console.log('click');
}

var button = document.getElementById('#id');
button.addEventListener('click', fn);

// Click on Event MouseEvent
var myClick = document.createEvent('MouseEvent');
myClick.initMouseEvent('click', false, false, null);

// implement
button.dispatchEvent(myClick); // 'click'

In addition to mouse events, you can customize events:

// Customize an event test.click
button.addEventListener('test.click', fn);

var testEvent = document.createEvent('CustomEvent');
// CusmEvent can also be initialized as a mouse event, not necessarily a custom event
testEvent.initCustomEvent('test.click', false, false, null);

button.dispatchEvent(testEvent); // 'click'

JS native simulation events are still very convenient to use, and the above is native.

But jQuery also has its own custom event scenario.

jQuery.trigger

jQuery.trigger can be compared with HTMLElement.dispatchEvent events, which are used to simulate and execute listened events.

How to use

For use, it's simpler. .trigger():

var $body = $(document.body);

// First Bind Events
$body.on('click', function(){
  console.log('click');
})

// implement
$body.trigger('click'); //'click'

trigger also supports more parameters and can also customize events:

$body.on('click.test', function(e, data1, data2){
  console.log(data1 + '-' + data2);
})

$body.trigger('click.test', ['hello', 'world']);

trigger source code

The source code of trigger is somewhat simple, because it is still handled by jQuery.event:

jQuery.fn.extend( {
  trigger: function(type, data){
    return this.each(function(){
      jQuery.event.trigger(type, data, this);
    })
  },
  // triggerHandler handles the first event without triggering the default event
  triggerHandler: function( type, data ) {
    var elem = this[ 0 ];
    if ( elem ) {
      return jQuery.event.trigger( type, data, elem, true );
    }
  }
} );

So the trigger event is back to jQuery.event.

jQuery.event.trigger

Actually, trigger and add + handler functions are very similar. They are basically searching for caches from data cache and executing callback functions. You need to consider whether you want to execute the default event, where the fourth parameter is true.

jQuery.extend(jQuery.event, {
  // ONLEY Handlers denotes that bubble events are not considered
  trigger: function( event, data, elem, onlyHandlers ) {

    var i, cur, tmp, bubbleType, ontype, handle, special,
      eventPath = [ elem || document ],
      type = hasOwn.call( event, "type" ) ? event.type : event,
      namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];

    cur = tmp = elem = elem || document;

    // Don't do events on text and comment nodes
    if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
      return;
    }

    // Asynchronous non-conflict
    if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
      return;
    }

    if ( type.indexOf( "." ) > -1 ) {

      // Namespaced trigger; create a regexp to match event type in handle()
      namespaces = type.split( "." );
      type = namespaces.shift();
      namespaces.sort();
    }
    ontype = type.indexOf( ":" ) < 0 && "on" + type;

    // Refitting native event events
    event = event[ jQuery.expando ] ?
      event :
      new jQuery.Event( type, typeof event === "object" && event );

    // Determine whether only the current trigger event is executed without bubbling
    event.isTrigger = onlyHandlers ? 2 : 3;
    event.namespace = namespaces.join( "." );
    event.rnamespace = event.namespace ?
      new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
      null;

    // Clean up the event in case it is being reused
    event.result = undefined;
    if ( !event.target ) {
      event.target = elem;
    }

    // Clone any incoming data and prepend the event, creating the handler arg list
    data = data == null ?
      [ event ] :
      jQuery.makeArray( data, [ event ] );

    // Allow special events to draw outside the lines
    special = jQuery.event.special[ type ] || {};
    if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
      return;
    }

    // Bubble the document and store the bubbles in the eventPath array
    if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {

      bubbleType = special.delegateType || type;
      if ( !rfocusMorph.test( bubbleType + type ) ) {
        cur = cur.parentNode;
      }
      for ( ; cur; cur = cur.parentNode ) {
        eventPath.push( cur );
        tmp = cur;
      }

      // Only add window if we got to document (e.g., not plain obj or detached DOM)
      if ( tmp === ( elem.ownerDocument || document ) ) {
        eventPath.push( tmp.defaultView || tmp.parentWindow || window );
      }
    }

    // Execution on demand
    i = 0;
    while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {

      event.type = i > 1 ?
        bubbleType :
        special.bindType || type;

      // Getting callback functions from data cache
      handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
        dataPriv.get( cur, "handle" );
      if ( handle ) {
        // implement
        handle.apply( cur, data );
      }

      // Native handler
      handle = ontype && cur[ ontype ];
      if ( handle && handle.apply && acceptData( cur ) ) {
        event.result = handle.apply( cur, data );
        if ( event.result === false ) {
          event.preventDefault();
        }
      }
    }
    event.type = type;

    // If nobody prevented the default action, do it now
    if ( !onlyHandlers && !event.isDefaultPrevented() ) {

      if ( ( !special._default ||
        special._default.apply( eventPath.pop(), data ) === false ) &&
        acceptData( elem ) ) {

        // Call a native DOM method on the target with the same name as the event.
        // Don't do default actions on window, that's where global variables be (#6170)
        if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) {

          // Don't re-trigger an onFOO event when we call its FOO() method
          tmp = elem[ ontype ];

          if ( tmp ) {
            elem[ ontype ] = null;
          }

          // Prevent re-triggering of the same event, since we already bubbled it above
          jQuery.event.triggered = type;
          elem[ type ]();
          jQuery.event.triggered = undefined;

          if ( tmp ) {
            elem[ ontype ] = tmp;
          }
        }
      }
    }

    return event.result;
  },
})

summary

In jQuery.event.trigger, it is interesting to simulate bubbling mechanism. The general idea is:

  1. Save the current elem into an array;

  2. Find the parent element of the current elem, if it matches, push into the array, repeat the first step, otherwise the next step;

  3. Traverse the array to see if the type event is bound from the data cache, and then execute it in turn.

Bubble event is the process of bubbling from inside to outside, which is not very complicated.

event events should be all about that.

Reference resources

Decrypting jQuery Event Core - Custom Design (3)
MDN createEvent
Decrypting jQuery Event Core - Simulated Events (4)

On github Source address Welcome to star.

Welcome. My blog Communication.

Posted by ow-phil on Sat, 13 Apr 2019 11:09:32 -0700