Add jsExtension snippet #2

Merged
preciel merged 1 commits from dom-utils into main 2025-10-31 14:00:05 +00:00
2 changed files with 198 additions and 0 deletions
Showing only changes of commit 2b6dfa6457 - Show all commits

View File

@ -0,0 +1,96 @@
# js-extension (JavaScript)
A tiny, dependencyfree utility class with a few DOM and string helpers that I often reach for in small projects and prototypes.
It includes:
- `addGlobalEventListener` — delegated events with a CSS selector
- `createElement` — quick DOM element creation with attributes, class, dataset, and text
- `qs` / `qsa` — short aliases for `querySelector` / `querySelectorAll` with optional parent
- `strToHtml` — convert an HTML string to a `DocumentFragment`
- `isJson` — quick check if a string parses as JSON
- `sleep` — simple delay (`Promise`-based)
## Quick usage
Minimal examples showing what the helpers do. Adjust to your environment as needed.
```js
// Assume jsExtension is available in scope
// 1) Delegated event listener
jsExtension.addGlobalEventListener("click", "[data-action=remove]", (e) => {
const btn = e.target.closest("[data-action=remove]");
btn?.closest(".item")?.remove();
});
// 2) Create element with attributes, class, dataset, and text
const card = jsExtension.createElement("div", {
class: "card",
id: "card-1",
dataset: { id: "1", type: "example" },
"aria-label": "Example card",
text: "Hello!",
});
// 3) Shorthand query helpers
const firstItem = jsExtension.qs(".item");
const allItems = jsExtension.qsa(".item");
// 4) String to HTML fragment
const frag = jsExtension.strToHtml(`
<ul>
<li>One</li>
<li>Two</li>
</ul>
`);
document.body.appendChild(frag);
// 5) JSON check
jsExtension.isJson('{"a":1}'); // true
jsExtension.isJson('{a:1}'); // false
// 6) Sleep helper
await jsExtension.sleep(300);
```
## API
### `addGlobalEventListener(type, selector, callback, options?, parent=document)`
Adds a single delegated listener on `parent` and runs `callback` when the event target matches `selector` (or is inside an element that matches).
- `type`: string event type, e.g. `"click"`
- `selector`: CSS selector to match or `closest()` to
- `callback`: function receiving the event
- `options`: optional `addEventListener` options
- `parent`: `HTMLElement | Document` (defaults to `document`)
### `createElement(type, options={}) => HTMLElement`
Creates an element and applies the provided options.
Supported option keys:
- `class`: string — added via `classList.add`
- `dataset`: object — assigned to `element.dataset[key] = value`
- `text`: string — sets `textContent`
- any other key — assigned as attribute: `setAttribute(key, value)`
### `qs(selector, parent=document) => Element|null`
Shorthand for `parent.querySelector(selector)`. Returns `null` if `parent` is not a `Document`/`HTMLElement`.
### `qsa(selector, parent=document) => NodeList<Element>`
Shorthand for `parent.querySelectorAll(selector)`. Falls back to `document.querySelectorAll` when `parent` is not a `Document`/`HTMLElement`.
### `strToHtml(str) => DocumentFragment`
Converts an HTML string into a `DocumentFragment` using `Range#createContextualFragment`.
### `isJson(str) => boolean`
Returns `true` if `JSON.parse(str)` succeeds; otherwise `false`.
### `sleep(time) => Promise<void>`
Resolves after `time` milliseconds using `setTimeout`.
## Notes
- DOM helpers assume a browser environment (i.e., `document` is available).
- `addGlobalEventListener` uses event delegation; attach it once at a suitable ancestor.
## License
See the repository-level `LICENSE` file.

View File

@ -0,0 +1,102 @@
export default class jsExtension {
/**
* @param {string} type
* @param {string} selector
* @param {Function} callback
* @param options
* @param {HTMLElement|Document} parent
*/
static addGlobalEventListener(type, selector, callback, options, parent = document) {
parent.addEventListener(type, (eventListener) => {
const target = eventListener.target;
if (!(target instanceof Element)) return;
if (target.matches(selector) || target.closest(selector)) {
callback(eventListener);
}
}, options);
}
/**
* @param {string} type
* @param {Object} options
* @returns {HTMLElement}
*/
static createElement(type, options = {}) {
const htmlElement = document.createElement(type);
for(const [_key, _value] of /** @type {[string, string][]} */Object.entries(options)) {
if(_key === "class") {
htmlElement.classList.add(_value);
break;
}
if(_key === "dataset") {
Object.entries(_value).forEach(([dataKey, dataValue]) => {
htmlElement.dataset[dataKey] = dataValue;
});
break;
}
if(_key === "text") {
htmlElement.textContent = _value;
break;
}
htmlElement.setAttribute(_key, _value);
}
return htmlElement;
}
/**
* @param {string} selector
* @param {HTMLElement|Document} parent
* @returns {Element|null}
*/
static qs(selector, parent = document) {
return parent instanceof Document || parent instanceof HTMLElement
? parent.querySelector(selector)
: null;
}
/**
* @param {string} selector
* @param {HTMLElement|Document} parent
* @returns {NodeList<Element>}
*/
static qsa(selector, parent = document) {
return parent instanceof Document || parent instanceof HTMLElement
? parent.querySelectorAll(selector)
: document.querySelectorAll(selector);
}
/**
* @param {string} str
* @return {DocumentFragment}
*/
static strToHtml(str) {
return document.createRange().createContextualFragment(str.trim());
}
/**
* @param str
* @returns {boolean}
*/
static isJson(str) {
try { JSON.parse(str); } catch(e) { return false; }
return true;
}
/**
* @param {number} time
* @returns {Promise<void>}
*/
static sleep(time) {
return new Promise(resolve => setTimeout(resolve, time));
}
}