Skip to content
/ dot-dom Public
forked from wavesoft/dot-dom

.dom is a tiny (511 byte) template engine that uses virtual DOM and some of react principles

License

Notifications You must be signed in to change notification settings

styfle/dot-dom

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

.dom Build Status Try it in codepen.io

A tiny (511 byte) virtual DOM template engine for embedded projects

IE / Edge IE / Edge Firefox Firefox Chrome Chrome Safari Safari Opera Opera iOS Safari iOS Safari Chrome for Android Chrome for Android
Edge 14+ 45+ 49+ 10+ 37+ 10.2+ 55+

.dom borrows some concepts from React.js (such as the re-usable Components and the Virtual DOM) and tries to replicate them with the smallest possible footprint, exploiting the ES6 javascript features.

Why? Because with such library you can create powerful GUIs in tight space environments, such as IoT devices, where saving even an extra byte actually matters!

Features

  • Tiny by design : The library should never exceed the 512 bytes in size. The goal is not to have yet another template engine, but to have as many features as possible in 512 bytes. If a new feature is needed, an other must be sacraficed or the scope must be reduced.

  • Built for the future : The library is heavily exploiting the ES6 specifications, meaning that it's not supported by older borwsers. Currently it's supported by the 70% of the browsers in the market, but expect this to be 90% within the next year.

  • Declarative : Describe your HTML DOM in a structured, natural manner, helping you create powerful yet readable user interfaces.

  • Component-Oriented : Just like React.js, .dom promotes the use of functional components.

  • "Write less" accelerators : The library API is designed specifically to have short function names and accelerators, allowing you to describe your views with less code.

Installation

For minimum footprint, include dotdom.min.js.gz (511b) to your project.

<script src="dotdom.min.js.gz" />

Alternatively you can just include the minified version of the library directly before your script. Just copy-paste the following (779b):

((a,b,c,d,e,f,g,h)=>{String.prototype[d]=1,f=(i,j={},...k)=>({[d]:1,E:i,P:j[d]?{C:[].concat(j,...k)}:(j.C=[].concat(...k))&&j}),a.R=g=(i,j,k='',l=j.childNodes,m=0)=>{for((i.map?i:[i]).map((n,o,p,q=k+'.'+o,r=e[q]||[{},n.E],s=e[q]=r[1]==n.E?r:[{},n.E],t=l[m++],u)=>{n.E&&n.E.call&&(n=n.E(n.P,s[0],v=>c.assign(s[0],v)&&g(i,j,k))),u=n.trim?b.createTextNode(n):b.createElement(n.E),(u=t?t.E!=n.E&&t.data!=n?j.replaceChild(u,t)&&u:t:j.appendChild(u)).E=n.E,n.trim?u.data=n:c.keys(n.P).map((v)=>'style'==v?c.assign(u[v],n.P[v]):u[v]!==n.P[v]&&(u[v]=n.P[v]))&&g(n.P.C,u,q)});l[m];)j.removeChild(l[m])},h=i=>new Proxy(i,{get:(j,k,l)=>h((...m)=>((l=j(...m)).P.className=[l.P.className]+' '+k,l))}),a.H=new Proxy(f,{get:(i,j)=>i[j]||h(f.bind(a,j))})})(window,document,Object,Symbol(),{});

Examples

If you already know React.js, the following examples can help you understand how the .dom primitives relate to React.

1. Plain DOM

Rendering a very simple DOM structure.

React .dom
ReactDOM.render(
  React.createElement('div', null, 'Hello world'),
  document.body
);
R(
  H('div', 'Hello world'),
  document.body
)

2. Simple component

Creating a component on which you can pass properties.

React .dom
class Hello extends React.Component {
  render() {
    return React.createElement(
      'div', null, `Hello ${this.props.toWhat}`
    );
  }
}
ReactDOM.render(
React.createElement(
Hello, {toWhat: 'World'}, null
),
document.body
);

function Hello(props) {
  return H('div', `Hello ${props.toWhat}`);
}

R(
  H(Hello, {toWhat: 'World'}),
  document.body
)
</td>

3. Stateful component

Creating components that can maintain their own state.

React .dom
class Clickable extends React.Component {
  constructor() {
    super(...arguments);
    this.state = {
      clicks: 0
    };
  }
render() {
const {clicks} = this.state;
return React.createElement(
  'button', {
    onClick() {
      this.setState({clicks: clicks+1})
    }
  }, `Clicked ${clicks} times`
);

}
}
ReactDOM.render(
React.createElement('div', null,
React.createElement(Clickable, null, null),
React.createElement(Clickable, null, null)
),
document.body
);

function Clickable(props, state, setState) {
  const {clicks=0} = state;

  return H('button',
    {
      onclick() {
        setState({clicks: clicks+1})
      }
    },
    `Clicked ${clicks} times`
  );
}

R(
  H('div',
    H(Clickable),
    H(Clickable)
  ),
  document.body
)
</td>

API Reference

Render R( VNode, DOMElement )

R( H('div', 'Hello'), document.body )

Renders the given VNode tree to the given DOM element. Further updates from stateful components will only occur on their immediate children.

Create Element H( tagName | function, [properties], [children ...])

H( 'tag' )
H( 'tag', {prop: "value"})
H( 'tag', H( 'child' ))
H( 'tag', {prop: "value"}, H( 'child' ))
H( Component, {prop: "value"} )

Creates a VNode element. If a string is passed as the first argument, it will create a HTML element. If a function is given, it will create a stateful component.

Properties and children are optional and they can be omitted.

Functional Components

Instead of a tag name you can provide a function that returns a Virtual DOM according to some higher-level logic. Such function have the following signature:

const Component = (props, state, setState) {

  // Return your Virtual DOM
  return div( ... )
}

The props property contains the properties object as given when the component was created.

The state is initialized to an empty object {} and it's updated by calling the setState({ newState }) method. The latter will also trigger an update to the component and it's children.

You can also assign properties to the state object directly if you don't want to cause an update.

Tag Shorthand tag( [properties], [children ...] )

const {div, span, a} = H;

div( 'hello', span( 'world' ) )
div( 'click', a({href: '#'}, 'Here'), 'to continue')

A shorthand function can be extracted as a property from the H function. Such shorthands behave exactly like H, but with the tag name already populated.

It's recommended to use a deconstructuring assignment in the beginning of your script in order to help javascript minifiers further optimize the result:

const {div, span, a, button} = H;

Tag + Class Shorthand tag.class( [properties], [children ...] )

const {h1, span, p} = H;

h1.short( 'short header', span.strong( 'strong text' ) )
button.primary({onclick: handleClick}, 'Primary Action')
p.bold.italic( twitterPost )

Instead of providing the className as a property, you can use the .className shorthand in combination with the shorthand tag methods.

This is the same as calling div({className: 'className'}) and the function interface is exactly the same as above.

Note: You can add more than one class by concatenating more than one .class to the tag. For example: div.foo.bar is the same as div({className: 'foo bar'}).

Caveats

  • You cannot trigger an update with a property removal. You must set the new property to an empty value instead. For example:

    // Wrong
    R(div({className: 'foo'}), document.body);
    R(div({}), document.body);
    
    // Correct
    R(div({className: 'foo'}), document.body);
    R(div({className: ''}), document.body);

Contribution

Are you interested in contributing to .dom? You are more than welcome! Just be sure to follow the guidelines:

  1. Install a local development environment (you will need node.js 6.x or later)
npm install
  1. Always run the following when you think you are ready for a pull request:
npm test && npm run build && ls -l dotdom.min.js.gz
  1. If tests pass and the size of dotdom.min.js.gz is smaller than or equal to 512 bytes, create a pull request. Otherwise reduce your scope or think of another implementation in order to bring it back down to 512 bytes.

  2. Make sure to properly comments your code, since you will most probably have to do some extreme javascript hacking. The gudeliens are the following:

/**
 * Functions are commented as JSDoc blocks
 *
 * @param {VNode|Array<VNode>} vnodes - The node on an array of nodes to render
 * ...
 */
global.R = render = (
  vnodes,                                                           // Flat-code comments start on column 70 and
  dom,                                                              // wrap after column 120.

  /* Logical separations can be commented like this */

  ...

About

.dom is a tiny (511 byte) template engine that uses virtual DOM and some of react principles

Resources

License

Security policy

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 100.0%