Last active
June 21, 2025 13:42
-
-
Save StoneyEagle/5df834788300bd403d9a203b33ed8467 to your computer and use it in GitHub Desktop.
Typesafe create element
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
export interface CreateElement<E extends Element> { | |
addClasses: (names: string[]) => AddClasses<E>; | |
appendTo: <P extends Element>(parent: P) => AddClassesReturn<E>; | |
prependTo: <P extends Element>(parent: P) => AddClassesReturn<E>; | |
get: () => E; | |
} | |
export interface AddClasses<E extends Element> { | |
appendTo: <P extends Element>(parent: P) => AddClassesReturn<E>; | |
prependTo: <P extends Element>(parent: P) => AddClassesReturn<E>; | |
addClasses: (names: string[]) => AddClasses<E>; | |
get: () => E; | |
} | |
export interface AddClassesReturn<E extends Element> { | |
addClasses: (names: string[]) => AddClasses<E>; | |
get: () => E; | |
} | |
/** | |
* Creates a new HTML element of the specified type and assigns the given ID to it. | |
* @param type - The type of the HTML element to create. | |
* @param id - The ID to assign to the new element. | |
* @param unique - Whether to use an existing element with the specified ID if it already exists. | |
* @returns An object with four methods: | |
* - `addClasses`: adds the specified CSS class names to the element's class list and returns the functions recursively. | |
* - `appendTo`: appends the element to a parent element and and returns addClasses and get methods. | |
* - `prependTo`: prepends the element to a parent element and returns addClasses and get methods. | |
* - `get`: returns the created element. | |
*/ | |
function createElement<K extends keyof HTMLElementTagNameMap>(type: K, id: string, unique?: boolean) { | |
let el: HTMLElementTagNameMap[K]; | |
if (unique) { | |
el = (document.getElementById(id) ?? document.createElement(type)) as HTMLElementTagNameMap[K]; | |
} else { | |
el = document.createElement(type); | |
} | |
el.id = id; | |
return { | |
addClasses: (names: string[]) => addClasses(el, names), | |
appendTo: <T extends Element>(parent: T) => { | |
parent.appendChild(el); | |
return { | |
addClasses: (names: string[]) => addClasses(el, names), | |
get: () => el, | |
}; | |
}, | |
prependTo: <T extends Element>(parent: T) => { | |
parent.prepend(el); | |
return { | |
addClasses: (names: string[]) => addClasses(el, names), | |
get: () => el, | |
}; | |
}, | |
get: () => el, | |
}; | |
} | |
/** | |
* Adds the specified CSS class names to the given element's class list. | |
* | |
* @param el - The element to add the classes to. | |
* @param names - An array of CSS class names to add. | |
* @returns An object with three methods: | |
* - `appendTo`: appends the element to a parent element and returns addClasses and get methods. | |
* - `prependTo`: prepends the element to a parent element and returns addClasses and get methods. | |
* - `get`: returns the created element. | |
* @template T - The type of the element to add the classes to. | |
*/ | |
function addClasses<T extends Element>(el: T, names: string[]): AddClasses<T> { | |
for (const name of names.filter(Boolean)) { | |
el.classList?.add(name.trim()); | |
} | |
return { | |
appendTo: <T extends Element>(parent: T) => { | |
parent.appendChild(el); | |
return { | |
addClasses: (names: string[]) => addClasses(el, names), | |
get: () => el, | |
}; | |
}, | |
prependTo: <T extends Element>(parent: T) => { | |
parent.prepend(el); | |
return { | |
addClasses: (names: string[]) => addClasses(el, names), | |
get: () => el, | |
}; | |
}, | |
addClasses: (names: string[]) => addClasses(el, names), | |
get: () => el, | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment