TE
TechEcho
Home24h TopNewestBestAskShowJobs
GitHubTwitter
Home

TechEcho

A tech news platform built with Next.js, providing global tech news and discussions.

GitHubTwitter

Home

HomeNewestBestAskShowJobs

Resources

HackerNews APIOriginal HackerNewsNext.js

© 2025 TechEcho. All rights reserved.

Show HN: A better way of writing HTML via JavaScript?

7 pointsby atum479 months ago
Although I&#x27;ve been using React for at least 5 years now, I don&#x27;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&#x27;s quite neat to be able to write HTML inside of JavaScript code. With that in mind, I&#x27;ve been trying to come up with a way to better create HTML dynamically.<p>Something along the lines of:<p><pre><code> const req = await fetch(&#x27;api.com&#x2F;posts&#x27;) const data = await req.json() for (const d of data) createPost(d) </code></pre> For that, I end up writing this piece of code<p><pre><code> const assignDeep = (elm, props) =&gt; Object.entries(props).forEach(([key, value]) =&gt; typeof value === &#x27;object&#x27; ? assignDeep(elm[key], value) : Object.assign(elm, {[key]: value})) const $c = (tag, props = {}, children = []) =&gt; { const elm = document.createElement(tag) assignDeep(elm, props) elm.append(...children) return elm } </code></pre> that way, I can define a blog post like this:<p><pre><code> const div = $c(&#x27;div&#x27;, {className: &#x27;blog-post&#x27;}, [ $c(&#x27;h1&#x27;, {innerText: &#x27;My blog post&#x27;}), $c(&#x27;div&#x27;, {className: &#x27;body&#x27;}, [ $c(&#x27;p&#x27;, {innerText: &#x27;My first blog post&#x27;}) ]), $c(&#x27;div&#x27;, {className: &#x27;footer&#x27;}, [ $c(&#x27;span&#x27;, {innerText: &#x27;Author: &#x27;}, [ $c(&#x27;a&#x27;, {href: &#x27;#&#x27;, innerText: &#x27;atum47&#x27;}) ]), $c(&#x27;span&#x27;, {innerText: &#x27;Date: &#x27; + new Date().toLocaleString()}) ]) ]); </code></pre> Here&#x27;s an JS Fiddle of that code - <a href="https:&#x2F;&#x2F;jsfiddle.net&#x2F;victorqribeiro&#x2F;5cketz40&#x2F;" rel="nofollow">https:&#x2F;&#x2F;jsfiddle.net&#x2F;victorqribeiro&#x2F;5cketz40&#x2F;</a><p>I think this is a simple and elegant way to create HTML elements in JavaScript reducing the verbose of calling <i>document.createElement()</i> and <i>element.append()</i> everywhere. What do you think?

6 comments

