Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
@mangoweb/delegated-events
Advanced tools
A small, fast delegated event library for JavaScript.
import {on, off, fire} from 'delegated-events';
// Listen for browser-generated events.
on(document, 'click', '.js-button', function(event) {
console.log('clicked', this);
});
// Listen for custom events triggered by your app.
on(document, 'robot:singularity', '.js-robot-image', function(event) {
console.log('robot', event.detail.name, this.src);
});
// Dispatch a custom event on an element.
var image = document.querySelector('.js-robot-image');
fire(image, 'robot:singularity', {name: 'Hubot'});
// Stop listening for event type, selector and handler.
off(document, 'click', '.js-button', clickHandler);
// Stop listening for event type and selector.
off(document, 'robot:singularity', '.js-robot-image');
// Stop listening for event type for all selectors.
off(document, 'robot:singularity');
The standard method of registering event handler functions is to directly bind the listener to the element.
// Find an element and bind a function directly to it.
var button = document.querySelector('.js-button');
button.addEventListener('click', function(event) {
console.log('clicked', event.target);
});
If we have several clickable elements, listeners can be directly registered on them in a loop.
// Find all the buttons and attach a function to each of them.
var buttons = document.querySelectorAll('.js-button');
buttons.forEach(function(button) {
button.addEventListener('click', function(event) {
console.log('clicked', event.target);
});
});
Directly binding event listeners to elements works great if the page doesn't change after it's initially loaded. However, if we dynamically add another button to the document, it won't receive a click event.
// No click handler is registered on the new element.
var button = document.createElement('button');
button.textContent = 'Push';
var list = document.querySelector('.js-button-list');
list.appendChild(button);
A solution to this is to delegate the event handler up the tree to the parent element that contains all of the button children. When a button is clicked, the event bubbles up the tree until it reaches the parent, at which point the handler is invoked.
// Event handling delegated to a parent element.
var list = document.querySelector('.js-button-list');
list.addEventListener('click', function(event) {
console.log('clicked', event.target);
});
However, now anything clicked inside the list will trigger this event
handler, not just clicks on buttons. So we add a selector check to determine
if a button
generated the click event, rather than a span
element, text, etc.
// Filter events by matching the element with a selector.
var list = document.querySelector('.js-button-list');
list.addEventListener('click', function(event) {
if (event.target.matches('.js-button')) {
console.log('clicked', event.target);
}
});
Now we have something that works for any button element inside the list whether it was included in the initial HTML page or added dynamically to the document sometime later.
But what if the list element is added to the page dynamically?
If we delegate most events up to the global document
, we no longer worry
about when elements are appended to the page—they will receive event listeners
automatically.
// Delegated click handling up to global document.
document.addEventListener('click', function(event) {
if (event.target.matches('.js-button')) {
console.log('clicked', event.target);
}
});
Now that we've covered how browsers handle directly-bound and delegated events natively, let's look at what this library actually does.
The goals of this library are to:
Delegated event handling shortcuts (on
, off
, fire
) are provided
so event handlers aren't required to test for matching elements
themselves. jQuery has great documentation on event delegation with selectors too.
Here's the same globally delegated handler as above but using on
.
// Easy :)
on(document, 'click', '.js-button', function(event) {
console.log('clicked', event.target);
});
To provide compatibility with older browsers, jQuery uses "synthetic" events. jQuery listens for the native browser event, wraps it inside a new event object, and proxies all function calls, with modifications, through to the native object.
All browsers now share a standard event system, so we can remove the extra layer of event handling to recover performance.
The delegated event system is written in vanilla JavaScript,
so it won't significantly increase download times (minified + gzip = 640 bytes).
It relies on a small SelectorSet
data structure to optimize selector matching against the delegated events.
A micro-benchmark to compare relative event handling performance is included
and can be run with npm run bench
.
A fire
shortcut function is provided to trigger custom events with
attached data objects.
on(document, 'validation:success', '.js-comment-input', function(event) {
console.log('succeeded for', event.detail.login);
});
var input = document.querySelector('.js-comment-input');
fire(input, 'validation:success', {login: 'hubot'});
The standard way of doing this works well but is more verbose.
document.addEventListener('validation:success', function(event) {
if (event.target.matches('.js-comment-input')) {
console.log('succeeded for', event.detail.login);
}
});
var input = document.querySelector('.js-comment-input');
input.dispatchEvent(
new CustomEvent('validation:success', {
bubbles: true,
cancelable: true,
detail: {login: 'hubot'}
})
);
Internet Explorer requires polyfills for CustomEvent
and WeakMap
.
npm run bootstrap
npm test
npm run bench
npm run browser
Distributed under the MIT license. See LICENSE for details.
FAQs
A small, fast delegated event library.
The npm package @mangoweb/delegated-events receives a total of 12 weekly downloads. As such, @mangoweb/delegated-events popularity was classified as not popular.
We found that @mangoweb/delegated-events demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 5 open source maintainers 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
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.