Skip to content
Kyle Robinson Young edited this page Sep 14, 2016 · 28 revisions

bel is a simple element creator. It returns pure DOM elements.

If you are looking for a more higher level library, that will do DOM diffing with bel, check out https://github.com/maxogden/yo-yo.

Because bel creates native DOM elements, they run standalone. No need to constantly update to keep in step with a framework or separate rendering library. Spend that time making each element do one thing better and better.

When compiling for production performance or when targeting older browsers, use yo-yoify to tranform your bel or yo-yo calls into raw document calls.

How bel works

bel uses tagged template strings to compose elements. They are part of the JavaScript language and are available in modern browsers. There are build tools to compile for older browser targets.

You can read a more detailed description on the page How bel works

Compose an Element

// Require bel
var bel = require('bel')

// Compose an element
var element = bel`<div>Hello!</div>`

// Add to the page
document.body.appendChild(element)

Compose Multiple Elements

With template strings, anything between the curly braces ${} is JavaScript:

var bel = require('bel')

var bears = ['grizzly', 'polar', 'brown']
var element = bel`<ul>
  ${bears.map(function (bear) {
    return bel`<li>${bear}</li>`
  })}
</ul>`

document.body.appendChild(element)

Attributes and Events

Attributes and event handlers can be added to elements in a natural way:

var button = bel`<button class="primary" onclick=${function (e) {
  alert('button was clicked!')
}}>click me</button>`

Updating an Element

When your element has changed, call it's .update() function to replace the element with a new one using DOM diffing:

var element = bel`<div>hello</div>`
var morphdom = require('morphdom')

// ... later ...

morphdom(element, bel`<div>changed!</div>`)

Data Down

Any time the data changes, re-render your element with the new data:

function render (msg) {
  return bel`<div>${msg}</div>`
}
var element = render('hello')

// ... later ...

var morphdom = require('morphdom')
morphdom(element, render('changed!'))

Scaling

As your element grows more complex, simply move that portion to another function and keep passing the data down:

var bears = ['grizzly', 'polar', 'brown']
var element = render(bears)

function render (bears) {
  return bel`<ul>
    ${bears.map(function (bear) {
      return bel`<li>${button(bear)}</li>`
    })}
  </ul>`
}

function button (label) {
  return bel`<button>${label}</button>`
}

Actions Up

When an event occurs and/or your data needs to change, send up an action:

var bears = ['grizzly', 'polar', 'brown']

// First render a list of bears without any selected
var element = render(bears, 'No Bear Selected')

// We pass down all the values needed to render
function render (bears, selectedBear) {
  return bel`<ul>
    <li><strong>${selectedBear}</strong></li>
    ${bears.map(function (bear) {
      return bel`<li>${button(bear, selectBear)}</li>`
    })}
  </ul>`
}

// When a bear is selected, we update the element with the newly selected bear
function selectBear (bear) {
  morphdom(element, render(bears, bear))
}

// This button doesn't need to know about the above code
// This element focuses on doing one thing, being a button
function button (label, onselected) {
  return bel`<button onclick=${function () {
    onselected(label)
  }}>${label}</button>`
}

More Complex Actions

If an element emits many actions, you can still handle them all through a single interface:

function button (label, action) {
  return bel`<button onclick=${function () {
    action('clicked', label)
  }} oncontextmenu=${function (e) {
    e.preventDefault()
    action('menu', e.target)
  }}>${label}</button>`
}

var element = button('click me', function (name, item) {
  console.log('do ' + name + ' action')
})

SVG

var r = 50
var svg = bel`<svg width="100" height="100" viewBox="0 0 100 100">
  <circle r="${r}" cx="${r/2}" cy="${r/2}" fill="red" />
</svg>`

Be aware that SVG elements and attributes are case sensitive. viewbox will not be corrected to viewBox.

Elements On npmjs.com

A primary goal of this project is to make it easy to compose and publish standalone elements to npm.

The end user doesn't even need to know they are using bel. They can use the element like any other native element:

var breadcrumb = require('breadcrumb-element')
document.querySelector('.app').appendChild(breadcrumb([1, 2, 3]))

Or if they are using bel, elements integrate naturally:

var bel = require('bel')
var breadcrumb = require('breadcrumb-element')
var table = require('./lib/table.js')

// Even compatible with elements not created using bel
var nonBelElement = document.createElement('h3')
nonBelElement.textContent = 'Hello!'

var app = bel`<div class="app">
  ${nonBelElement}
  ${breadcrumb([1, 2, 3])}
  ${table()}
</div>`

List of Elements on npm