breck9 months ago
&gt; What do you think?<p>I like your ideas! I think you should go deeper. Have you learned Racket and&#x2F;or ANTLR before? This screams to me that you are the type of person that should be inventing languages! Come hang out here: <a href="https:&#x2F;&#x2F;reddit.com&#x2F;r&#x2F;programminglanguages" rel="nofollow">https:&#x2F;&#x2F;reddit.com&#x2F;r&#x2F;programminglanguages</a><p>My user test: <a href="https:&#x2F;&#x2F;www.loom.com&#x2F;share&#x2F;ca86adff9abc4fb0ae294d32e491636a?sid=4c46f05d-8355-4ec3-bd9f-23a3d8ca8723" rel="nofollow">https:&#x2F;&#x2F;www.loom.com&#x2F;share&#x2F;ca86adff9abc4fb0ae294d32e491636a?...</a>
评论 #41451885 未加载
dtagames9 months ago
Lit is an easy way to create HTML with JS. It&#x27;s a very thin layer over standard web components.<p><a href="https:&#x2F;&#x2F;lit.dev" rel="nofollow">https:&#x2F;&#x2F;lit.dev</a>
ssahoo9 months ago
Congratulations! You have reinvented Hyper script&#x2F;jsx.
atum479 months ago
ok, so, from this ideia came TinyJS and I just released it on GitHub <a href="https:&#x2F;&#x2F;github.com&#x2F;victorqribeiro&#x2F;TinyJS">https:&#x2F;&#x2F;github.com&#x2F;victorqribeiro&#x2F;TinyJS</a>
atum479 months ago
I guess most people did not understand what I was trying to achieve with this code snippet; I&#x27;m trying, with the less amount of code possible, to create HTML elements dynamically; no React alternatives or frameworks. Anyways, here&#x27;s my second iteration on the code snippet.<p><pre><code> const tagNames = [ &#x27;a&#x27;, &#x27;abbr&#x27;, &#x27;address&#x27;, &#x27;area&#x27;, &#x27;article&#x27;, &#x27;aside&#x27;, &#x27;audio&#x27;, &#x27;b&#x27;, &#x27;base&#x27;, &#x27;bdi&#x27;, &#x27;bdo&#x27;, &#x27;blockquote&#x27;, &#x27;body&#x27;, &#x27;br&#x27;, &#x27;button&#x27;, &#x27;canvas&#x27;, &#x27;caption&#x27;, &#x27;cite&#x27;, &#x27;code&#x27;, &#x27;col&#x27;, &#x27;colgroup&#x27;, &#x27;data&#x27;, &#x27;datalist&#x27;, &#x27;dd&#x27;, &#x27;del&#x27;, &#x27;details&#x27;, &#x27;dfn&#x27;, &#x27;dialog&#x27;, &#x27;div&#x27;, &#x27;dl&#x27;, &#x27;dt&#x27;, &#x27;em&#x27;, &#x27;embed&#x27;, &#x27;fieldset&#x27;, &#x27;figcaption&#x27;, &#x27;figure&#x27;, &#x27;footer&#x27;, &#x27;form&#x27;, &#x27;h1&#x27;, &#x27;h2&#x27;, &#x27;h3&#x27;, &#x27;h4&#x27;, &#x27;h5&#x27;, &#x27;h6&#x27;, &#x27;head&#x27;, &#x27;header&#x27;, &#x27;hr&#x27;, &#x27;html&#x27;, &#x27;i&#x27;, &#x27;iframe&#x27;, &#x27;img&#x27;, &#x27;input&#x27;, &#x27;ins&#x27;, &#x27;kbd&#x27;, &#x27;label&#x27;, &#x27;legend&#x27;, &#x27;li&#x27;, &#x27;link&#x27;, &#x27;main&#x27;, &#x27;map&#x27;, &#x27;mark&#x27;, &#x27;meta&#x27;, &#x27;meter&#x27;, &#x27;nav&#x27;, &#x27;noscript&#x27;, &#x27;object&#x27;, &#x27;ol&#x27;, &#x27;optgroup&#x27;, &#x27;option&#x27;, &#x27;output&#x27;, &#x27;p&#x27;, &#x27;param&#x27;, &#x27;picture&#x27;, &#x27;pre&#x27;, &#x27;progress&#x27;, &#x27;q&#x27;, &#x27;rp&#x27;, &#x27;rt&#x27;, &#x27;ruby&#x27;, &#x27;s&#x27;, &#x27;samp&#x27;, &#x27;script&#x27;, &#x27;section&#x27;, &#x27;select&#x27;, &#x27;small&#x27;, &#x27;source&#x27;, &#x27;span&#x27;, &#x27;strong&#x27;, &#x27;style&#x27;, &#x27;sub&#x27;, &#x27;summary&#x27;, &#x27;sup&#x27;, &#x27;table&#x27;, &#x27;tbody&#x27;, &#x27;td&#x27;, &#x27;template&#x27;, &#x27;textarea&#x27;, &#x27;tfoot&#x27;, &#x27;th&#x27;, &#x27;thead&#x27;, &#x27;time&#x27;, &#x27;title&#x27;, &#x27;tr&#x27;, &#x27;track&#x27;, &#x27;u&#x27;, &#x27;ul&#x27;, &#x27;var&#x27;, &#x27;video&#x27;, &#x27;wbr&#x27; ] const assignDeep = (elm, props) =&gt; Object.entries(props).forEach(([key, value]) =&gt; typeof value === &#x27;object&#x27; ? assignDeep(elm[key], value) : Object.assign(elm, {[key]: value})) tagNames.forEach(tag =&gt; window[tag] = function() { const [arg1, arg2] = arguments const props = arg2 ? typeof arg1 == &#x27;object&#x27; ? arg1 : null : null let children = props ? arg2 : arg1 if (!Array.isArray(children)) children = [children] const elm = document.createElement(tag) props &amp;&amp; assignDeep(elm, props) elm.append(...children.map(c =&gt; typeof c == &#x27;string&#x27; ? document.createTextNode(c) : c)) return elm }) </code></pre> Now I can use it like this:<p><pre><code> const blogPost = div({className: &#x27;blog-post&#x27;}, [ h1(&#x27;My blog post&#x27;), div({className: &#x27;body&#x27;}, p(&#x27;My first blog post&#x27;)), div({className: &#x27;footer&#x27;}, [ span([&#x27;Author: &#x27;, a({href: &#x27;#&#x27;}, &#x27;atum47&#x27;)]), span([&#x27;Date: &#x27;, new Date().toLocaleString()]) ]) ]) document.body.append(blogPost) </code></pre> Basically I&#x27;m creating a function for each tagName, so instead of calling <i>document.createElement(&#x27;p&#x27;)</i> I can just call <i>p()</i>. Other than that I do some argument manipulation to see if the function is being called with props or children
评论 #41466669 未加载
评论 #41460750 未加载
meiraleal9 months ago
you can improve that using Proxy to make it like:<p><pre><code> $.h1`My Blog Post`, $.div.body( $.p`My First blog post` )</code></pre> ...<p>that would be a much better API.<p>Also, there is a very cool new JS framework called TiniJS: <a href="https:&#x2F;&#x2F;tinijs.dev&#x2F;" rel="nofollow">https:&#x2F;&#x2F;tinijs.dev&#x2F;</a>