
What if we'd had better HTML-in-JS syntax all along? - zdw
https://leontrolski.github.io/dom-syntax.html
======
masswerk
> _I have a theory that a grave mistake was made in 1995 - the decision not to
> have a neat, succinct and declarative way of representing html elements in
> javascript._

It may be interesting to recall that initially the only way to output HTML was
by `document.write()` using strings (which was pretty declarative).

The genuine way of generating HTML in JS were various methods of
`String.prototype` for generating HTML formatted output, as `anchor()`,
`big()`, `blink()`, `bold()`, `fixed()` (providing "<TT>…</TT>"), `italics()`,
`link()`, `small()`, `strike()`, `sub()`, `sup()`. However, there was no
provision to set an ID or any other attributes or className, since these
things (and CSS) hadn't been introduced yet, with the notable exception of the
"name" attribute for anchor tags, the value of which was to be supplied as an
argument to the `string.anchor()` method. This was pretty much sufficient to
generate any of the HTML required then.

The first occasion for a more complex approach was the introduction of the
`Link` and `Image` objects, which allowed for the first time not only a
representation of a DOM object, but also means to manipulate their attributes
on the fly (e.g., changing image sources), but this came only with Netscape
Navigator 3.0 / JavaScript1.1.

Also, keep in mind that the JS object notation (object and array literals) was
only introduced in the ECMA standard, which came another browser iteration
later. Initially, in JavaScript1.0 (AKA LiveScript), there hadn't been even
Array or Object constructors in the core language (these came, again, with
JavaScript1.1), but only functions. (Meaning, you had to construct your own
arrays from generic functions). Hence, something like a declarative HTML-in-JS
syntax would have been totally out of place.

~~~
TedDoesntTalk
There was also the the idea that JavaScript was just one way of scripting the
page. VisualBasic Script (VBScript) had momentum in IE releases. The general
idea was that the user-agent was going to be able to parse multiple scripting
languages, the list of which would probably grow as time went on. Think native
TypeScript parsing in a browser today!

~~~
pjmlp
ActiveState had browser plugins for Perl, Python and Tcl. I think they were IE
only via COM extensions.

~~~
blacksqr
Netscape had a plugin API and there was a Tcl plugin for it. As I recall the
Netscape API was the de facto standard for as long as plugins were a thing, so
the Netscape plugin was usable in conforming browsers.

