Skip to content

Instantly share code, notes, and snippets.

@andrejewski
Created May 8, 2017 21:23
Show Gist options
  • Select an option

  • Save andrejewski/fe553535217b2f3894a19b18d62ef9be to your computer and use it in GitHub Desktop.

Select an option

Save andrejewski/fe553535217b2f3894a19b18d62ef9be to your computer and use it in GitHub Desktop.

can-dom-events

This is the DOM events API.

The package modules are:

  • can-dom-events which replaces can-util/dom/events
  • can-dom-events/event/make-custom for standardizing custom events
  • can-dom-events/event/make-native for standardizing native events
  • can-dom-events/event/make-compat for compatibility with can-util/dom/events
  • can-dom-events/make-registry for creating event registries

Topics:

  1. Standalone events
    1. Package custom events
    2. Unified native events
  2. Registered events
    1. Generic registries
    2. Global registry
  3. Documentation story

Standalone events

Allows events without the need for a registry. Example:

import myCustomEvent from 'can-event-my'

const el // HTML element
const removeListener = myCustomEvent.addListener(el, () => {
	// called when the custom event triggers
})

removeListener() // removes the handler from the custom event

Packaged events

Custom events that can be their own packages. The prefix is can-event- such as in can-event-radiochange. The make-custom module makes custom events:

// my-event.js
const makeCustom = require('can-dom-events/event/make-custom')
module.exports = makeCustom('my-event', function addListener () {
	/* EventTarget as this */
	/* EventTarget.addEventListener() arguments as arguments */

	// add listener ...
	return function removeListener () {
		// remove listener ...
	}
})

To support can-util/dom/events overriding behavior, pass the result of make-custom to make-compat which returns a compatibility layer which accepts an can-util/dom/events/events object to override:

// my-event-compat.js
const myEvent = require('./my-event')
const makeCompat = require('can-dom-events/event/make-compat')
module.exports = makeCompat(myEvent)
// my-old-can-util-app.js
const domEvents = require('can-util/dom/events/events')
const myEvent = require('./my-event-compat')
const removeEvent = myEvent.override(domEvents)

removeEvent() // un-does the overriding

Unified events

For consistency, native events can adapt to the subscription-disposal pattern. This utility lives in the can-dom-events/event/make-native module.

// can-dom-events/event/make-native.js
const makeCustom = require('./make-native')
module.exports = function makeNativeEvent (eventName) {
	return makeCustom(eventName, function addListener () {
		this.addEventListener.apply(this, arguments)
		return () => {
			this.removeListener.apply(this, arguments)
		}
	})
}
const makeNative = require('can-dom-events/event/make-native')
const clickEvent = makeNative('click')
const cleanupClick = clickEvent.addListener(el, () => {
	// called when the click event triggers
})

cleanupClick() // removes the handler from the click event

The main can-dom-events module calls make-native for unknown events when called with addListener, returning a disposal function as custom events do.

Registered events

Registries group custom events.

Generic registries

The can-dom-events/make-registry module creates registries.

// my-registry.js
const makeRegistry = require('can-dom-events/make-registry')

/*
	NOTE: implementors decide where to store registry data
	Some possible implementations: inside of a DefineMap prop, DOM data, or global map
*/
const sharedStorage = {}
const storageSolution = {
	get (eventName) {
		return sharedStorage[eventName]
	},
	set (eventName, event) {
		sharedStorage[eventName] = event
	},
	remove (eventName) {
		delete sharedStorage[eventName]
	}
}

module.exports = makeRegistry(storageSolution)
// can-dom-events/make-registry.js
module.exports = function makeRegistry (storage) {
	return {
		addEvent (eventName, customEvent) {
			const self = this
			eventName = eventName || customEvent.defaultEventName
			const existingEvent = storage.get(eventName)
			if (existingEvent) {
				// throw error; no duplicate events allowed
			}
			storage.set(eventName, customEvent)
			return function removeEvent () {
				storage.remove(eventName)
			}
		},
		addEventListener (eventName) {
			const customEvent = storage.get(eventName)
			if (customEvent) {
				return customEvent.addListener.apply(this, arguments)
			}
		}
	}
}

Adding events to a registry:

import myCustomEvent from 'can-event-my'
import myRegistry from './my-event-registry'

const removeEvent = myRegistry.addEvent('my-event', myCustomEvent)

const removeListener = myRegistry.addEventListener.call(el, 'my-event', () => {
	// called when the my-event triggers
})

removeListener() // remove listener from event
removeEvent() // remove custom event from registry

Global registry

The custom event .addGlobally() method will add an event to the global registry.

const removeGlobally = require('can-event-my').addGlobally()
// ^ add custom event to global registry

removeGlobally() // remove custom event from global registry

The global registry used by the core can-dom-events module is not exposed.

Documentation story

End users of can-dom-events use can-dom-events directly along with the custom events they choose to use. All other packages are for implementors. The make-registry module is for can-component and other packages which want to support scoped events. The event/make-{custom|native|compat} modules are for custom event package authors to extend. This will give us much greater flexibility in the future to make sweeping interface changes to all event types at once as they all extend from a common base.

The can-util/dom/events split into packages, get marked as deprecated in util, and get removed in can-util's 4.0. The breaking up of packages can happen in pieces thanks to the can-dom-events/event/make-compat layer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment