Created
September 26, 2025 19:32
-
-
Save RepComm/92c28e71b6ee1fc54f2777d39ff22a07 to your computer and use it in GitHub Desktop.
elem.js
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /**@typedef {keyof HTMLElementTagNameMap} ElemTypeId */ | |
| /**@typedef {HTMLElementTagNameMap[ElemTypeId]} ElemType*/ | |
| /**@typedef {keyof CSSStyleDeclaration} ElemStyleKey */ | |
| /**@typedef {Partial<CSSStyleDeclaration>} ElemStyleObj*/ | |
| /**@typedef {{[key: string]: ElemStyleObj}} ElemStyleEntries*/ | |
| /**@param {string} s*/ | |
| function stringCamelToLowerHyphen(s) { | |
| const result = [] | |
| for (const ch of s) { | |
| const lower = ch.toLowerCase() | |
| if (ch !== lower) { | |
| result.push("-") | |
| result.push(lower) | |
| } else { | |
| result.push(ch) | |
| } | |
| } | |
| return result.join("") | |
| } | |
| /** | |
| * @param {ElemStyleEntries} entries | |
| * @returns {string} | |
| */ | |
| function styleEntriesToTextContent(entries) { | |
| const results = [] | |
| for (const entryKey in entries) { | |
| const entry = entries[entryKey] | |
| results.push(entryKey, " {\n") | |
| for (const key in entry) { | |
| const name = stringCamelToLowerHyphen(key) | |
| const value = entry[key] | |
| results.push(" ", name, ":", value, ";\n") | |
| } | |
| results.push("}\n") | |
| } | |
| return results.join("") | |
| } | |
| /** | |
| * @template {keyof HTMLElementEventMap} EvtType | |
| * @typedef {(evt: HTMLElementEventMap[EvtType])=>void } EvtCb<EvtType> | |
| */ | |
| /** | |
| * @template E | |
| */ | |
| export class Elem { | |
| /**@type {E} */ | |
| current | |
| /** | |
| * @template {keyof HTMLElementTagNameMap} K | |
| * @param {K} type | |
| * @returns {Elem<HTMLElementTagNameMap[K]>} | |
| */ | |
| static make(type) { | |
| const result = new Elem() | |
| result.current = document.createElement(type) | |
| return result | |
| } | |
| /** | |
| * @template En | |
| * @param {En} e | |
| * @returns {Elem<En>} | |
| */ | |
| static wrap(e) { | |
| if (e instanceof Elem) return e | |
| const result = new Elem() | |
| result.current = e | |
| return result | |
| } | |
| static querySelector(selectors) { | |
| const e = document.querySelector(selectors) | |
| if (!e) { | |
| return null; | |
| } | |
| const result = new Elem() | |
| result.current = e | |
| return result | |
| } | |
| /**@returns {HTMLElement}*/ | |
| get _e() { | |
| return this.current | |
| } | |
| /** | |
| * @template {keyof HTMLElementEventMap} EvtType | |
| * @param {EvtType} type | |
| * @param {EvtCb<EvtType>} cb | |
| */ | |
| on(type, cb) { | |
| this._e.addEventListener(type, cb) | |
| return this | |
| } | |
| /** | |
| * @param {string} type | |
| * @param {EvtCb<any>} cb | |
| */ | |
| off(type, cb) { | |
| this._e.removeEventListener(type, cb) | |
| return this | |
| } | |
| /** | |
| * @template {keyof HTMLElementEventMap} EvtType | |
| * @param {EvtType} type | |
| * @param {EvtCb<EvtType>} cb | |
| */ | |
| once(type, cb) { | |
| this._e.addEventListener(type, cb, { | |
| once: true | |
| }) | |
| return this | |
| } | |
| /**@param {...string} cnames */ | |
| addClasses(...cnames) { | |
| /**@type {HTMLElement} */ | |
| const e = this.current | |
| e.classList.add(...cnames) | |
| return this | |
| } | |
| /**@param {...string} cnames */ | |
| removeClasses(...cnames) { | |
| /**@type {HTMLElement} */ | |
| const e = this.current | |
| e.classList.remove(...cnames) | |
| return this | |
| } | |
| /** | |
| * @param {...Elem|HTMLElement} children | |
| */ | |
| append(...children) { | |
| for (const ch of children) { | |
| if (!ch) continue | |
| if (ch instanceof Elem) { | |
| if (!ch.current) continue | |
| this._e.appendChild(ch.current) | |
| } else { | |
| this._e.appendChild(ch) | |
| } | |
| } | |
| return this | |
| } | |
| /**@param {Elem|HTMLElement} parent*/ | |
| appendTo(parent) { | |
| if (parent instanceof Elem) { | |
| parent._e.appendChild(this._e) | |
| } else { | |
| parent.appendChild(this._e) | |
| } | |
| return this | |
| } | |
| /** | |
| * @param {...Elem|HTMLElement} children | |
| */ | |
| remove(...children) { | |
| for (const ch of children) { | |
| if (!ch) continue | |
| if (ch instanceof Elem) { | |
| if (!ch.current) continue | |
| this._e.removeChild(ch.current) | |
| } else { | |
| this._e.removeChild(ch) | |
| } | |
| } | |
| return this | |
| } | |
| clear() { | |
| for (const child of this._e.children) { | |
| child.remove() | |
| } | |
| return this | |
| } | |
| /**@param {string} text */ | |
| setTextContent(text) { | |
| this._e.textContent = text | |
| return this | |
| } | |
| /**@param {string} text */ | |
| setInnerText(text) { | |
| this._e.innerText = text | |
| return this | |
| } | |
| /**@param {string} type*/ | |
| setType(type) { | |
| /**@type {HTMLInputElement} */ | |
| const i = this.current | |
| i.type = type | |
| return this | |
| } | |
| /**@param {string} id */ | |
| setId(id) { | |
| this._e.id = id | |
| return this | |
| } | |
| /**@param {any} v*/ | |
| setValue(v) { | |
| this._e.value = v | |
| return this | |
| } | |
| get value() { | |
| return this._e.value | |
| } | |
| /**@param {ElemStyleEntries} entries*/ | |
| setStyleElement(entries) { | |
| this.setTextContent( | |
| styleEntriesToTextContent(entries) | |
| ) | |
| return this | |
| } | |
| /**@param {ElemStyleObj} sobj*/ | |
| setStyle(sobj) { | |
| for (const k in sobj) { | |
| const value = sobj[k] | |
| const name = k //stringCamelToLowerHyphen(k) | |
| this._e.style[name] = value | |
| } | |
| return this | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment