Hacker News new | past | comments | ask | show | jobs | submit login
Show HN: A better way of writing HTML via JavaScript?
7 points by atum47 13 days ago | hide | past | favorite | 9 comments
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/

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?






> 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

My user test: https://www.loom.com/share/ca86adff9abc4fb0ae294d32e491636a?...


This 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.

Lit is an easy way to create HTML with JS. It's a very thin layer over standard web components.

https://lit.dev


Congratulations! You have reinvented Hyper script/jsx.

ok, so, from this ideia came TinyJS and I just released it on GitHub https://github.com/victorqribeiro/TinyJS

I 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

Everybody understood what you asked chatgpt to generate, Victor. We just don't find it good or new. Btw you should check for JS Proxies, it can improve a lot your code.

I will repeat someone else's advice and recommend lit-html which is on its way to become part of the browser spec.


I like it. It’s an elegant solution and you can easily make higher order components from this.

you 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/




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: