/** * Returns the 1st HTMLElement or , which corresponds to the , * @param query - CSS selector as a string * @returns {HTMLElement} - the 1st element found */ const $$ = query => Array.from(document.querySelectorAll(query)) /** * Binds an event handler (=function) to a DOM element * @param element - the target element, e.g. button * @param event - the event, e.g. 'click' * @param func - the function to be called, e.g. handleValidation * @returns {*} - the target element */ const $on = (element, event, func) => { Array.isArray(element) ? element.forEach(arrayElement => $on(arrayElement, event, func)) : element.addEventListener(event, func) return element } /** * Runs through the HTML document and renders all handlebars script tags * @param data - the data to be rendered * @param querySelector - an optional querySelector * @returns {Promise} */ const render = async (data, querySelector) => { Handlebars.registerHelper('toFixed', function(num) { return num && num.toFixed(2); }); const selector = querySelector || '[type="text/x-handlebars-template"]' const templates = $$(selector) for (const source of templates) { await loadPartials(source) const template = Handlebars.compile(source.innerHTML) const target = source.parentElement // remove former HTML elements if (target.children.length > 1) { const start = querySelector? 0 : 1 for (let i = start; i < target.children.length; i++) { target.lastElementChild.remove() } } // insert refreshed HTML elements target.insertAdjacentHTML('beforeend', template(data)) } } /** * Loads partials with the file extension '.html' from the same directory * @param code - the source code to be parsed * @returns {Promise} */ async function loadPartials(code) { const partialNames = code.innerText.match(/(?<={{(#>|>)).+?(?=\s)/g) if (partialNames) { for (let name of partialNames) { name = name.trim() const fileName = name + '.html' const partialCode = await fetch(fileName).then(response => response.text()) Handlebars.registerPartial(name, partialCode) } } }