Security News
38% of CISOs Fear They’re Not Moving Fast Enough on AI
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
Yes, it's another DOM event delegation toolkit! If you're new to event delegation, here's the gist:
Event delegation is a method for listening for events that bubble to a higher level in the DOM and applying a function only if the event's target meets some criteria (typically that it matches a given CSS selector). The primary advantage over adding event listeners to specific elements is that you don't need to know what's in the DOM to listen for events from certain elements. In other words, you can modify the DOM willy-nilly without having to juggle adding and removing "direct" event listeners whenever certain elements are added and removed.
There are lots of different tools that do delegation. The technique is even baked into jQuery, which behaves "correctly" in the sense that delegated functions match CSS selectors both for the target element and its ancestors.
💥 The big difference between receptor and other tools is that it offers functional, declarative methods for "routing" events via different selectors and keyboard keys, ignoring subtrees of the DOM, adding one-off listeners, and crafting reuseable behaviors.
You can install receptor as a Node module with:
npm install receptor
In CommonJS environments, you may either require the module and access its API via the top-level export:
const receptor = require('receptor');
const click = receptor.delegate('click', 'button', function(e) {
// ...
});
...or, if you're using ES2015 and/or tree-shaking, you can import the top-level API methods directly:
import {behavior, delegateAll} from 'receptor';
// etc.
To use receptor in the browser, you can either bundle it with your CommonJS tool of choice (webpack, rollup, browserify, etc.) or grab the browser-ready build from the dist directory.
receptor.delegate(selector, fn)
Returns a delegated function that only calls the fn
callback if
the event target matches the given CSS selector
, or if it is
contained by an element that does. The callback is called with the
matching element as this
.
Why? Because many delegation tools only handle the case in which
the event's target
matches the given selector, which breaks down
as soon as you want to delegate to elements with children.
<button>hi</button>
<button>hi <i>there</i></button>
<script>
document.body.addEventListener(
'click',
receptor.delegate('button', function(event) {
console.log('clicked button:', this);
})
);
</script>
receptor.delegateAll(selectors)
Returns a delegated function that treats each key in the selectors
map as a CSS selector to match a la receptor.delegate()
, and can either
delegate events to multiple callbacks with matching selectors or
short-circuit delegation to later selectors by returning false
:
<button>some button</button>
<button aria-controls="other">another button</button>
<script>
document.body.addEventListener(
'click',
receptor.delegateAll({
'button[aria-controls]': function(event) {
console.log('clicked controller:', this);
return false; // no other delegates will be called
},
'button': function(event) {
console.log('clicked other button:', this);
}
})
);
receptor.ignore(element, callback)
Returns a delegated function that only calls the callback
if the
event's target
isn't contained by the provided element
. This
is useful for creating event handlers that only fire if the user
interacts with something outside of a given UI element.
receptor.once(callback [, options])
Returns a wrapped function that removes itself as an event listener
as soon as it's called, then calls the callback
function with the
same arugments. If you provide listener options,
those will also be passed to removeEventListener()
.
receptor.keymap(keys)
Returns a delegated function in which each key in the keys
object
is treated as a key name or combination with modifier keys:
document.body.addEventListener('keydown', receptor.keymap({
'ArrowLeft': moveLeft,
'ArrowRight': moveRight,
'Shift+ArrowLeft': moveLeftMore,
'Shift+ArrowRight': moveRightMore
}));
In other words, this is a more declarative alternative to a single event handler with potentially messy key detection logic. Supported modifier keys are Alt, Control (or Ctrl), and Shift.
receptor.behavior(listeners [, properties])
Returns a behavior object defined by one or more delegated
listeners, which exposes add()
and remove()
methods for
attaching and removing all delegates. Other properties
will be
merged if provided.
Each key in listeners
should be one of either:
click
, keydown
, or
mousedown touchstart
(multiple types are separated by spaces),
in which case the value can either be a function or an object
suitable for receptor.delegateAll()
; ortypes:delegate(selector)
, where types
can
be one or more event types separated with spaces, and selector
is a CSS selector.This is the primary building block for developing rich, declarative interactions:
<button role="menubutton" aria-controls="menu">toggle menu</button>
<menu id="menu" aria-hidden="true">
<!-- stuff -->
</menu>
<script>
const MenuButton = receptor.behavior({
'click': {
'[role=menubutton]': function(event) {
let pressed = MenuButton.toggle(this);
if (pressed) {
const close = MenuButton.getCloseListener(this);
window.addEventListener('click', close);
}
}
}
}, {
toggle: function(button, pressed) {
if (typeof pressed !== 'boolean') {
pressed = button.getAttribute('aria-pressed') !== 'true';
}
button.setAttribute('aria-pressed', pressed);
MenuButton.getMenu(button).setAttribute('aria-hidden', !pressed);
return pressed;
},
getMenu: function(button) {
const id = button.getAttribute('aria-controls');
return document.getElementById(id);
},
getCloseListener: function(button) {
return receptor.ignore(button, receptor.once(function() {
MenuButton.toggle(button, false);
}));
}
});
// enable the interaction on any [role=menubutton]
MenuButton.add(document.body);
// later...
// MenuButton.remove(document.body);
</script>
FAQs
A better way to manage DOM event delegation and handling
The npm package receptor receives a total of 8,619 weekly downloads. As such, receptor popularity was classified as popular.
We found that receptor demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
CISOs are racing to adopt AI for cybersecurity, but hurdles in budgets and governance may leave some falling behind in the fight against cyber threats.
Research
Security News
Socket researchers uncovered a backdoored typosquat of BoltDB in the Go ecosystem, exploiting Go Module Proxy caching to persist undetected for years.
Security News
Company News
Socket is joining TC54 to help develop standards for software supply chain security, contributing to the evolution of SBOMs, CycloneDX, and Package URL specifications.