Skip to content

Template Tree ​

In a template function, we have one special argument - the x argument in the example below:

js
import { template } from 'paintor'

template((tree) => {
  tree.div('Hello')
})

We will use this argument to create the HTML tree structure with JavaScript. Some call this HyperScript.

js
(x) => {
  x.table(
    x.thead(
      x.tr(
        x.td(/* ... */),
        x.td(/* ... */)
      )
    ),
    x.tbody(
      x.tr(
        x.td(/* ... */),
        x.td(/* ... */)
      )
    )
  )
}

The argument x (or however you name it) is an object, containing methods for each valid HTML element. For example, .div() is for creating <div> elements, .p() is for creating <p> elements, and so on. Each of these methods accepts multiple arguments, used for creating text content or child elements.

There are a few extra methods, such as:

  • .$element() - for creating a custom element.
  • .$if() - if statement.
  • .$repeat() - iteration loop, starting with one number and ending with another.
  • .$each() - to iterate over Array, Object, or State.
  • .$state() - to react on State changes.

Argument Name ​

The argument name in the template function is arbitrary, it's your choice.

Here are few examples with different names:

js
($) => {
  $.table(
    $.tr(
      $.td('tr1 td1'),
      $.td('tr1 td2')
    ),
    $.tr(
      $.td('tr2 td1'),
      $.td('tr2 td2')
    )
  )
}
js
(_) => {
  _.table(
    _.tr(
      _.td('tr1 td1'),
      _.td('tr1 td2')
    ),
    _.tr(
      _.td('tr2 td1'),
      _.td('tr2 td2')
    )
  )
}
js
(L) => {
  L.table(
    L.tr(
      L.td('tr1 td1'),
      L.td('tr1 td2')
    ),
    L.tr(
      L.td('tr2 td1'),
      L.td('tr2 td2')
    )
  )
}
js
(l) => {
  l.table(
    l.tr(
      l.td('tr1 td1'),
      l.td('tr1 td2')
    ),
    l.tr(
      l.td('tr2 td1'),
      l.td('tr2 td2')
    )
  )
}
js
(o) => {
  o.table(
    o.tr(
      o.td('tr1 td1'),
      o.td('tr1 td2')
    ),
    o.tr(
      o.td('tr2 td1'),
      o.td('tr2 td2')
    )
  )
}
js
(x) => {
  x.table(
    x.tr(
      x.td('tr1 td1'),
      x.td('tr1 td2')
    ),
    x.tr(
      x.td('tr2 td1'),
      x.td('tr2 td2')
    )
  )
}

The $ symbol can be confusing, because of jQuery.

The _ is minimalistic, but it also can be confusing, because of Underscore.

L kinda looks like stair steps when there are inner levels of elements.

l is similar to L, but more compact.

o is like a dot symbol.

x is faster to write with two hands, along with the . after it.

If you don't like to have such prefix, you can use the destructuring assignment syntax:

js
({ table, tr, td }) => {
  table(
    tr(
      td('Row 1, Column 1'),
      td('Row 1, Column 2'),
    )
  )
}

Or like this:

js
(x) => {
  const { table, tr, td } = x

  table( 
    tr(
      td('Row 1, Column 1'),
      x.td('Row 1, Column 2'), // x. can still be used
    )
  )
}

While this may look clean, it may be unpleasant to write the destructured objects and sort the constants in them. Also, it can be a mess if there are other constants and variables around.

WARNING

Depending on the IDE you are using, code completion may not work properly when destructuring.

Syntax Styles ​

Free Style ​

You probably noticed that in the examples above the function calls in the template function aren't entangled in any way. Other blocks of code can exist in between:

js
(x) => {
  /* ... JS code ... */
  
  x.h1('h1 element')

  /* ... JS code ... */
  
  x.h2('h2 element')

  /* ... JS code ... */
  
  x.h3('h3 element')

  /* ... JS code ... */
}

Entangled Style ​

With this style, the elements live together within the same Array:

js
(x) => [
  x.h1('h1 element'),
  x.h2('h2 element'),
  x.h3('h3 element'),
]
html
<h1>h1 element</h1>
<h2>h2 element</h2>
<h3>h3 element</h3>

Or within a single element:

js
(x) => $.div(
  x.h1('h1 element'),
  x.h2('h2 element'),
  x.h3('h3 element'),
)
html
<div>
  <h1>h1 element</h1>
  <h2>h2 element</h2>
  <h3>h3 element</h3>
</div>