Skip to main content
Deno 2 is finally here 🎉️
Learn more

! Attention: This whole repo is automatically generated !

Building, linting and unit tests can be found in original repo:
https://github.com/eirikb/domdom

This repo exists only for pure Deno support.
It makes domdom available without use of pika.dev or jspm!

image

Usage with Deno

index.tsx

import domdom from 'https://deno.land/x/domdom';

const { React, data, append } = domdom();

append(document.body, ({ on }) => <div>Hello {on('test')}</div>);

data.set('test', 'World!');

tsconfig.json

{
  "compilerOptions": {
    "lib": ["dom", "esnext"],
    "noImplicitAny": false
  }
}

Original readme below here

npm Build bundlephobia

The proactive web framework for the unprofessional

domdom is an alternative to React + Redux or Vue + Vuex, with support for routing.
There’s no virtual dom.

Install

npm i @eirikb/domdom

Getting started

index.html

<body><script src="app.jsx"></script></body>

app.jsx

import domdom from '@eirikb/domdom'

const dd = domdom()

const view = ({ on }) => <div>Hello, {on('name')}</div>

dd.append(document.body, view)

dd.set('name', 'world!')

Run

npx parcel index.html

Recipes

How to handle common tasks with domdom

Routing

const view = ({ when }) => <div>
  {when('route', [
    'login', () => <Login/>,
    'welcome', () => <Welcome/>,
  ]).or('Loading app...')}
</div>

function gotoRoute(route) {
  window.location.hash = route
}

window.addEventListener('hashchange', () =>
  dd.set('route', window.location.hash.slice(1))
)

Login form

function login(event) {
  event.preventDefault()
  fetch('/login', {
    method: 'post',
    body: new URLSearchParams(new FormData(event.target))
  })
}

const view = () => <form onSubmit={login}>
  <input name="username"/>
  <input name="password" type="password"/>
  <button type="submit">Login</button>
</form>

Split view and data

data.js

export default ({ on, set }) => {
    on('= search', event => {
      event.preventDefault()
      const searchText = event.target.search.value
      set('result', `Data for ${searchText} here...`)
    })
}

index.jsx

import data from './data'
import domdom from '@eirikb/domdom'

const dd = domdom()

data(dd)

const view = ({ on, trigger }) => <form onSubmit={e => trigger('search', e)}>
  <input type="search" name="search"/>
  <button type="submit">Search</button>
  {on('result', _ => _)}
</form>

dd.append(document.body, view)

Animation (garbage collection)

At writing moment domdom doesn’t have any unmount callback.
I’m not a big fan of destructors, unmounted, dispose or similar.
This might seem silly, and it might not be obvious how to use say setInterval, without this preventing the element from ever being cleaned up by garbage collector.
The idea is to use dd for such things, as these listeners are automatically cleaned up.

const view = ({ on, get, set }) => {
  const img = <img src="https://i.imgur.com/rsD0RUq.jpg"/>

  on('tick', time => img.style.transform = `rotate(${time % 180}deg)`)

  return <div>
    <button onClick={() => set('run', !get('run'))}>Start/Stop</button>
    {img}
  </div>
}

(function loop(time) {
  if (dd.get('run')) {
    dd.set('tick', time)
  }
  requestAnimationFrame(loop)
})(0)

API

The domdom object (from domdom(), called dd above) extends from @eirikb/data.
All data functions are available, on off get set unset trigger, so it’s possible to do things like:

dd.on('!+* a', a => console.log(a))
dd.set('a', 'yes')

The only function in addition is append, which is for appending a view to a parent element.

const viewFunction = () => <div></div>
const parentElement = document.querySelector('#app')
dd.append(parentElement, viewFunction)

Elements

All elements created with jsx, in the context of domdom, are elements which can be instantly referenced.

const element = <div>Behold!</div>
element.style.color = 'red'

“Components”

By creating a function you create a component.

function MyComponent({ on }) {
  return <ul>
    {on('players.$id.name', name => <li>Player {name}</li>)}
  </ul>
}

Children / Composition

Content of a component will be passed as children.

function Button({ children }) {
  return <button>{children}</button>
}

const view = () => <div>
  <Button>
    <b>Hello</b>
  </Button>
</div>

Events

All attributes starting with ‘on’ are added to addEventListener on the element.

function MyButton({trigger}) {
  return <button onClick={() => trigger('Clicked!')}>Click me!</button>
}

on(path, callback)

Similar to data.on, except without flags.

Note that on triggers on change in accordance with data.on, and it’s not “truey”/”falsey”, in order for elements to be removed one must use dd.unset.

callback is optional, if omitted the result will be returned as-is,
either as string or JSON of object.

const view = ({ on }) => <ul>
  {on('players.$id.name', name => <li>Player {name}</li>)}
  {on('info')}
</ul>

when(path, oddEvenArrayOfCheckAndResult)

Heard of pattern matching? This isn’t it

const view = ({ when }) => <div>
  {when('route', [
    'home', () => <div>Home!</div>,
    'away', () => <div>Away!</div>,
     route => (route || '').startsWith('eh'), () => <div>Eh?</div>,
     false, () => <div>Route is literally boolean false</div>
  ])}
</div>

or

Neither on or when will trigger unless there is a value on the path, in order to show something at all until some value is set or must be used.

const view = ({ when }) => <div>
  {when('routing', [
    'home', () => <div>Home!</div>
  ]).or(<div>Loading app in the fastest possible way...</div>)}
</div>

dd-model

This is a convenience hack for putting data into and out of a data path from an input.
Similar to v-model and ng-model.
Suggest not using this if possible, using forms directly like in recipes is much better.

dd.on('= search', event => {
  event.preventDefault()
  dd.set('result', `Data for ${dd.get('text')} here...`)
})

const view = ({ when, on, trigger }) => <form onSubmit={e => trigger('search', e)}>
  <input type="search" dd-model="text"/>
  <input type="checkbox" dd-model="more"/>
  {when('more', [
    true, () => 'This is more'
  ])}
  Current text: {on('text')}
  <button type="submit">Search</button>
  {on('result', _ => _)}
</form>

Attributes

It’s possible to use on directly on attributes.
It might feel and look a bit quirky, but there it is.

  const view = ({ on, set, get }) => <div>
    <button onClick={() => set('toggle', !get('toggle'))}>Toggle</button>
    <button disabled={on('toggle').or(true)}>A</button>
    <button disabled={on('toggle', res => !res)}>B</button>
  </div>;