@thednp/event-listener
Advanced tools
Comparing version 1.0.4 to 2.0.0-alpha1
{ | ||
"name": "@thednp/event-listener", | ||
"author": "thednp", | ||
"version": "1.0.4", | ||
"version": "2.0.0alpha1", | ||
"description": "Modern event listener for efficient web applications based on subscribe-publish pattern.", | ||
"license": "MIT", | ||
"homepage": "https://thednp.github.io/event-listener", | ||
"main": "dist/event-listener.min.js", | ||
"module": "dist/event-listener.esm.js", | ||
"jsnext": "src/index.js", | ||
"main": "src/index.ts", | ||
"module": "dist/index.cjs.js", | ||
"esnext": "src/index.js", | ||
"types": "types/index.d.ts", | ||
@@ -26,3 +26,4 @@ "repository": { | ||
"listener", | ||
"javascript-events" | ||
"javascript-events", | ||
"typescript" | ||
], | ||
@@ -39,30 +40,31 @@ "files": [ | ||
"scripts": { | ||
"test": "cypress run", | ||
"cypress": "npx cypress open", | ||
"pre-test": "npm run clean-coverage && npx tsc", | ||
"test": "npm run pre-test && cypress run", | ||
"clean": "del-cli --force dist", | ||
"clean-coverage": "del-cli --force coverage .nyc_output", | ||
"cypress": "npm run pre-test && npx cypress open", | ||
"coverage:report": "nyc report --reporter=lcov --reporter=json --reporter=text --reporter=json-summary", | ||
"build": "npm run fix:js && npm-run-all --parallel build-*", | ||
"custom-build": "rollup -c --environment", | ||
"fix:js": "eslint src/ --config .eslintrc --fix", | ||
"lint:js": "eslint src/ --config .eslintrc", | ||
"build:ts": "tsc -d", | ||
"build-js": "rollup --environment FORMAT:umd,MIN:false -c", | ||
"build-docs": "rollup --environment FORMAT:umd,MIN:false,OUTPUTFILE:docs/event-listener.js -c", | ||
"build-js-min": "rollup --environment FORMAT:umd,MIN:true -c", | ||
"build-js-es5": "rollup --environment FORMAT:umd,MIN:false,ES5:true,OUTPUTFILE:dist/event-listener-es5.js -c", | ||
"build-js-es5-min": "rollup --environment FORMAT:umd,MIN:true,ES5:true,OUTPUTFILE:dist/event-listener-es5.min.js -c", | ||
"build-esm": "rollup --environment FORMAT:esm,MIN:false -c", | ||
"build-esm-min": "rollup --environment FORMAT:esm,MIN:true -c" | ||
"build": "npm run fix:ts && npm-run-all --parallel build-*", | ||
"fix:ts": "eslint src/ --fix", | ||
"lint:ts": "eslint src/", | ||
"build:ts": "del-cli --force ./types && tsc -d --removeComments --emitDeclarationOnly --declarationMap --outDir ./types", | ||
"build-js": "node esbuild.config.js FORMAT:iife", | ||
"build-docs": "node esbuild.config.js FORMAT:iife OUTPUTFILE:docs/event-listener.js", | ||
"build-minjs": "node esbuild.config.js MIN:true FORMAT:iife", | ||
"build-esm": "node esbuild.config.js", | ||
"build-minesm": "node esbuild.config.js MIN:true", | ||
"build-mod": "node esbuild.config.js FORMAT:cjs", | ||
"build-minmod": "node esbuild.config.js MIN:true FORMAT:cjs" | ||
}, | ||
"devDependencies": { | ||
"@bahmutov/cypress-esbuild-preprocessor": "^2.1.3", | ||
"@cypress/code-coverage": "^3.9.12", | ||
"@rollup/plugin-buble": "^0.21.3", | ||
"@rollup/plugin-json": "^4.1.0", | ||
"@rollup/plugin-node-resolve": "^7.1.0", | ||
"@rollup/plugin-typescript": "^8.3.0", | ||
"cypress": "^9.6.0", | ||
"@cypress/code-coverage": "^3.10.0", | ||
"@types/istanbul-lib-instrument": "^1.7.4", | ||
"@typescript-eslint/eslint-plugin": "^5.31.0", | ||
"@typescript-eslint/parser": "^5.31.0", | ||
"cypress": "^10.3.1", | ||
"del-cli": "^5.0.0", | ||
"esbuild": "^0.14.30", | ||
"eslint": "^7.22.0", | ||
"eslint-config-airbnb-base": "^14.2.1", | ||
"eslint-plugin-import": "^2.22.1", | ||
"eslint": "^8.20.0", | ||
"eslint-plugin-prettier": "^4.2.1", | ||
"istanbul-lib-coverage": "^3.2.0", | ||
@@ -72,6 +74,4 @@ "istanbul-lib-instrument": "^5.2.0", | ||
"nyc": "^15.1.0", | ||
"rollup": "^2.38.5", | ||
"rollup-plugin-terser": "^5.3.1", | ||
"typescript": "^4.5.2" | ||
"typescript": "^4.7.4" | ||
} | ||
} |
@@ -1,21 +0,24 @@ | ||
# EventListener [![Coverage Status](https://coveralls.io/repos/github/thednp/event-listener/badge.svg?branch=main)](https://coveralls.io/github/thednp/event-listener?branch=main) ![cypress version](https://img.shields.io/badge/cypress-9.6.0-brightgreen) ![typescript version](https://img.shields.io/badge/typescript-4.5.2-brightgreen) [![ci](https://github.com/thednp/event-listener/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/thednp/event-listener/actions/workflows/ci.yml) | ||
## EventListener | ||
Modern event listener for efficient applications based on the [subscribe-publish](https://hackernoon.com/do-you-still-register-window-event-listeners-in-each-component-react-in-example-31a4b1f6f1c8) pattern. | ||
[![Coverage Status](https://coveralls.io/repos/github/thednp/event-listener/badge.svg)](https://coveralls.io/github/thednp/event-listener) | ||
[![ci](https://github.com/thednp/event-listener/actions/workflows/ci.yml/badge.svg)](https://github.com/thednp/event-listener/actions/workflows/ci.yml) | ||
[![NPM Version](https://img.shields.io/npm/v/@thednp/event-listener.svg)](https://www.npmjs.com/package/@thednp/event-listener) | ||
[![NPM Downloads](https://img.shields.io/npm/dm/@thednp/event-listener.svg)](http://npm-stat.com/charts.html?package=@thednp/event-listener) | ||
[![jsDeliver](https://img.shields.io/jsdelivr/npm/hw/@thednp/event-listener)](https://www.jsdelivr.com/package/npm/@thednp/event-listener) | ||
[![cypress version](https://img.shields.io/badge/cypress-10.3.1-brightgreen)](https://cypress.io/) | ||
[![typescript version](https://img.shields.io/badge/typescript-4.7.4-brightgreen)](https://www.typescriptlang.org/) | ||
[![esbuild version](https://img.shields.io/badge/esbuild-0.14.30-brightgreen)](https://esbuild.github.io/) | ||
**EventListener** is less than 900 bytes when minified, but packs a surprising amount of power. | ||
A TypeScript sourced event listener for efficient applications based on the [subscribe-publish](https://hackernoon.com/do-you-still-register-window-event-listeners-in-each-component-react-in-example-31a4b1f6f1c8) pattern, less than 900 bytes when minified and packs a surprising amount of power. | ||
[![NPM Version](https://img.shields.io/npm/v/@thednp/event-listener.svg?style=flat-square)](https://www.npmjs.com/package/@thednp/event-listener) | ||
[![NPM Downloads](https://img.shields.io/npm/dm/@thednp/event-listener.svg?style=flat-square)](http://npm-stat.com/charts.html?package=@thednp/event-listener) | ||
[![jsDeliver](https://data.jsdelivr.com/v1/package/npm/@thednp/event-listener/badge)](https://www.jsdelivr.com/package/npm/@thednp/event-listener) | ||
## Features | ||
- **EventListener** is TypeScript sourced; | ||
- **EventListener** makes use of the native [Map](https://caniuse.com/mdn-javascript_builtins_map) to subscribe/register or unsubscribe/remove listeners, which is perfect since we need to make sure the exact listeners are added/removed; this completely invalidates the need to [deconstruct function objects](https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript) for comparison's sake to make sure event listeners are properly handled; | ||
- **EventListener** allows you to register multiple listeners for the same target, even of the same type, but always uses a single `globalListener` to call them all at once when event is triggered; | ||
- **EventListener** "should" be able to manage event options, especially `once`, meaning that when the option is `true`, the listener is automatically un-subscribed and detached from target; | ||
- **EventListener** will unsubscribe and detach listeners with the same options used when attached, which means you can "lazy" remove listeners on the fly. | ||
# Features | ||
* **EventListener** is ES6+ sourced with TypeScript definitions; | ||
* **EventListener** comes with ES6, ES5 and ESM packaging, all in `/dist` folder; | ||
* **EventListener** makes use of the native [Map](https://caniuse.com/mdn-javascript_builtins_map) to subscribe/register or unsubscribe/remove listeners, which is perfect since we need to make sure the exact listeners are added/removed; this completely invalidates the need to [deconstruct function objects](https://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-deep-clone-an-object-in-javascript) for comparison's sake to make sure event listeners are properly handled; | ||
* **EventListener** allows you to register multiple listeners for the same target, even of the same type, but always uses a single `globalListener` to call them all at once when event is triggered; | ||
* **EventListener** "should" be able to manage event options, especially `once`, meaning that when the option is `true`, the listener is automatically un-subscribed and detached from target; | ||
* **EventListener** will unsubscribe and detach listeners with the same options used when attached, which means you can "lazy" remove listeners on the fly. | ||
# NPM | ||
# NPM | ||
``` | ||
@@ -25,38 +28,45 @@ npm install @thednp/event-listener | ||
# CDN | ||
## CDN | ||
```html | ||
<script src="https://cdn.jsdelivr.net/npm/@thednp/event-listener/dist/event-listener.min.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/@thednp/event-listener/dist/index.min.js"></script> | ||
``` | ||
# Use | ||
## Use | ||
```js | ||
import EventListener from '@thednp/event-listener'; | ||
import Listener from "@thednp/event-listener"; | ||
// execute a listener once | ||
EventListener.on(document, 'DOMContentLoaded', () => { | ||
console.log('document is now loaded'); | ||
}, { once: true }); | ||
Listener.on( | ||
document, | ||
"DOMContentLoaded", | ||
() => { | ||
console.log("document is now loaded"); | ||
}, | ||
{ once: true } | ||
); | ||
// add a listener with `useCapture: false` | ||
function handleMyClick(e) { | ||
if (e.target.tagName === 'button') { | ||
if (e.target.tagName === "button") { | ||
e.preventDefault(); | ||
e.stopImmediatePropagation(); | ||
} | ||
console.log('do something else instead'); | ||
console.log("do something else instead"); | ||
} | ||
EventListener.on(document, 'click', handleMyClick, false); | ||
Listener.on(document, "click", handleMyClick, false); | ||
// remove a listener, `EventListener` will get listener options from registry | ||
EventListener.off(document, 'click', handleMyClick); | ||
Listener.off(document, "click", handleMyClick); | ||
// add listener to `window`, this listener has no name and cannot be removed | ||
EventListener.on(window, 'scroll', () => console.log(window.scrollY)); | ||
Listener.on(window, "scroll", () => console.log(window.scrollY)); | ||
``` | ||
Since we're implementing `Map`, you can make use of its prototype to access registry: | ||
```js | ||
// get element listener registry | ||
const documentClickListeners = EventListener.registry['click'].get(document); | ||
const documentClickListeners = Listener.registry['click'].get(document); | ||
@@ -86,7 +96,8 @@ // returns | ||
# Advanced Use | ||
## Advanced Use | ||
You can also make use of "tree shaking" to import only the module you want, for instance: | ||
```js | ||
import { on } from '@thednp/event-listener'; | ||
import { on } from "@thednp/event-listener"; | ||
@@ -99,3 +110,3 @@ on(document, handleMyClick, true); | ||
```js | ||
import { addListener } from '@thednp/event-listener/src/event-listener'; | ||
import { addListener } from "@thednp/event-listener/src/event-listener"; | ||
@@ -107,3 +118,12 @@ addListener(document, handleMyClick, true); | ||
# License | ||
**EventListener** is released under the [MIT License](https://github.com/thednp/event-listener/blob/main/LICENSE). | ||
## Run the tests suite (new) | ||
- [Download](https://github.com/thednp/event-listener/archive/refs/heads/master.zip) the package from Github; | ||
- unpack/unzip and open the folder with your editor; | ||
- open your terminal and navigate to the root of the unpacked folder; | ||
- run `npm install` or `npm update`, takes a few minutes to download the Electron browser; | ||
- run `npm run cypress` to open the Cypress console OR `npm run test` to run the tests in headless mode. | ||
## License | ||
**EventListener** is released under the [MIT License](https://github.com/thednp/event-listener/blob/main/LICENSE). |
@@ -1,3 +0,56 @@ | ||
import Listener from './event-listener'; | ||
const EventRegistry = {}; | ||
export { EventRegistry }; | ||
export function globalListener(e) { | ||
const that = this; | ||
const { type } = e; | ||
[...EventRegistry[type]].forEach((elementsMap) => { | ||
const [element, listenersMap] = elementsMap; | ||
if (element === that) { | ||
[...listenersMap].forEach((listenerMap) => { | ||
const [listener, options] = listenerMap; | ||
listener.apply(element, [e]); | ||
if (options && options.once) { | ||
removeListener(element, type, listener, options); | ||
} | ||
}); | ||
} | ||
}); | ||
} | ||
export const addListener = (element, eventType, listener, options) => { | ||
if (!EventRegistry[eventType]) { | ||
EventRegistry[eventType] = new Map(); | ||
} | ||
const oneEventMap = EventRegistry[eventType]; | ||
if (!oneEventMap.has(element)) { | ||
oneEventMap.set(element, new Map()); | ||
} | ||
const oneElementMap = oneEventMap.get(element); | ||
const { size } = oneElementMap; | ||
oneElementMap.set(listener, options); | ||
if (!size) { | ||
element.addEventListener(eventType, globalListener, options); | ||
} | ||
}; | ||
export const removeListener = (element, eventType, listener, options) => { | ||
const oneEventMap = EventRegistry[eventType]; | ||
const oneElementMap = oneEventMap && oneEventMap.get(element); | ||
const savedOptions = oneElementMap && oneElementMap.get(listener); | ||
const eventOptions = savedOptions !== undefined ? savedOptions : options; | ||
if (oneElementMap && oneElementMap.has(listener)) | ||
oneElementMap.delete(listener); | ||
if (oneEventMap && (!oneElementMap || !oneElementMap.size)) | ||
oneEventMap.delete(element); | ||
if (!oneEventMap || !oneEventMap.size) | ||
delete EventRegistry[eventType]; | ||
if (!oneElementMap || !oneElementMap.size) { | ||
element.removeEventListener(eventType, globalListener, eventOptions); | ||
} | ||
}; | ||
const Listener = { | ||
on: addListener, | ||
off: removeListener, | ||
globalListener, | ||
registry: EventRegistry, | ||
}; | ||
export default Listener; | ||
//# sourceMappingURL=index.js.map |
@@ -1,18 +0,14 @@ | ||
export as namespace Listener; | ||
import { EventsRegistry } from "./types"; | ||
declare const EventRegistry: EventsRegistry; | ||
export { EventRegistry }; | ||
export declare function globalListener(e: Event): void; | ||
export declare const addListener: (element: EventTarget, eventType: string, listener: EventListenerObject["handleEvent"], options?: AddEventListenerOptions) => void; | ||
export declare const removeListener: (element: EventTarget, eventType: string, listener: EventListenerObject["handleEvent"], options?: AddEventListenerOptions) => void; | ||
declare const Listener: { | ||
on: (element: EventTarget, eventType: string, listener: EventListenerObject["handleEvent"], options?: AddEventListenerOptions) => void; | ||
off: (element: EventTarget, eventType: string, listener: EventListenerObject["handleEvent"], options?: AddEventListenerOptions) => void; | ||
globalListener: typeof globalListener; | ||
registry: EventsRegistry; | ||
}; | ||
export default Listener; | ||
import './event-listener'; | ||
export type ListenerAction<T> = ( | ||
element: T, | ||
eventType: string, | ||
listener: EventListenerObject['handleEvent'], | ||
options?: AddEventListenerOptions | ||
) => void; | ||
import { default as Listener } from 'event-listener/src/event-listener'; | ||
export { EventRegistry, globalListener, addListener, removeListener } from "event-listener/src/event-listener"; | ||
declare module "@thednp/event-listener" { | ||
export default Listener; | ||
} | ||
//# sourceMappingURL=index.d.ts.map |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
59930
15
25
126
437
1
2