------
adambard
The "object" syntax reminds me a lot of Clojure/script's Hiccup
([https://github.com/weavejester/hiccup](https://github.com/weavejester/hiccup))
style, which has become a de-facto standard across many popular libraries. In
hiccup, this example:

    
    
        {
            tag: "button",
            attributes: {
                "id": "baz",
                "class": "foo bar",
                "data": "1",
                "onclick": f,
            },
            children: [
                "Hello",
                {
                    tag: "em",
                    attributes: {},
                    children: ["there"],
                }
            ]
        }
    

Would be represented as:

    
    
        [:button#baz.foo.bar {:data 1 :on-click f} "Hello" [:em "there"]]

~~~
austincheney
That looks identical to WC3 XML Schema language using JSON syntax instead of
XML syntax.

~~~
quietbritishjim
I think you've missed that the hiccup example is the code on the final line.

------
coding123
I think my favorite thing about JSX specifically - is that the {} breakouts
used in it, get all the same treatment from the JS/TS compiler as your
Javascript/Typescript does. And all without changing HTML into a JSON or Yaml
representation. It just works in VSCode. The number of years of HTML being an
relative of the SGML/XML family (pedants calm here, you know what I mean)
shouldn't be thrown away just because it's not immediately available in JS.

I think one thing I would love to see from JSX is for it to start growing
outside of the React world. Vue seems to be trying. Until React came along, we
really had no compile time checking on templating languages at all. I think
the reason for that, is, in part, because the templating aspect was always
thought of as "dirty" that you shouldn't mix JS and the view parts. I think
React debunked that notion. I've never been as productive in creating UIs as I
have when I'm working in React. And a lot of that is because I call my
backend, I get a typed object back, I tell the UI to use
<div>{response.medication.firstName}</div> and if we ever change the structure
of medication? I get full help from the compiler that that view will no longer
work... If you haven't used React you might not get the full power of that.

Even if I am a React fan, it doesn't mean I don't think other frameworks won't
ever take over, but I think at this point, the one that may eventually eclipse
React is probably going to have native JSX support.

~~~
fragmede
Yeah, agreed. React and JSX really shines with a good component library, and
the key word with react is _composable_. I don't know that, by itself, better
syntax for HTML-in-JS would advance the state of the art. Instead of having
divs and spans everywhere, they're an implementation detail of components.

I can change

    
    
        <leftPanel>
            <userInfo>
        </leftPanel>
        <rightPanel>
            <medicationsPanel>
        </rightPanel>
    

to

    
    
        <topBar>
            <userInfo>
        </topBar>
        <leftPanel>
            <medicationsPanel>
        </leftPanel>
    

Without having to know what html+css tricks topBar is doing under the hood.

Inside of medicationsPanel, there's <medicationsList> which is the `ul`.

    
    
        <ul>
            {medications.map(m => {
                <medicationListItem item=m />
            })}
        </ul>
    

medicationListItem has the `<li>`, with the styling living somewhere inside.
Templates, especially in separate files, don't really manage to be composable
with the same ease. Leaving the divs and spans and CSS to the lowest levels of
the component library does wonders for making the code easier to reason about,
and maintain. React code from a few years ago still has some warts, but it's
still way less painful to try and pick up than code that was written with
traditional templating. (Specifically, the templated project was using `.erb`
files.)

React threw out MVC and templates (though some say it's just the _V_ instead),
and is worth learning just to feel like it's possible to implement UIs
cleanly.

~~~
thayne
Webcomponents should help with this. Although the current API is not as
pleasant to use as react.

~~~
dmitriid
They can't and they won't. Because they rely on DOM APIs to do anything
useful. Which brings us back to the article.

~~~
thayne
What does that have to do with having composable components?

~~~
dmitriid
Everything.

The only way the vanilla webcomponents are "composable" is through tons of
boilerplate code using DOM APIs. You can't even re-create the example with
list elements without tearing your hair out when using webcomponents.

It's so bad that any examples showing "composability" immediately give up and
use .innerHtml everywhere, and even then it's pretty bad.

It's so bad, in fact, that people do anything to avoid writing vanilla and use
lit-html (yay, programming with strings), Stencil, or even Preact (AMP
switched to Preact to author components).

------
neilv
An early alternative would've been to use an integrated language for both HTML
and JS features, like Curl, published in 1997:

[https://en.wikipedia.org/wiki/Curl_(programming_language)](https://en.wikipedia.org/wiki/Curl_\(programming_language\))

[https://dl.acm.org/doi/10.1504/IJWET.2003.003259](https://dl.acm.org/doi/10.1504/IJWET.2003.003259)

[https://groups.csail.mit.edu/cag/curl/wwwpaper.html](https://groups.csail.mit.edu/cag/curl/wwwpaper.html)

(Disclosure: I worked at the Curl commercial spinoff.)

Or, many Lisp people just used s-expression data for HTML, and Lisp for
everything else. Here's a typical way I did, in 2000, in Scheme:

[https://www.neilvandyke.org/racket/html-
writing/](https://www.neilvandyke.org/racket/html-writing/)

And a less-common later take on it, which used syntax extension rather than
data:

[https://www.neilvandyke.org/racket/html-
template/](https://www.neilvandyke.org/racket/html-template/)

I actually pitched some Web ideas to Tim Berners-Lee around 2000, including
Scheme for Web programming. But shortly after that, he promoted using Python
for some Web stuff, because, IIRC, Python was inclusive, making Web
programming accessible to more people. Which was understandable, and one of
the ways many techies thought about the Internet, before there were career
tracks, when the Internet was something we knew about and wanted to bring to
the world. (In hindsight, I wish I'd then tried to write a very inclusive
practical intro book/tutorial for Scheme at that early point, but I was
preoccupied with other things.)

~~~
mlwiese
The modern equivalent is Hiccup and Clojure.
[https://github.com/weavejester/hiccup/wiki/Syntax](https://github.com/weavejester/hiccup/wiki/Syntax)

Clojure has literal lists using parens and vectors using square brackets. By
using both you can mix code and markup in a readable way. It's great!

------
db48x
Don't forget E4X: [https://developer.mozilla.org/en-
US/docs/Archive/Web/E4X_tut...](https://developer.mozilla.org/en-
US/docs/Archive/Web/E4X_tutorial/Introduction)

It never really caught on.

~~~
leovander
We use it in healthcare, at least in NextGen Connect, formerly Mirth
Connect[0]. It uses Rhino[1] as its scripting engine which still supports E4X.

[0]
[https://en.wikipedia.org/wiki/NextGen_Connect](https://en.wikipedia.org/wiki/NextGen_Connect)

[1] [https://developer.mozilla.org/en-
US/docs/Mozilla/Projects/Rh...](https://developer.mozilla.org/en-
US/docs/Mozilla/Projects/Rhino)

~~~
db48x
That is really cool. I had no idea anyone was still using it.

------
glutamate
Something close to this is possible already in JS, I'm working on a library
for generating HTML in node, heavily inspired by Haskell's blaze and lucid.
Example code:

    
    
      div(
          { class: "table-responsive" },
          table(
            { class: ["table table-sm", opts.onRowSelect && "table-hover"] },
            thead(tr(hdrs.map(hdr => headerCell(hdr)))),
            tbody(
              vs.map(v =>
                tr(
                  mkClickHandler(opts, v),
                  hdrs.map(hdr =>
                    td(typeof hdr.key === "string" ? text(v[hdr.key]) : hdr.key(v))
                  )
                )
              )
            )
          )
    
    

The first argument to a tag is optionally an object with the attributes, any
subsequent arguments are children of the tag, or an array with the children.

EDIT:

the library as a gist:
[https://gist.github.com/glutamate/69d38fb1d2024cb829edfff57d...](https://gist.github.com/glutamate/69d38fb1d2024cb829edfff57da9b162)

~~~
applecrazy
This actually already exists as a library. This type of syntax is called
Hyperscript[1], and it's existed (at least) since 2012.

[1]:
[https://github.com/hyperhype/hyperscript](https://github.com/hyperhype/hyperscript)
(Although this library does not look maintained anymore)

~~~
glutamate
Cool! I was looking for something like this but couldn't find it. Oh well,
building my own kept me sane during lockdown.

I think combinator libraries like this and one I'm building are definitely
better than templating languages, although JSX can also work really well, in
particular because it retains the HTML syntax making it easy to copy from the
browser into code.

~~~
wilsonfiifi
You can also take a look at mithril.js implementation of hyperscript [0]

    
    
      [0] https://github.com/MithrilJS/mithril.js/blob/next/render/hyperscript.js

~~~
holtalanm
was about to mention Mithril.

solid framework, but not great for large applications.

~~~
brlewis
What issues have you encountered that make it not great for large apps?

~~~
holtalanm
it has been a few years, so maybe these issues have been addressed at some
point since I used it last, but:

1) there were multiple issues with deeply nested object models not being
reactive within the UI code that cause me to explicitly call m.refresh (can't
remember the exact api, as it has been a while, but the function that was
called forced a virtual dom refresh, which was _not_ good for performance). I
ended up writing a debounce function that would make sure the m.refresh
function only got called once per paint at the most, which helped, but it was
just a band-aid over the underlying problem, which was that the framework was
not detecting reactive property changes within deeply nested models. At the
time I was using it, there was no other remedy according to issues that had
been brought up on stack overflow.

2) this is probably personal opinion, but the hyperscript model for
structuring html, while it looks good on paper, lends to a really tedious
process of writing html-generating javascript.

I guess I didn't need a numbered list. Those were my main two gripes.

~~~
brlewis
For (1), m.redraw() is async; I don't think you have to debounce anymore. The
autoredraw system may be more sophisticated than when you used it, too.
[https://mithril.js.org/autoredraw.html](https://mithril.js.org/autoredraw.html)

For (2), I've been using JSX with Mithril the whole time. My team and I didn't
like deeply nested hyperscript either. (At my employment we're in the process
of moving away from Mithril, but I'm still using Mithril 2 in a side project)

------
devxpy
I think flutter puts a nail in the coffin with its semi-declarative syntax.

While you're technically programming using an imperative language, they've
somehow managed to make the syntax close enough to a declarative one that it
isn't just a soup of bare object surgery (like tkinter or java swing).

They also, really went the extra mile to NOT have to do tree diffing[1], by
making Widgets immutable, while still having a "detached" State associated
with them.

And this is eerily close to the JSON-is-HTML syntax the author is proposing. A
replication of flutter's strategy in pure JS is defininitely worth exploring.

[1] [https://flutter.dev/docs/resources/inside-flutter#linear-
rec...](https://flutter.dev/docs/resources/inside-flutter#linear-
reconciliation)

------
kroltan
This reminds me of a very cool property of the Lua language that allows for
very neat "syntax extensions", and reminds of functional languages too:

If you have two expressions adjacent to eachother, it evaluates to calling the
left-hand side with the right-hand side as a parameter.

At its simplest:

    
    
      function foo(text)
        print("Foo:", text)
      end
    
      foo "bar"
    

But you could implement a JSX-lite in it:

    
    
      function Div(props)
        return function(children)
          return ViewLibrary.createElement("div", props or {}, children or {})
        end
      end
    

Then, you use it like this:

    
    
      Div{id="outer"} {
        Div{id="innerA"} (),
        Div{id="innerB"} {
          Div()()
        }
      }
    

No compiler extensions needed! And with a bit of library sugar, you could do
away with the empty parameter lists too (calling any unresolved functions with
empty parameters), but this would lengthen the demo a bit so I omitted it.

~~~
kroltan
In fact, since objects and arrays are one and the same in Lua, one can even do
away with the nested calls, which also simplifies things visually:

    
    
      Div {
        id = "outer",
        Div {id = "innerA"},
        Div {
          id = "innerB",
          Div {}
        }
      }
    

This works because when you omit the key (id=) of a table item in Lua, it is
assumed to have a incrementing numeric key.

~~~
goatlover
That's very neat. In the pure prototyping language IO, since the syntax was
defined as messages to objects, you could define your own syntax to support
xml or html directly in IO.

I wonder if Netscape hadn't collaborated with Sun if JS would have had a more
pure implementation of prototypes, and how those might have been leveraged
more minus all the confusion from mixing in Java-like syntax on top of JS's
object model.

~~~
kroltan
I don't have the sources right now, but I once watched (not attended!) a
presentation that talked about how JS was initially intended to be a language
more like Lisp or Smalltalk, but was changed into an OO language to appease
the heavy marketing at the time.

~~~
kazinator
The duo of Lisp and Smalltalk basically define what OO _means_.

Javascript simulates some of the aspects of OOP in an _ad hoc_ way by turning
every object into a hash table so that properties can be tacked onto it.

It's what you implement if it's Wednesday, and the boss asks for a some sort
of working OOP system by Friday.

~~~
kroltan
You're right I was half asleep when I wrote the previous comment, I am very
sorry. I meant a C(Java?)-like language. Which I am aware has nothing to do
with OO, just syntax style.

------
flowerlad
I think the JSX is just fine as an HTML-in-JS syntax. You can use JSX
independently of React. Here's a 200-line library that lets you use JSX as a
templating language just like Handlebars:
[https://github.com/wisercoder/uibuilder](https://github.com/wisercoder/uibuilder)

~~~
Scarbutt
What's the issue with web components that they have never took off?

~~~
FlorianRappl
Besides the poor browser support (supporting IE11 is still quite a pain) the
API is just so bad that you'll need another abstraction on top anyway.

The main use right now is as target for framework independent design systems /
component libraries.

~~~
flowerlad
Here's an example of a Web Component:
[https://github.com/wisercoder/uibuilder/blob/master/WebCompo...](https://github.com/wisercoder/uibuilder/blob/master/WebComponentsDemo/elements/ZxListEditor.tsx)

Looks pretty straightforward, and there isn't much of an API to begin with.
It's just plain HTML, CSS, JavaScript and DOM APIs.

~~~
dmitriid
> It's just plain HTML, CSS, JavaScript and DOM APIs.

Exactly. The entire history of web development has been about finding good
abstractions around the horrendous DOM APIs.

And that's _before_ you start talking about the need to roll out your own
solutions like state management for WebComponents.

------
kgwxd
I've never had a chance to use it in production but, ClojureScript with
Reagent[1] and it's Hiccup-like[2] markup is bliss in this domain.

[1] [http://reagent-project.github.io/](http://reagent-project.github.io/) [2]
[https://github.com/weavejester/hiccup](https://github.com/weavejester/hiccup)

~~~
rafd
...and, there's nothing particularly preventing a similar approach in JS
(keywords would need to be strings, and... data manipulation in JS is
relatively a huge pain compared to Clojure)

------
andrewla
An interesting native approach; there have been library-based approaches to
this that date back a while.

Laconic
([https://github.com/joestelmach/laconic](https://github.com/joestelmach/laconic))
is one of the oldest that I'm aware of, and suffers from some jsquery-ness:

    
    
        $.el.div({'class' : 'example'}, 
          $.el.div('content'));
    
    

Pithy ([https://github.com/caolan/pithy](https://github.com/caolan/pithy)) is
another; uses function syntax to get the effect of the tag name. This was
discussed previously on hn,
[https://news.ycombinator.com/item?id=5486239](https://news.ycombinator.com/item?id=5486239)

    
    
        html.div('#main', [
          html.h1(null, 'Hello, world!'),
          html.img({src: 'foo.jpg'})
        ]);
    

In that discussion, there are various other implementations of similar ideas -
[https://github.com/markgandolfo/el.js](https://github.com/markgandolfo/el.js)
and [https://github.com/insin/DOMBuilder](https://github.com/insin/DOMBuilder)
and [https://github.com/jed/domo](https://github.com/jed/domo) and so on.
el.js came to my attention a long time ago, and I ended up writing a clone
that I have used in my personal projects since the library almost writes
itself once you steal the basic idea.

Using function application is the easiest way to get the effect of TFA's
syntax without the cumbersome overloading of braces.

~~~
insin
How far back can we go? insin/DOMBuilder was initially based on a 2006 library
of the same - but differently-cased - name by Dan Webb
([https://www.webstandards.org/2006/04/13/dom-
builder](https://www.webstandards.org/2006/04/13/dom-builder)) and the
earliest variant I can recall was Mochikit.DOM
([https://mochi.github.io/mochikit/doc/html/MochiKit/DOM.html](https://mochi.github.io/mochikit/doc/html/MochiKit/DOM.html))

By the end of DOMBuilder's life it could generate DOM or HTML from the same
code and I'd tried unsuccessfully to implement a way to rehydrate HTML by
hooking up events using the same code on the client.

Not long after, I happened to be in the audience for Pete Hunt's React:
Rethinking best practices talk at JSConf EU
([https://www.youtube.com/watch?v=x7cQ3mrcKaY](https://www.youtube.com/watch?v=x7cQ3mrcKaY))
and I was totally floored; they'd solved everything I had been trying to do
with UI as code and much more, and JSX was the icing on the cake, as using
nested object/array syntax has always been rife with comma-management hell
when you're maintaining UI code.

------
lhorie
Suspiciously absent, but right up this alley is hyperscript:

    
    
        h('div', {id: 'foo', onclick: f}, 'Hello')
    

The benefits of hyperscript over `div()` is that in JS, variable names must be
imported by name (so `import {div, ul, li, ...}` gets annoying fast). Also,
React is super popular, and hyperscript interoperates nicely with JSX.

~~~
pickle-ts
FYI in typescript with VS you just hit TAB to auto-complete an import.

------
sergeykish
What if we had better JS in HTML?

    
    
        <form name="comment" onsubmit="event.preventDefault(); output.textContent = email.value">
          <input name="email">
          <input type="submit">
          <output name="output">
        </form>
    

What if we made function and it was called with target as `this`?

    
    
        <form onsubmit="commentOnSubmit(this)">...
        <script>
          function commentOnSubmit(comment) {
            event.preventDefault()
            comment.output.textContent = comment.email.value
          }
    

I've heard it's possible now with React

    
    
        const email = React.useRef(null)
        return (
          <form onSubmit={this.handleSubmit}>
            <input ref={email} />

------
thramp
I like the retrospective of this post! The "what ifs" that could've been taken
are fun to think about.

I think I might take a step further than this post does—I think some sort of
Racket/Rust-style macro system should be implemented in JS, which means that
that means something like JSX could be implemented as a library. There's
already been _some_ work on this in JavaScript (SweetJS
[https://www.sweetjs.org/](https://www.sweetjs.org/)), but making it first-
class would be a big win, IMO.

I know that there are common objects to macros as being too complicated/having
poor misuse resistence, but it can (and has!) been implemented and used pretty
judiciously all the non-C/C++ languages I've used.

------
toohotatopic
>What if we'd always had something like this in js?

What if html would use round brackets and just use the closing bracket instead
of the closing tag?

e.g. <html><body><div>foo</div></body></html>

could be:

(html (body (div 'foo')))

Something similar could be done for javascript. Instead of writing html in
javascript we could write javascript in html. Just imagine if we could
represent the code as html or in that round bracket form:

function inc (x){ return x + 1; }

could be:

<function name="inc" param="x"><add>x 1</add></function>

or with round brackets:

(function inc (x) (+ x 1))

~~~
krebs_liebhaber
Congratulations - you've invented S-Expressions! Fortunately for you, there's
a whole family of programming languages[0] that are based solely on this
syntax!

If you want to see a mature library that uses this syntax to represent both
DOM elements and code, I'd suggest checking out Reagent[1].

[0]
[https://en.wikipedia.org/wiki/Lisp_(programming_language)](https://en.wikipedia.org/wiki/Lisp_\(programming_language\))

[1] [https://reagent-project.github.io/](https://reagent-project.github.io/)

------
aylmao
They are proposing the syntax:

    
    
        button{id: 'baz' class: ['foo' 'bar'] data: '1' onclick: f
            'Hello '
            em{'there'}
        }
    

which is of course not possible to use at the moment, but thought I'd convert
it to the closest thing that's possible right now:

    
    
        button({id: 'baz' class: ['foo' 'bar'] data: '1' onclick: f},
            'Hello ',
            em('there')
        )
    
    

Not too far!

------
pickle-ts
In solenya it's very terse - you can use the HTML to JS converter to see the
translation is pretty straightforward:
[https://www.solenya.org/convert](https://www.solenya.org/convert)

Example code:

    
    
        div ({ class: "form-group" },   
          label ({ for: "exampleFormControlSelect1" }, "Example select"),  
          select ({ class: "form-control", id: "exampleFormControlSelect1" },   
            option ("1"),  
            option ("2"),  
            option ("3"),  
            option ("4"),  
            option ("5")  
          )  
        )  
    

In conjunction with typestyle, you never have to leave js/ts. Very clean.

The arguments against this approach usually boil down to the mistaken notion
that "you're not separating concerns". The response is: separation of language
!= separation of concerns. No matter what you should still strive to reduce
cyclomatic complexity. So you're still separating model, view, themes etc. And
you're doing so using abstractions within the language, rather than relying on
language and file boundaries. Notably in the reverse case, when you have
unavoidable links between these layers (since no useful abstraction isn't
leaky), the benefits of expressing everything in one language are considerable
(e.g. when refactoring).

------
BenoitEssiambre
The section on templates should mention lit-html, the modern way of doing
this: [https://lit-html.polymer-project.org/](https://lit-html.polymer-
project.org/)

------
one-punch
Reminds me of the “Shakespearean Templates“ [1] from the Yesod web framework
[2].

[1]: [https://www.oreilly.com/library/view/developing-web-
applicat...](https://www.oreilly.com/library/view/developing-web-
applications/9781449336868/ch04.html)

[2]: [https://www.yesodweb.com/](https://www.yesodweb.com/)

Copied below to save you a click:

Hamlet (HTML)

    
    
        $doctype 5
        <html>
            <head>
                <title>#{pageTitle} - My Site
                <link rel=stylesheet href=@{Stylesheet}>
            <body>
                <h1 .page-title>#{pageTitle}
                <p>Here is a list of your friends:
                $if null friends
                    <p>Sorry, I lied, you don't have any friends.
                $else
                    <ul>
                        $forall Friend name age <- friends
                            <li>#{name} (#{age} years old)
                <footer>^{copyright}
    

Cassius (CSS)

    
    
        #myid
            color: #{red}
            font-size: #{bodyFontSize}
        foo bar baz
            background-image: url(@{MyBackgroundR})
    

Lucius (CSS)

    
    
        section.blog {
            padding: 1em;
            border: 1px solid #000;
            h1 {
                color: #{headingColor};
            }
        }
    

Julius (JavaScript)

    
    
        $(function(){
            $("section.#{sectionClass}").hide();
            $("#mybutton").click(function(){document.location = "@{SomeRouteR}";});
            ^{addBling}
        });
    

For those curious, the above is implemented in the metaprogramming framework
of Template Haskell.

~~~
eyelidlessness
Does Hamlet produce structured data, or a string?

~~~
one-punch
It produces structured data (the `MarkupM` datatype from `blaze` to be precise
[1]).

To check this, note that Hamlet produces `Html` [2], which (when following the
link) is defined to be `Markup` [3], which is in turn `MarkupM` [4].

[1]: [https://hackage.haskell.org/package/blaze-
markup-0.8.2.3/doc...](https://hackage.haskell.org/package/blaze-
markup-0.8.2.3/docs/Text-Blaze-Internal.html#t:MarkupM)

[2]:
[https://hackage.haskell.org/package/shakespeare-2.0.24/docs/...](https://hackage.haskell.org/package/shakespeare-2.0.24/docs/Text-
Hamlet.html#g:2)

[3]:
[https://hackage.haskell.org/package/shakespeare-2.0.24/docs/...](https://hackage.haskell.org/package/shakespeare-2.0.24/docs/Text-
Hamlet.html#t:Html)

[4]: [https://hackage.haskell.org/package/blaze-
markup-0.8.2.3/doc...](https://hackage.haskell.org/package/blaze-
markup-0.8.2.3/docs/Text-Blaze-Internal.html#t:Markup)

------
lisper
This sort of thing is easy in Lisp:

[https://edicl.github.io/cl-who/](https://edicl.github.io/cl-who/)

~~~
bjoli
Or sxml/ssax in scheme.

------
anonytrary
> I think it's a fine enough solution, but the fact that it's a different
> syntax from your standard js objects encourages people to consider the VDOM
> objects as "not normal data", but they are. Notation as a tool of thought
> innit.

This is the author's only commentary on JSX and IMO it's not enough of a
reason to completely ditch JSX. Most people who work with JSX in any
reasonable capacity understand that they get transpiled to hyperscript-flavor
calls (and ultimately POJOs). I'm not sure why we need to reinvent this wheel
in TypeScript as hyperscript already addresses this problem in native JS.

------
coronasearch
Anyone here worked with Mithril before? It uses that object representation of
HTML in JS that this post is suggesting,
[https://mithril.js.org/](https://mithril.js.org/)

------
jdmichal
I have a hypothesis about a different way to organize responsibilities within
webpages. I think the split of "content" to HTML and "presentation" to CSS was
a mistake. To me, those are both describing content. That is, the author's
intent to emphasize a particular part of text, whether via <em> tags or CSS,
is part of the content.

I think there was a missed opportunity to instead split page layout into its
own technology. This would allow for complex layouts, up to and including
constraint solving. The browser would calculate the layout, then load each of
the content segments into the calculated layout. I imagine it something like
ASP.NET master pages, where you can define a page structure with fixed
placeholders that can be replaced with other content. Except, of course, this
could only specify where on the screen a particular panel exists.

HTML and CSS could then live together in a single technology. I think you
could even go a step farther and combine content, presentation, and scripting
all into a single technology. Some operators within that technology would have
the side-effect of manifesting things on the user's screen. Something based on
s-expressions would probably work very well, with its ability to blend code
and data into a single format.

I imagine I could probably do some kind of mock-up using iframes to at least
demo the idea...

~~~
rimliu
It was not a mistake. The theory being that HTML describes the structure, and
CSS describes the presentation. Do not forget, that there are other ways for
the page to be rendered: e.g. in screen reader or for print. We had "HTML and
CSS living together" with FONT and BGCOLOR and friends. It was not nice. Sad
to see people wanting to get it back. We are finally comming to realize that
maybe rendering HTML on the server is not so bad after all, when will we
realize, that maybe the idea of separating concerns was not so bad after all.
And most importantly, when we finally start to learn the technology before
trying to "improve" it. It looks like HTML and CSS is somehow considered not
worthy learning and understanding, something do be disgusted about. Hating CSS
for me sound like the lame bragging "I do not read book" or as the as lame
jokes about PHP.

~~~
jdmichal
You're grinding an axe that has little to do with what I said.

Your defined split of "structure" and "presentation" is orthogonal to my
choice of "layout" and "content". Currently both HTML and CSS define layout
and content. Layout being defined as the positioning and arrangement of panels
within the viewport, and content being defined as the material displayed
within those panels _as the author intended_. That emphasized part is
important.

The issue with styling in HTML had nothing to do with the use of tags and
everything to do with the fact that they could not be classed. So the chosen
tags had to be duplicated in entirety for every instance, which was a
maintenance nightmare. Later versions of CSS saw it become more powerful than
the original HTML tags. (Aside: This makes it ironic that it seems to be
becoming idiomatic to inline styles for HTML templating. Templating solves
code duplication, so it no longer requires classing.)

But the HTML+CSS tools for layout are extremely primitive. There's lots of
things to point at here. One simple one is that HTML often has superfluous
structure that is required only for the CSS-defined layout to work. A more
complex one is that it difficult for me to say I want the footer either at the
bottom of the viewport or at the bottom of the content, whichever is lower.

------
_hardwaregeek
I've thought that Ruby's syntax would map nicely to HTML elements:

    
    
       div class: "language-javascript" do 
         a href: "index.html" { img style: "height:2em", src: "pic.png"; "⇦"}
         h1 {"What if we'd had better " + code{"html"} + "-in-" code{"js"} syntax all along?"}
         p{"I have a theory that a grave mistake was made in 1995 ..."}
       end

------
tolmasky
_> What about .jsx though?

I think it's a fine enough solution, but the fact that it's a different syntax
from your standard js objects encourages people to consider the VDOM objects
as "not normal data", but they are. Notation as a tool of thought innit._

This is incidental. JSX can be extended beyond just representing HTML nodes,
and IMO has far cooler consequences as first class _.bind()_ notation vs. its
current use as an alternate _call_ notation (which already has a notation of
course in the form of appending parenthesis to an identifier). Namely, you can
begin to curry JSX nodes. For the purposes of discussing HTML specifically,
you can do things like:

    
    
        const myPre = <pre style = "color:red">;
    
        <myPre>hi</myPre>
    

You can actually then take this and end up with a more powerful object
notation too (especially for representing tree-like data). I discuss this
extensively in my blog post here:
[http://tolmasky.com/2016/03/24/generalizing-
jsx/](http://tolmasky.com/2016/03/24/generalizing-jsx/)

------
erpellan
The object syntax example looks very like what is already possible in Kotlin:
[https://github.com/Kotlin/kotlinx.html](https://github.com/Kotlin/kotlinx.html)

    
    
      html {
        body {
            div {
                a("https://kotlinlang.org") {
                    target = ATarget.blank
                    +"Main site"
                }
            }
        }
      }

------
haburka
The solution is not another templating language. Html is complex and many
templating languages exist because it’s hard to compress and everything thinks
it’s not that hard. There are so many things you need to do in HTML to make a
modern web page that this suggestion conveniently ignores. The web is complex
because it has replaced native programs and it’s perfectly backwards
compatible. Stop trying to make it look easy.

~~~
wtetzner
> There are so many things you need to do in HTML to make a modern web page
> that this suggestion conveniently ignores.

Any examples of things that aren't handled by the suggested syntax?

~~~
Tade0
Comment nodes, multiple text nodes in one element node, CDATA section.

~~~
wtetzner
Are there any examples of where you'd need that in a web application that's
just being used to update the DOM?

None of those seem to be necessary to create a modern web application.

I'm not arguing, just trying to understand.

~~~
Tade0
If you have a rich text editor or inline SVG there, you're bound to have to
deal with them eventually.

~~~
wtetzner
Why? I'm really trying to understand. If you're directly modifying the DOM,
why would you need comments or CDATA tags? Is CDATA useful in a DOM if you
don't actually need a textual representation?

------
enjikaka
In Webact I did a small tagged template literal with createContextualFragment.
Very smooth.

[https://github.com/enjikaka/webact/blob/master/src/helpers.j...](https://github.com/enjikaka/webact/blob/master/src/helpers.js#L75)
[https://github.com/enjikaka/webact/blob/master/src/helpers.j...](https://github.com/enjikaka/webact/blob/master/src/helpers.js#L46)

    
    
      html`<b>Hello world</b>`
    
    

returns a document fragment that you can just appendChild with. Super simple.

------
ChrisSD
Are we now reinventing PHP but coming from the opposite direction?

~~~
Joeri
No, this is something different because it understands html.

Php has no understanding of html. It streams the php file as bytes, and
anywhere it detects the byte sequence that corresponds to “<?php” it starts
executing code instead of streaming until it reaches “?>”. You can stream
anything. You can rename a png file to .php and put some php tags in there and
it will work. You need something that understands html’s structure to properly
escape. Php can’t do it natively, hence the many templating libraries for php.

------
moron4hire
I get a lot of mileage out of this setup:
[https://github.com/capnmidnight/Calla/blob/master/scripts/ht...](https://github.com/capnmidnight/Calla/blob/master/scripts/html.js)

------
freefriedrice
I have a theory that hindsight isn't even 20/20 when it comes to the web.
Hindsight is still 20/400 because we STILL do not know what is the best
language & framework. Frontends evolved quarterly with new paradigms popping
up at a breakneck pace.

There is a reason why we have knockout, ts, angular, react, vue, and even
people touting their own NEW frameworks in this very thread: we still don't
know WTF we are doing!

I don't think it is a bad thing, I think we're in the Cambrian Explosion of
web development, and will be for decades until the hardware slows down its
rapid evolution.

I was there when Mosaic first appeared in /usr/bin during grad school. We had
no clue where things were headed then, so I find it a bit harsh to diss on the
first devs.

------
pier25
I've been thinking about this for some time now and I totally agree. Maybe not
with the implementation but with the idea that we need a better native way of
working with markup.

> _It would be constantly staring us in the face that our html is just
> structured data_

Not only html, but css too.

------
Mikhail_Edoshin
JS is a procedural language. The somewhat forgotten procedural pattern for
constructing these things in procedural languages is to use a canvas::

    
    
        xmlCtx = MkXmlCtx()
        SetNsPfx(xmlCtx, "foo", "http://foo.com/ns")
        OpenTag(xmlCtx, "foo:bar")
        SetAttr(xmlCtx, "name", "value")
        Write(xmlCtx, "string")
        CloseTag(xmlCtx)
        xml = GetXml(xmlCtx)
    

It may look boring but it's exactly the right thing. Syntactic sugar is
deceptive. A declarative approach is not the right pattern in JS. (It's
essential to XML, but XML is a very different thing.)

------
spankalee
This article has a number of problems: it doesn't take into account the
primary reason for approaches like VDOM, the history of HTML-in-JS proposals,
or the current state-of-the-art in HTML-in-JS.

First, the word "update" doesn't appear in the article at all, and efficient
and stable updates are the primary reason that client-side template systems
are much more complex than their server-side string-concatenation
counterparts. Updates are the main reason why you want to treat HTML as
structured, because you can't map data changes to DOM changes without some
knowledge of the underlying DOM structure.

Then there's no mention of E4X (or the lesser-known E4H by Ian Hixie). We've
had a proposed syntax, so what went wrong and why not revive it?

Again, updates. Creating the initial DOM state isn't that bad. E4X is an
incremental improvement over innerHTML. What matters is updates, and the main
difference between JSX and E4X is that E4X created _new_ DOM for every
invocation, while JSX is typically used to create a new _description_ of the
DOM, which is applied to existing DOM to update it.

Any proposal for adding HTML syntax to JS is completely jumping the gun if it
doesn't address this point. The syntax matters way less than the semantics on
what such markup expressions actually evaluate to and how that result is used
to update the DOM.

THen, statements like this:

> This is rubbish for obvious reasons - composing the strings is bug-prone, no
> typing/linting etc etc. We then have to turn them into elements with the
> less than elegant:

don't hold up.

lit-html[1] (I'm a maintainer) works very much like this and it excels at
composition and turning markup into structured data. The complaint is very
much in the vein of complaining about embedding a language into strings and
saying that just programming with strings. Well, basically every programming
language is just strings, with a defined grammar and tools that enforce and
extract the structure. This is no different with embeddings, and JS tagged
template literals were specifically designed to make embedded languages more
useful.

lit-html uses the browsers HTML parser to parse the strings, enforcing that
they're well-formed. It creates HTML <template> elements that are efficiently
cloned for new DOM, and it remembers where the dynamic expressions are in the
DOM to efficiently update it on repeated renders. It interpolates data post-
clone, so it's robust against XSS. It has a plug-ins for VS Code, TypeScript,
eslint, and more, for type-checking, highlighting code-completion, and linting
of templates.

Which is just to say that string-based templates are a great option that _do_
allow for fast updates, type-checking, composition, and structure, with
standard syntax available today.

Finally... there are a couple of recent proposals for JavaScript that affect
this area. The records and tuples proposal might make it very efficient to
describe DOM in object notation. The block parameters proposal[3], though
probably defunct now, proposed adding something akin to Kotlin builders, which
would have allowed for the attribute/children structure we need for markup.

[1]: [https://lit-html.polymer-project.org/](https://lit-html.polymer-
project.org/) [2]: [https://github.com/tc39/proposal-record-
tuple](https://github.com/tc39/proposal-record-tuple) [3]:
[https://github.com/samuelgoto/proposal-block-
params](https://github.com/samuelgoto/proposal-block-params)

------
Seb-C
I actually wish we had more advanced features in HTML itself. There are still
many cases today where you only need JS to load something on click or for a
simple datepicker.

If some basic things like <input type="date"> and the <details> tag (and
associated css) were actually standard, reliable and working, we would not
need JS at all most of the time.

I also wonder after 20 years why we still don't have something like <a
target="#myDiv"> or <form target="#myDiv"> to dynamically load content rather
than billions of specific ajax query scripts everywhere.

------
teknopaul
I have had this in my toolkit

[https://gitlab.com/teknopaul/xgenjs](https://gitlab.com/teknopaul/xgenjs)

Gives me neat html in jQuery syntax.

$(...).create("div.foo#my-id/input{type=radio}");

Results in code that is just js, no template needed and no data binding.

$(...).create(...).text(quxx).data("foo", baa);

N.b. create() and most Jquery methods return a nodelist so chaining is easy.

I can work on sets as easily as nodes.

create("input[5]").addClass("btn btn-primary").click(...)

Wrote it years before the framework wars and never got sold on any of them.

I prefer code you can see, but I imagine thats not to everone's taste.

------
namelosw
It is said that JavaScript could be Scheme back in Netscape era. It would be
extremely straightforward to express HTML in Sexp.

It’s a pity that we didn’t behold this happen.

------
jccalhoun
It would be interesting to see what would have happened with the internet had
a few different decisions been made along the way. if a spec had been
implemented differently or CSS or JS hadn't become the norm.
[https://eager.io/blog/the-languages-which-almost-were-
css/](https://eager.io/blog/the-languages-which-almost-were-css/)

------
5cott0
Is the author claiming that example is any different than JSX?

[https://reactjs.org/docs/react-
api.html#createelement](https://reactjs.org/docs/react-api.html#createelement)

In any case I find JSX quite productive only without React or a VDOM.

[https://github.com/ScottORLY/jsx-pragma](https://github.com/ScottORLY/jsx-
pragma)

------
afrcnc
What if you left HTML alone?

------
Waterluvian
> the fact that it's a different syntax from your standard js objects
> encourages people to consider the VDOM objects as "not normal data", but
> they are.

Can someone help me understand what the author means by this? What are people
doing where the author sees them as considering vdom objects as not normal
data?

------
z3t4
Writing UI's in pure functions is a kind of beauty. It's bit tedious without a
framework, but you instead get full control and performance, and semantics.
Btw, if your website is just static HTML like in the article, just write it in
HTML (skip the JavaScript frameworks).

------
wwweston
Ctrl-F `E4X` .... nope.

[https://developer.mozilla.org/en-
US/docs/Archive/Web/E4X](https://developer.mozilla.org/en-
US/docs/Archive/Web/E4X)

(Not quite "all along"/1995, but available by the early 2000s...)

------
discreteevent
QML is this and more.

~~~
jcelerier
While being so much simpler at the same time

------
samuelgoto
I presented this at some point at TC39:

[https://github.com/samuelgoto/proposal-block-
params#kotlins-...](https://github.com/samuelgoto/proposal-block-
params#kotlins-templates)

------
pragmatic
Even in the early 00's JavaScript was a toy. ActiveX and Flash we're popular
ways to get real functionalality on a web page.

Ajax and Jquery are what made js generally useable for "app" type sites.

------
tannhaeuser
eh "you must know the rules to break them" I guess.

It's not like JavaScript, the DOM API, and HTML/SGML/XML don't have a very
long history of coexistence, including a native syntax for XML-in-JavaScript
(E4X) that was however endorsed by Mozilla only, and since abandoned. JSX
stands for "JavaScript XML" btw.

> _I have a theory that a grave mistake was made in 1995 - the decision not to
> have a neat, succinct and declarative way of representing html elements in
> javascript._

I'd have to say this theory is really poorly recherched, then :)

------
jandrese
Interesting note is that Perl's CGI module used to have functions like this
and they were removed a few years ago because it was considered bad form to
mix document and code.

------
sub7
HTML in JS feels so wrong, I'll never get used to React-land. Feels like the
simplest 2 page, 2 action apps now need a hundred modules, all kinds of weird
naming conventions and config dependency spaghetti and of course a complex
build system for what would otherwise be a few lines of plain old JS.

Rails 6 now webpacks 100s of KB of js for a new blank app. wtf if going on.

~~~
baggy_trough
Yes, it's some weird cargo-culting nonsense for sure.

------
akrymski
Personally I find the syntax of SwiftUI to be most sensible.

------
lerpapoo
if this is about react then...xml inside javascript is for scrubs, just rename
your createElement to 1 letter (mine is h) and use coffeescript lol.

i also renamed my "className" to "cn" so i dont have to type it but i wouldn't
recommend that.

    
    
      h 'div',
        cn: 'myclass'
        h 'span',{},"mytext"
        h 'span,{},"asd"

------
gregjor
We'd have PHP in the browser.

~~~
ferzul
if php had a html-in-php syntax, that might be true. but as anyone who has
ever worked with php and jsx knows, this isn't true

~~~
gregjor
PHP has multiple ways to mix HTML and PHP, that’s part of its design. The
simplest way to put a block of HTML inside PHP code is with the heredoc string
syntax. As of PHP 7.3 heredocs allow indenting of the closing label, fixing
the slight ugliness of heredocs.

You can also just switch in and out of PHP to an HTML block.

------
tomc1985
Every day we stray further and further away from God

Please people... keep your HTML out of your javascript

------
ben85ts
JsonML

------
shaunxcode
you mean s-expressions?

