2 * Check if the given param is a HTMLElement
4 export function isHTMLElement(el: any): el is HTMLElement {
5 return el instanceof HTMLElement;
9 * Create a new element with the given attrs and children.
10 * Children can be a string for text nodes or other elements.
12 export function elem(tagName: string, attrs: Record<string, string> = {}, children: Element[]|string[] = []): HTMLElement {
13 const el = document.createElement(tagName);
15 for (const [key, val] of Object.entries(attrs)) {
17 el.removeAttribute(key);
19 el.setAttribute(key, val);
23 for (const child of children) {
24 if (typeof child === 'string') {
25 el.append(document.createTextNode(child));
35 * Run the given callback against each element that matches the given selector.
37 export function forEach(selector: string, callback: (el: Element) => any) {
38 const elements = document.querySelectorAll(selector);
39 for (const element of elements) {
45 * Helper to listen to multiple DOM events
47 export function onEvents(listenerElement: Element, events: string[], callback: (e: Event) => any): void {
48 for (const eventName of events) {
49 listenerElement.addEventListener(eventName, callback);
54 * Helper to run an action when an element is selected.
55 * A "select" is made to be accessible, So can be a click, space-press or enter-press.
57 export function onSelect(elements: HTMLElement|HTMLElement[], callback: (e: Event) => any): void {
58 if (!Array.isArray(elements)) {
59 elements = [elements];
62 for (const listenerElement of elements) {
63 listenerElement.addEventListener('click', callback);
64 listenerElement.addEventListener('keydown', event => {
65 if (event.key === 'Enter' || event.key === ' ') {
66 event.preventDefault();
74 * Listen to key press on the given element(s).
76 function onKeyPress(key: string, elements: HTMLElement|HTMLElement[], callback: (e: KeyboardEvent) => any): void {
77 if (!Array.isArray(elements)) {
78 elements = [elements];
81 const listener = (event: KeyboardEvent) => {
82 if (event.key === key) {
87 elements.forEach(e => e.addEventListener('keydown', listener));
91 * Listen to enter press on the given element(s).
93 export function onEnterPress(elements: HTMLElement|HTMLElement[], callback: (e: KeyboardEvent) => any): void {
94 onKeyPress('Enter', elements, callback);
98 * Listen to escape press on the given element(s).
100 export function onEscapePress(elements: HTMLElement|HTMLElement[], callback: (e: KeyboardEvent) => any): void {
101 onKeyPress('Escape', elements, callback);
105 * Set a listener on an element for an event emitted by a child
106 * matching the given childSelector param.
107 * Used in a similar fashion to jQuery's $('listener').on('eventName', 'childSelector', callback)
109 export function onChildEvent(
110 listenerElement: HTMLElement,
111 childSelector: string,
113 callback: (this: HTMLElement, e: Event, child: HTMLElement) => any
115 listenerElement.addEventListener(eventName, (event: Event) => {
116 const matchingChild = (event.target as HTMLElement|null)?.closest(childSelector) as HTMLElement;
118 callback.call(matchingChild, event, matchingChild);
124 * Look for elements that match the given selector and contain the given text.
125 * Is case-insensitive and returns the first result or null if nothing is found.
127 export function findText(selector: string, text: string): Element|null {
128 const elements = document.querySelectorAll(selector);
129 text = text.toLowerCase();
130 for (const element of elements) {
131 if ((element.textContent || '').toLowerCase().includes(text) && isHTMLElement(element)) {
139 * Show a loading indicator in the given element.
140 * This will effectively clear the element.
142 export function showLoading(element: HTMLElement): void {
143 element.innerHTML = '<div class="loading-container"><div></div><div></div><div></div></div>';
147 * Get a loading element indicator element.
149 export function getLoading(): HTMLElement {
150 const wrap = document.createElement('div');
151 wrap.classList.add('loading-container');
152 wrap.innerHTML = '<div></div><div></div><div></div>';
157 * Remove any loading indicators within the given element.
159 export function removeLoading(element: HTMLElement): void {
160 const loadingEls = element.querySelectorAll('.loading-container');
161 for (const el of loadingEls) {
167 * Convert the given html data into a live DOM element.
168 * Initiates any components defined in the data.
170 export function htmlToDom(html: string): HTMLElement {
171 const wrap = document.createElement('div');
172 wrap.innerHTML = html;
173 window.$components.init(wrap);
174 const firstChild = wrap.children[0];
175 if (!isHTMLElement(firstChild)) {
176 throw new Error('Could not find child HTMLElement when creating DOM element from HTML');