h5on
v0.1.1
Published
HTML5 Object Notation
Downloads
12
Maintainers
Readme
H5ON (HTML5 Object Notation)
H5ON is an object notation, like JSON, for representing objects as HTML5 elements.
This document describes the syntax. The project contains both a Node.js and a jQuery plugin implementation.
It came out of a thought experiment with two goals in mind:
- How would I traverse a JavaScript object graph using jQuery?
- How can I have an object notation that the browser displays meaningfully?
As this is the fruit of a thought experiment, please be aware that there are many alternative ways to traverse object graphs, and that for the most part they are much saner than this one. You have been warned!
Despite that caveat, you can do some interesting and powerful things using this approach. jQuery is a sophisticated traversal library and contains a lot of useful ways to search and filter data.
Table of Contents
- Usage
- Node.js
- jQuery
- Convert an object to H5ON
- Place H5ON in the DOM for viewing
- Traverse H5ON using selectors
- Convert H5ON to an object
- Manipulation
- Mixing HTML and H5ON
- Demos and Examples
- Demos
- Selector Examples
- Why would you want to traverse an object graph with jQuery?
- Example - find all objects in the graph which have a property named “Weight”:
- What do you mean by human readable?
- Example of browser rendering, using H5ON generated by the input from the previous example and h5on.css:
- Syntax
- Primitive Literals
- Number
- String
- Boolean
- Array
- Object
- Null
- Status
- License
Usage
Node.js
npm install h5on
h5on requires a reference to a document - use window.document in the browser, otherwise you can use jsdom or similar:
var h5on = require( 'h5on' )( document );
var domElements = h5on.toEl( data );
// do something with elements
var backToJsObject = h5on.toObj( domElements );
The rest of this document assumes that you are using the jQuery plugin - the only real differences are traversal (but you can use document.querySelector
or a library), the custom selectors included in the jQuery plugin, and that you convert to and from JS objects using the static methods outlined above rather than the static $.h5on
and $instance.h5on()
methods.
jQuery
Download and reference h5on.jquery.min.js and h5on.css.
TODO: Bower
Convert an object to H5ON
var person = {
name: 'Akosua',
age: 39
};
var $person = $.h5on( person );
Place H5ON in the DOM for viewing
$( 'body' ).append( $person );
It's recommended that you use CSS (such as the included h5on.css) to style H5ON elements, otherwise the browser won't know how to render them and it'll come out as a mess of plain text.
Traverse H5ON using selectors
var $age = $person.find( '[data-key="age"]' );
Convert H5ON to an object
var age = $age.h5on();
Manipulation
You can manipulate the H5ON DOM in the same way you would the standard DOM, using jQuery.
You can create new H5ON elements however you like (provided the syntax is correct), but the easiest way to create them is to use the static helper:
var $age = $.h5on( 39 );
The plugin expects you to respect the H5ON syntax and performs no error checking.
When creating a property for an object, pass the key as the second argument to the static helper:
$someH5Object.append( $.h5on( 39, 'age' ) );
Mixing HTML and H5ON
The H5ON DOM can contain arbitrary HTML at any point. HTML is converted to and from JavaScript objects using the following syntax:
var heading = {
"tagName": "h1",
"attr": {
"style": "color: red;"
},
"children": [
"Hello World"
]
};
The children
array can contain any mixture of text nodes (as strings), element nodes structured as per above, and H5ON elements - other node types such as comments are not currently supported (pull request welcome!).
Demos and Examples
Demos
Selector Examples
Find all of a specific type
//strings
var $strings = $( 'js-string' );
//objects
var $objects = $( 'js-object' );
//etc.
Find all objects that have a certain key
var $withWeights = $( 'js-object:has( > [data-key="Weight"] )' );
Find all objects with a certain key value pair
var $containers = $( 'js-object:has( > [data-key="Type"]:valEq( "Container" ) )' );
Find all objects that contain a certain type
var $withArrays = $( 'js-object:has( > js-array )' );
Custom selector expressions
The plugin has the following custom selector expressions:
:valEq( value )
:valLte( value )
:valLt( value )
:valGte( value )
:valGt( value )
For example:
var $lightObjects = $( 'js-object:has( > [data-key="Weight"]:valLt( 500 ) )' )
Why would you want to traverse an object graph with jQuery?
JSON data is an object graph - the DOM is an object graph. Many developers are already familiar with using selectors to traverse and manipulate the DOM. By representing an object graph as HTML5 custom elements, you can apply the same techniques you use on the DOM with any data. The plugin takes a plain (JSON-serializable) JavaScript object as data, and returns H5ON, which is composed of ordinary DOM elements wrapped in a jQuery object which can be traversed and manipulated as usual, and can be placed in a document for viewing. Calling the H5ON function again on these jQuery objects converts them back to a plain JavaScript object.
In addition, H5ON also maps non-H5ON elements to and from JavaScript objects so that you can mix H5ON and ordinary DOM elements.
Example - find all objects in the graph which have a property named "Weight":
Input
{
"Name": "Akosua",
"Occupation": "Zombie Hunter",
"Is Infected": false,
"Equipment": [
{
"Name": "Backpack",
"Type": "Container",
"Capacity": 40000,
"Weight": 2000,
"Contents": [
{
"Name": "Water Bottle",
"Type": "Container",
"Capacity": 1000,
"Weight": 0.2,
"Contents": [
{
"Name": "Water",
"Weight": 365.9
}
]
},
{
"Name": "Necronomicon",
"Type": "Book",
"Weight": 0.87
}
]
},
{
"Name": "Katana",
"Type": "Weapon",
"Class": "Edged",
"Damage": {
"Base": "4d6",
"Modifier": -2
},
"Weight": 1200
}
]
}
JavaScript
//convert a JavaScript object to H5ON
var $h5Data = $.h5on( input );
//find all objects in the object graph with a key matching "Weight"
var $h5WithWeights = $h5Data.find( 'js-object:has( > [data-key]="Weight" )' );
//convert the H5ON back to a JavaScript object
var output = $h5WithWeights.h5on();
Or, in one line:
var output = $.h5on( input ).find( 'js-object:has( > [data-key]="Weight" )' ).h5on();
Output
[
{
"Name": "Backpack",
"Type": "Container",
"Capacity": 40000,
"Weight": 2000,
"Contents": [
{
"Name": "Water Bottle",
"Type": "Container",
"Capacity": 1000,
"Weight": 0.2,
"Contents": [
{
"Name": "Water",
"Weight": 365.9
}
]
},
{
"Name": "Necronomicon",
"Type": "Book",
"Weight": 0.87
}
]
},
{
"Name": "Water Bottle",
"Type": "Container",
"Capacity": 1000,
"Weight": 0.2,
"Contents": [
{
"Name": "Water",
"Weight": 365.9
}
]
},
{
"Name": "Water",
"Weight": 365.9
},
{
"Name": "Necronomicon",
"Type": "Book",
"Weight": 0.87
},
{
"Name": "Katana",
"Type": "Weapon",
"Class": "Edged",
"Damage": {
"Base": "4d6",
"Modifier": -2
},
"Weight": 1200
}
]
What do you mean by human readable?
When we say "human readable", we primarily mean as rendered by a browser's layout engine.
Example of browser rendering, using H5ON generated by the input from the previous example and h5on.css, themed with Solarized Light:
Browser rendering
Generated H5ON
<js-object>
<js-string data-key="Name">Akosua</js-string>
<js-string data-key="Occupation">Zombie Hunter</js-string>
<js-boolean data-key="Is Infected">false</js-boolean>
<js-array data-key="Equipment">
<js-object>
<js-string data-key="Name">Backpack</js-string>
<js-string data-key="Type">Container</js-string>
<js-number data-key="Capacity">40000</js-number>
<js-number data-key="Weight">2000</js-number>
<js-array data-key="Contents">
<js-object>
<js-string data-key="Name">Water Bottle</js-string>
<js-string data-key="Type">Container</js-string>
<js-number data-key="Capacity">1000</js-number>
<js-number data-key="Weight">0.2</js-number>
<js-array data-key="Contents">
<js-object>
<js-string data-key="Name">Water</js-string>
<js-number data-key="Weight">365.9</js-number>
</js-object>
</js-array>
</js-object>
<js-object>
<js-string data-key="Name">Necronomicon</js-string>
<js-string data-key="Type">Book</js-string>
<js-number data-key="Weight">0.87</js-number>
</js-object>
</js-array>
</js-object>
<js-object>
<js-string data-key="Name">Katana</js-string>
<js-string data-key="Type">Weapon</js-string>
<js-string data-key="Class">Edged</js-string>
<js-object data-key="Damage">
<js-string data-key="Base">4d6</js-string>
<js-number data-key="Modifier">-2</js-number>
</js-object>
<js-number data-key="Weight">1200</js-number>
</js-object>
</js-array>
</js-object>
Syntax
Like JSON, possible values are:
- number
- string
- boolean
- array
- object
- null
Primitive Literals
The primitive values are number, string and boolean. The notation for these is:
<js-{{type}}>{{value}}</js-{{type}}>
When converting back to an object, the plugin expects this element to contain a single text node only, and the text content of that node is converted to the expected type. No error checking is performed!
Number
H5ON
<js-number>42</js-number>
JSON
42
String
H5ON
<js-string>Hello World</js-string>
JSON
"Hello World"
Boolean
H5ON
<js-boolean>false</js-boolean>
JSON
false
Array
H5ON
<js-array>
<js-number>42</js-number>
<js-string>Hello World</js-string>
<js-boolean>false</js-boolean>
</js-array>
JSON
[ 42, "Hello World", false ]
When converting back to an object each direct child of the js-array element is considered to be an array element. An array can contain any other value.
Object
H5ON
<js-object>
<js-string data-key="name">Akosua</js-string>
<js-number data-key="age">39</js-number>
<js-string data-key="hobby">Hunting zombies</js-string>
</js-object>
JSON
{
"name": "Akosua",
"age": 39,
"hobby": "Hunting zombies"
}
When converting an H5ON object back to a JavaScript object, every direct child of the js-object element is considered to be a property on the JS object. Each direct child should have an attribute data-key
and this will be used as the key on the resulting object. If the attribute is missing an autogenerated key will be used.
Null
H5ON
<js-null />
JSON
null
Status
Working prototype - usable, shows what it can do, demonstrates the concept, imperfect. Pull requests welcomed, if it's a simple and obvious bug fix go ahead, but for anything more major please open an issue to discuss first.
License
The MIT License (MIT)
Copyright (c) 2015 Nik Coughlin
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.