hyper-element
Combining the best of hyperHTML and Custom Elements!
Your new custom-element will be rendered with the super fast hyperHTML and will react to tag attribute and store changes.
why hyper-element
- hyper-element is fast & small
- With only 1 dependency: hyperHTML
- With a completely stateless approach, setting and reseting the view is trivial
- Simple yet powerful Api
- Built in template system to customise the rendered output
- Inline style objects supported (similar to React)
- First class support for data stores
- Pass
function
to other custom hyper-elements via there tag attribute
Live Demo
★ it on github
If you like it, pleaseDefine a custom-element
document// END my-elem
If using webpack
const hyperElement from "hyper-element"
To use your element in brower
Output
hello world
Api
Define your element
There are 2 functions. render
is required and setup
is optional
render
This is what will be displayed with in your element. Use the Html
to define your content
Html
The primary operation is to describe the complete inner content of the element.
{ Html` Lasted updated at `}// END render
The Html
has a primary operation and two utilities: .wire
& .lite
Html.wire
The .wire
is for creating reusable sub-element
The wire can take two arguments Html.wire(obj,id)
- a refrive object to match with the create node. Allowing for reuse of the exiting node.
- a string to identify the markup used. Allowing the markup template to be generated only once.
Example of displaying a list of users from an array
Html` `
An anti-pattern is to inline the markup as a string
BAD example: ✗
Html` `
This will create a new node for every element on every render. The is have a Negative impact on performance and output will Not be sanitized. So DONT do this!
Html.lite
The .lite
is for creating once off sub-element
Example of wrapping the jQuary date picker
{ console} // END onSelect { const inputElem = lite`<input type="text"/>` ; return any: inputElem once:true } // END Date { Html` Pick a date `} // END render
setup
The setup
function wires up an external data-source. This is done with the attachStore
argument that binds a data source to your renderer.
Connect a data source
Example of re-rendering when the mouse moves. Will pass mouse values to render
// getMouseValues(){ ... } { // the getMouseValues function will be call before each render and pass to render const onStoreChange = // call next on every mouse event // cleanup logic return console}// END setup
re-rendering without a data source
Example of re-rendering every second
{ ;}// END setup
Set initial values to pass to every render
Example of hard coding an object that will be used on every render
{ }// END setup
How to cleanup
Any logic you wish to run when the element is removed from the page should be returned as a function from the setup
function
// listen to a WebSocket{ let newSocketValue; const onStoreChange = ; const ws = "ws://127.0.0.1/data"; ws { newSocketValue = JSON; } // Return way to unsubscribe return wsclose}// END setup { // ...}// END render
Returning a "teardown function" from setup
address's the problem of needing a reference to the resource you want to release.
If the "teardown function" was a public function. We would need to store the reference to the resource somewhere. So the teardown can access it when needed.
With this approach there is no leaking of references.
✎ To subscribe to 2 events
{ const onStoreChange = ; mobx; // update when changed (real-time feedback) ; // update every second (update "the time is now ...") }// END setup
this
- this.attrs : the attributes on the tag
<my-elem min="0" max="10" />
={ min:0, max:10 }
- Casting types supported:
Number
- Casting types supported:
- this.store : the value returned from the store function. !only updated before each render
- this.wrappedContent : the text content embedded between your tag
<my-elem>Hi!</my-elem>
="Hi!"
- this.element : a reference to your created element
- this.dataset: this allows reading and writing to all the custom data attributes
data-*
set on the element.- Data will be parsed to try and cast them to Javascript types
- Casting types supported:
Object
,Array
,Number
&Boolean
dataset
is a live reflection. Changes on this object will update matching data attribute on its element.- e.g.
<my-elem data-users='["ann","bob"]'></my-elem>
tothis.dataset.users // ["ann","bob"]
- e.g.
- ⚠ For performance! The
dataset
works by reference. To update an attribute you must use assignment on thedataset
- Bad:
this.dataset.user.name = ""
✗ - Good:
this.dataset.user = {name:""}
✓
- Bad:
Advanced attributes
Dynamic attributes with custom-element children
Being able to set attributes at run-time should be the same for dealing with a native element and ones defined by hyper-element.
⚠ To support dynamic attributes on custom elements YOU MUST USE customElements.define
which requires native ES6 support! Use the native source /source/hyperElement.js
NOT /source/bundle.js
This is what allows for the passing any dynamic attributes from parent to child custom element! You can also pass a function
to a child element(that extends hyperElement).
Example:
In you document:
Implementation:
windowcustomElements windowcustomElements
Output:
Beckett Say hi!
Templates
You can declare markup to be used as a template within the custom element
To enable templates:
- Add an attribute "templates" to your custom element
- Define the template markup within your element
Example:
In you document:
<my-list template data-json='[{"name":"ann","url":""},{"name":"bob","url":""}]' > <div> <a href="{url}">{name}</a> </div></my-list>
Implementation:
document// END my-list
Output:
<my-list template data-json='[{"name":"ann","url":""},{"name":"bob","url":""}]' > <div> <a href="">ann</a> </div> <div> <a href="">bob</a> </div></my-list>
Fragments
Fragments are pieces of content that can be loaded asynchronously.
You define one with a class property starting with a capital letter.
To use one within your renderer. Pass an object with a property matching the fragment name and any values needed.
The fragment function should return an object with the following properties
- placeholder: the placeholder to show while resolving the fragment
- once: Only generate the fragment once.
- Default:
false
. The fragment function will be run on every render!
- Default:
and one of the following as the fragment's result:
- text: An escaped string to output
- any: An type of content
- html: A html string to output, (Not sanitised)
- template: A template string to use, (Is sanitised)
- values: A set of values to be used in the template
Example:
Implementation:
document// END my-friends
Output:
loading your number of friends
then
you have 635 friends
fragment templates
You can use the template syntax with in a fragment
- The template will use the values pass to it from the render or using a "values" property to match the template string
e.g. assigning values to template from with in the fragment function
Foo(values){ return{ template:"<p>{txt}</p>", values:{txt:"Ipsum"} }}
- with
Html`${{Foo:{}}}`
or assigning values to template from with in the render function
Foo(values){ return{ template:"<p>{txt}</p>" }}
- with
Html`${{Foo:{txt:"Ipsum"}}}`
Note: the different is whether or not a "values" is returned from the fragment function
output
<p>Ipsum</p>
Example:
Implementation:
document// END click-me
Output:
Try Click Me
Asynchronous fragment templates
You can also return a promise as your template
property.
Rewritting the my-friends example
Example:
Implementation:
document
In this example, the values returned from the promise are used. As the "values" from a fragment function(if provided) takes priority over values passed in from render.
Output:
you have 635 friends
Styling
Supports an object as the style attribute. Compatible with React's implementation.
Example: of centering an element
{ const style= position: "absolute" top: "50%" left: "50%" marginRight: "-50%" transform: "translate(-50%, -50%)" //END style Html` center ` }//END render
Example of connecting to a data store
backbone
var user = new BackboneModel;//END Backbone.Model.extend document//END my-profile
mobx
const user = //END observable document//END my-profile
redux
document