Skip to content

Instantly share code, notes, and snippets.

@RepComm
Created September 26, 2025 19:32
Show Gist options
  • Select an option

  • Save RepComm/92c28e71b6ee1fc54f2777d39ff22a07 to your computer and use it in GitHub Desktop.

Select an option

Save RepComm/92c28e71b6ee1fc54f2777d39ff22a07 to your computer and use it in GitHub Desktop.
elem.js
/**@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