5 ms·
Show HN: A better way of writing HTML via JavaScript?
Although I've been using React for at least 5 years now, I don't quite like frameworks. For personal projects I sometimes use web component but they can be over kill depending on what you are trying to achieve. I do like JSX though. I think it's quite neat to be able to write HTML inside of JavaScript code. With that in mind, I've been trying to come up with a way to better create HTML dynamically.
Something along the lines of:
const req = await fetch('api.com/posts')
const data = await req.json()
for (const d of data)
createPost(d)
For that, I end up writing this piece of code
const assignDeep = (elm, props) => Object.entries(props).forEach(([key, value]) =>
typeof value === 'object' ? assignDeep(elm[key], value) : Object.assign(elm, {[key]: value}))
const $c = (tag, props = {}, children = []) => {
const elm = document.createElement(tag)
assignDeep(elm, props)
elm.append(...children)
return elm
}
that way, I can define a blog post like this:
const div = $c('div', {className: 'blog-post'}, [
$c('h1', {innerText: 'My blog post'}),
$c('div', {className: 'body'}, [
$c('p', {innerText: 'My first blog post'})
]),
$c('div', {className: 'footer'}, [
$c('span', {innerText: 'Author: '}, [
$c('a', {href: '#', innerText: 'atum47'})
]),
$c('span', {innerText: 'Date: ' + new Date().toLocaleString()})
])
]);
Here's an JS Fiddle of that code - https://jsfiddle.net/victorqribeiro/5cketz40/ https://jsfiddle.net/victorqribeiro/5cketz40/
I think this is a simple and elegant way to create HTML elements in JavaScript reducing the verbose of calling document.createElement() and element.append() everywhere. What do you think?
- ssahoo 2y agoCongratulations! You have reinvented Hyper script/jsx.
- breck 2y ago> What do you think? I like your ideas! I think you should go deeper. Have you learned Racket and/or ANTLR before? This screams to me that you are the type of person that should be inventing languages! Come hang out here: https://reddit.com/r/programminglanguages https://reddit.com/r/programminglanguages My user test: https://www.loom.com/share/ca86adff9abc4fb0ae294d32e491636a?sid=4c46f05d-8355-4ec3-bd9f-23a3d8ca8723 https://www.loom.com/share/ca86adff9abc4fb0ae294d32e491636a?...
- atum47 2y agoThis was so nice. I really appreciate it. I've been using this $ notation to simplify the selection and creation of HTML elements (inspired by Jquery) for a while now. My goal was to have a code snippet like the one I shared that would reduce verbose when creating a blog post or a blog comment, you know, data that I fetch from an API. I did fiddle with creating a interpreter in the past, never a programming language. I will look into this. Thank you for taking the time out to make this video, it was awesome.
- dtagames 2y agoLit is an easy way to create HTML with JS. It's a very thin layer over standard web components. https://lit.dev https://lit.dev
- rizky05 2y ago[dead]
- atum47 2y agoI guess most people did not understand what I was trying to achieve with this code snippet; I'm trying, with the less amount of code possible, to create HTML elements dynamically; no React alternatives or frameworks. Anyways, here's my second iteration on the code snippet. const tagNames = [ 'a', 'abbr', 'address', 'area', 'article', 'aside', 'audio', 'b', 'base', 'bdi', 'bdo', 'blockquote', 'body', 'br', 'button', 'canvas', 'caption', 'cite', 'code', 'col', 'colgroup', 'data', 'datalist', 'dd', 'del', 'details', 'dfn', 'dialog', 'div', 'dl', 'dt', 'em', 'embed', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'head', 'header', 'hr', 'html', 'i', 'iframe', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'link', 'main', 'map', 'mark', 'meta', 'meter', 'nav', 'noscript', 'object', 'ol', 'optgroup', 'option', 'output', 'p', 'param', 'picture', 'pre', 'progress', 'q', 'rp', 'rt', 'ruby', 's', 'samp', 'script', 'section', 'select', 'small', 'source', 'span', 'strong', 'style', 'sub', 'summary', 'sup', 'table', 'tbody', 'td', 'template', 'textarea', 'tfoot', 'th', 'thead', 'time', 'title', 'tr', 'track', 'u', 'ul', 'var', 'video', 'wbr' ] const assignDeep = (elm, props) => Object.entries(props).forEach(([key, value]) => typeof value === 'object' ? assignDeep(elm[key], value) : Object.assign(elm, {[key]: value})) tagNames.forEach(tag => window[tag] = function() { const [arg1, arg2] = arguments const props = arg2 ? typeof arg1 == 'object' ? arg1 : null : null let children = props ? arg2 : arg1 if (!Array.isArray(children)) children = [children] const elm = document.createElement(tag) props && assignDeep(elm, props) elm.append(...children.map(c => typeof c == 'string' ? document.createTextNode(c) : c)) return elm }) Now I can use it like this: const blogPost = div({className: 'blog-post'}, [ h1('My blog post'), div({className: 'body'}, p('My first blog post')), div({className: 'footer'}, [ span(['Author: ', a({href: '#'}, 'atum47')]), span(['Date: ', new Date().toLocaleString()]) ]) ]) document.body.append(blogPost) Basically I'm creating a function for each tagName, so instead of calling document.createElement('p') I can just call p(). Other than that I do some argument manipulation to see if the function is being called with props or children
- felipeccastro 2y agoI like it. It’s an elegant solution and you can easily make higher order components from this.
- atum47 2y agook, so, from this ideia came TinyJS and I just released it on GitHub https://github.com/victorqribeiro/TinyJS https://github.com/victorqribeiro/TinyJS
- meiraleal 2y agoyou can improve that using Proxy to make it like: $.h1`My Blog Post`, $.div.body( $.p`My First blog post` ) ... that would be a much better API. Also, there is a very cool new JS framework called TiniJS: https://tinijs.dev/ https://tinijs.dev/