hyperglue2

3.2.0 • Public • Published

hyperglue2

A hyperglue implementation (DOM-only).

npm

Why

Hyperglue is an awesome templating engine that accepts vanilla html strings or DOM elements as templates. This is nice because you don't have to learn any fancy new languages and you can update templates you've already rendered without recompiling any HTML.

Although the concept behind this library is faithful to the original, the API is not. More about this under Notes.

How

Set textContent

var hg = require('hyperglue2');
var el = hg('<div></div>', 'hello');
// or 
var el = hg('<div></div>', { _text: 'hello' });
// <div>hello</div>

Make updates

// note: 'el' is a dom element, not a string
hg(el, 'hello world');
// <div>hello world</div>

InnerHTML

hg(el, { _html: '<input>' });
// <div><input></div>

Attributes

hg(el, { input: { _attr: { name: 'field', value: 42 }}}); // add
// <div><input name="field" value="42"></div>
hg(el, { input: { _attr: { name: null }}});               // remove
// <div><input value="42"></div>

Form element value (attributes only work for inital values)

hg(el, { input: { _value: 23 }});              // add
// <div><input value="42"></div>               // but display and js will show 23
hg(el, { input: { _value: null }});            // remove
// <div><input value="42"></div>               // but display and js will show ""

Generic properties

hg(el, { input: { _prop: { checked: true }}}); // sets .checked directly on element

ClassList

hg(el, { input: { _class: { hidden: true }}}); // add
// <div><input class="my-input hidden"></div>
hg(el, { input: { _class: { hidden: false }}}); // remove
// <div><input class="my-input"></div>

Elements

var el = hg('<div></div>');
var header = hg('<h1></h1>');
var bold = hg('<b>elementary</b>');
 
hg(el, header);                     // element literal
hg(el, { h1: { _element: bold }});  // element selector
// <div><h1><b>elementary</b></h1></div>

Singletons

var src = 'movie.avi'
var opts = {
  video: {
    _single: src,
    _attr: {
      src: src
    }
  }
}
var el = hg('<div><video></video></div>', opts);
// <div><video src="movie.avi"></video></div>
el.querySelector('video').setAttribute('data-single', '')
el = hg('<div><video></video></div>', opts);
// <div><video src="movie.avi" data-single></video></div>

Arrays

var el = hg('<ul><li></li></ul>', { li: [ 1, 2, 3 ] });
// <ul>
//   <li>1</li>
//   <li>2</li>
//   <li>3</li>
// </ul>

Array updates

hg(el, { li: [ 1, 'the end' ] });
// <ul>
//   <li>1</li>
//   <li>the end</li>
// </ul>

Respect element boundaries

hg('<section><h1></h1><section><h1></h1></section></section>', { h1: 'the title' }, { boundary: 'section' });
// <section>
//   <h1>the title</h1>
//   <section>
//     <h1></h1>
//   </section>
// </section>

Notes

So, I really like the original hyperglue library, but!

  • the ":first" selector confused me
  • array updates don't work
  • can't remove attributes

Detailed explanations below:

The ":first" selector

The first thing you're likely to try with hyperglue is probably something along the lines of: hg('<div></div>', 'hello world') which of course doesn't work, because to select outer elements you actually need to use the ":first" selector like so: hg('<div></div>', { ':first': 'hello world' }). Hyperglue2 does away with ":first" and just assumes you are always talking about the outermost element you've selected, this sacrifices attribute manipulation ease in favor of a more obvious way to set inner content:

// original hyperglue
hg('<ul><li></li></ul>', { li: [ { ':first': 1 }, { ':first': 2 }, { ':first': 3 } ] });
// <ul>
//   <li>1</li>
//   <li>2</li>
//   <li>3</li>
// </ul>
 
// hyperglue2
hg('<ul><li></li></ul>', { li: [ 1, 2, 3 ] });
// <ul>
//   <li>1</li>
//   <li>2</li>
//   <li>3</li>
// </ul>

Array updates

Updating an existing dom element (instead of generating a new one from a string) is possible with the original hyperglue, but it doesn't work for arrays. Consider:

// original hyperglue
 
// first render
var el = hg('<ul><li></li></ul>', { li: [ { ':first': 1 }, { ':first': 2 }, { ':first': 3 } ] });
 
// update
hg(el, { li: [ { ':first': 'a' }, { ':first': 'b' }, { ':first': 'c' } ] });
// <ul>
//   <li>a</li>
//   <li>b</li>
//   <li>c</li>
//   <li>a</li>
//   <li>b</li>
//   <li>c</li>
//   <li>a</li>
//   <li>b</li>
//   <li>c</li>
// </ul>
 
// Works in hyperglue2
var el = hg('<ul><li></li></ul>', { li: [ 1, 2, 3 ] })
hg(el, { li: [ 'a', 'b', 'c' ] });
// <ul>
//   <li>a</li>
//   <li>b</li>
//   <li>c</li>
// </ul>

Attribute removal

Some html attributes like "checked" or "selected" can't be set to falsy values, so the only way to disable them is to completely remove the attribute. You can remove attributes with hyperglue2 by setting the attribute data value to null.

hg('<input type="checkbox" checked>', { _attr: { checked: null }});
// <input type="checkbox">

Releases

The latest stable release is published to npm. Below is an abbreviated changelog:

  • 3.x

    • Switch to MIT license.
  • 2.x

    • Explictly passing null values now removes elements matched by the given selector. Clearing textContent can be done by passing an empty string.
  • 1.6.x

    • Add _value selector for working with form inputs (value attribute and textContent only work for setting an initial value)
  • 1.5.x

    • Don't use for-in loop as it doesn't work properly on arrays with unset indexes
  • 1.4.x

    • Add classList support via the _class selector
  • 1.3.x

    • Improved falsy value handling
  • 1.1.x

    • Added _element selector
  • 1.0.x

    • Added ability to pass DOM elements as data
    • Added "boundary" option
  • 0.0.x

    • Prototype

License

MIT

Package Sidebar

Install

npm i hyperglue2

Weekly Downloads

1

Version

3.2.0

License

MIT

Last publish

Collaborators

  • jessetane