Skip to content
master
Go to file
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 

README.md

Aris - Write HTML in JS easily.

Aris is a simple library (< 5kb minified + gzipped) that:

  • Allows you to write HTML in JS in a clean, powerful and extensible manner
    (contrary to what many people believe is impossible).
  • Lazily load JS and CSS files (it also auto-prefixes your CSS).
  • Does routing with page anchor tags (hash routing, e.g. href="#path/to/page")

Just these 3 functions alone will allow you to easily build frontend web-apps
(e.g. Single page applications, Progressive web apps) that are performant and scalable.

Aris is so simple that its whole documentation is this README.

Download

Just copy and paste the aris.min.js from this repository.
If you want to read the entire file (it's less than 1k lines), you can look at aris.js.

The Problem

Writing HTML in JS is a traditionally messy experience.

You'll have to carefully fiddle with quotes, double quotes, string concatenation, escaping characters, etc.
This breaks syntax highlighting, causes mistakes, and a LOT of stress.

It is little wonder why most people resort to backend templating solutions, or use frontend templating frameworks/libraries/transpilers.

Unfortunately, these solutions are not good enough (even JSX).
Their are usually either too complex, clunky, slow, obscure, incomplete, bloated, or inflexible.

The Solution

Imagine you want to write the following shit:

var dropdownHTML = '';
for (var i = 0; i < dropdown.values.length; ++i) {
  dropdownHTML += '<a class="dropdown-item">' + dropdown.values[i] + '</a>';
}
el.innerHTML = '<div class="dropdown">' +
  '<button class="btn dropdown-toggle"' +
  'type="button"' +
  'id="dropdownMenuButton"' +
  'data-toggle="dropdown"' +
  'aria-haspopup="true"' +
  'aria-expanded="false">' +
    dropdown.text +
  '</button>' +
  '<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">' +
    dropdownHTML +
  '</div>' +
'</div>';

Wouldn't it be better to write it like:

el.innerHTML = HTML(['div', {class: 'dropdown'},
  ['button', dropdown.text, {
    class: 'btn dropdown-toggle', 
    type: 'button', 
    id: 'dropdownMenuButton', 
    dataToggle: 'dropdown', 
    ariaHaspopup: true, 
    ariaExpanded: false
  }],
  ['div', {class: 'dropdown-menu', ariaLabelledby: 'dropdownMenuButton'},
    [dropdown.values, function (x) { 
      return ['a', {class: 'dropdown-item'}, x]
    }]
  ]
]);

Wow! Such syntax. Much clean.

Notice how the HTML is being expressed in an intermediate form with native JS objects and arrays.
(We call this intermediate form a HTML context).

This simple change makes all the difference, and opens up a whole new world of possibilities.

Functions

HTML

  • HTML(context)
    Creates a HTML string with the context.

Example:

HTML(["div", {id: "y", class: "a b", style: {color: "red"}, ariaLabel: "x"}, 
    "Text",
    ["a", {href: "example.com", target: "_blank"}, "link"],
    {style: {width: 1, opacity: 0.5}, class: "c", pos: 1},
    ['A', 'B', 'C'], function (x) { return ["div", x] }, 
    [ [0, 1, 2], function (x) { return ["span", x] } ]
])

Turns into:

<div id="y" class="a b c" style="color: red; width: 1px; opacity: 0.5"
  ariaLabel="x" aria-label="x" aria_label="x" pos="1">
    Text
    <a href="example.com" target="_blank">link</a>
    <div>A</div><div>B</div><div>C</div>
    <span>0</span><span>1</span><span>2</span>
</div>
  • If the starting element is a string, it is treated as a tag name.
    ['div', 'Text'] => <div>Text</div>

  • Attributes are added via objects.
    The object can be anywhere in the array except for the starting element.
    You can use any preferred style:
    ['div', {id: 'x'}, 'a', 'b', 'c'] OR ['div', 'a', 'b', 'c', {id: 'x'}]
    This allows you to specialize contexts by pushing classes onto them.

  • Attributes can be defined via camelCase or snake_case.
    They will automatically converted to camelCase, kebab-case and snake_case.
    This is so that you can avoid using quotes on the keys.
    {ariaLabel: "x"} => aria-label="x"

  • If the starting element is an array, the contents of the entire
    array will be converted to HTML and joined.
    [['div', 0], ['div', 1]] => <div>0</div><div>1</div>

  • Inline CSS can be defined via objects or strings.
    They will be combined in sequential order.
    Repeated CSS properties will be replaced.
    The CSS will be auto-magically prefixed.
    For numerical properties, px will be automatically added if where applicable. (similar to jQuery).
    ['div', {style: {opacity: 0, width: 2}}, 'x', {style: "opacity: 1; filter: grayscale(100%)"}] =>
    <div style="opacity: 1; width: 2px; -webkit-filter: grayscale(100%); filter: grayscale(100%)">x</div>

  • Classes are joined with spaces if repeated in an object.
    ['div', {class: 'a'}, 'x', {class: 'b'}] => <div class="a b">x</div>

  • Other attributes are replaced if repeated in an object.
    ['div', {id: 'a'}, 'x', {id: 'b'}] => <div id="b">x</div>

  • If an element is an array, and the next element is a function,
    the array will be automatically mapped to the function.
    ['div', [1,2,3], function (x) { return x*2 }] => <div>246</div>
    ['div', [[1,2,3], function (x) { return x*2 }] ] => <div>246</div>

Other Functions

Escaping HTML special characters

  • HTML.escape(text)
    Returns the chunk of text with special HTML characters (<>?"') escaped.

    To allow HTML to be used in text, Aris does not auto-escape special HTML characters.
    Please use this function to manually escape the characters where intended.

HTML Boolean Attributes

  • HTML.bool("checked", true) => {"checked": "checked"}
    HTML.bool("checked", false) => ""
    HTML boolean attributes are specified as false by totally omitting it, as such, we created a helper function to make this easier to write.

HTML Output Key Order and Hash

  • HTML(['a', {href: 'x.com', id: 'link'}, 'x']) => <a href="x.com" id="link">x</a>
    HTML(['a', 'x', {id: 'link', href: 'x.com'}]) => <a href="x.com" id="link">x</a>
    The HTML output is deterministic, with attribute keys sorted in ascending order.

  • HTML.hash(['a', {href: 'x.com', id: 'link'}, 'x']) => 841135124
    HTML.hash(['a', 'x', {id: 'link', href: 'x.com'}]) => 841135124
    HTML.hash(HTML(['a', 'x', {id: 'link', href: 'x.com'}])) => 841135124
    HTML.hash('some string') => -984100687
    HTML contexts and strings can be hashed to 32-bit integers for compact storage and quick comparison.

Lazy Loading

  • HTML.load(file0, file1, ...).done(fn)
    Loads (.js, .css) files, then execute the done function fn (optionally).
    The files are downloaded asynchronously in parallel, but attached to the webpage in the specified order.
    Each file will be only loaded once.
    The done function is always executed once per load call,
    irregardless of whether the files have been loaded previously.

    The files are treated accordingly with their file extension.
    To force a file to be treated as a JS or CSS file, prefix the url with (js: or css:):
    js: js/main (whitespace around the : is ignored)

    CSS files will be auto-magically prefixed.

    Lazily loaded JS can be debugged easily in modern browsers,
    as we auto-prepend the sourceURL directive to the JS files.

Hash Routing

  • HTML.route("#path/to/page/anchor", fn)
    Attaches the function fn to #path/to/page/anchor.

  • HTML.route.go("#path/to/page/anchor")
    Executes the function attached to #path/to/page/anchor.

  • HTML.route.go("#path/to/page/:anchor")
    Attemps to execute the function attached to the path.
    The prefix : on the path component denotes that it is is default option.
    If the visitor has visited #path/to/page/one, or if the address bar points to #path/to/page/one, it will execute the function attached to #path/to/page/one.
    Otherwise, it will execute the function attached to #path/to/page/anchor.

  • HTML.route.go("#:path/:to/:page")
    You can prefix any path component with ":" to mark it as the default option.

  • HTML.route.go()
    Attempts to execute the function attached to the path in the address bar.
    (i.e. window.location.hash)

SVG (for the artsy coders)

Example:

var SVG = HTML.SVG, P = HTML.SVG.Path;
HTML(SVG(30, 30, 
   ['circle', {class: 'frame', cx: 15, cy: 15, r: 12}],
   P({class: 'hand hour'}).M(15,15).L(20,15),
   P({class: 'hand minute'}).M(15,15).L(15,2),
   P().M(0,0).L(1,1),
   P.M(0,0).L(1,1),
   ['text', {x: 0, y: 0}, 'Path can be also be called without args!']
))

Is equivalent to:

HTML(['svg', {xmlns: 'http://www.w3.org/2000/svg',
  width: '30px', height: '30px', viewBox: '0 0 30 30'},
    ['circle', {class: 'frame', cx: 15, cy: 15, r: 12}],
    ['path' {class: 'hand hour', d: 'M15,15 L20,15'}],
    ['path' {class: 'hand minute', d: 'M15,15 L15,2'}],
    ['path' {d: 'M0,0 L1,1'}],
    ['path' {d: 'M0,0 L1,1'}],
    ['text', {x: 0, y: 0}, 'Path can be also be called without args!']
])

Which turns into:

<svg xmlns="http://www.w3.org/2000/svg"
  width="30px" height="30px" viewBox="0 0 30 30">
    <circle class="frame" cx="15" cy="15" r="12"></circle>
    <path class="hand hour" d="M15,15 L20,15"></path>
    <path class="hand minute" d="M15,15 L15,2"></path>
    <path d="M0,0 L1,1"></path>
    <path d="M0,0 L1,1"></path>
    <text x="0" y="0">Path can be also be called without args!</text>
</svg>

You can even use this to create complex animated SVGs. ;)

Why use Aris?

Just use Aris. Seriously.

Aris will save you precious time, effort, and brain space.

Your code will be shorter, cleaner, and more performant.

Advantages

  • Just plain old JS.
  • Zero dependencies.
  • No tooling needed.
  • Easy. Learn once, use forever.
  • Automagic CSS prefixing.
  • Fast.

Performance

Aris performs with so little overhead, it's as if you have written that HTML and CSS in plain text.

Support

Aris is actively maintained and constantly tested against all major browsers.
If you have any suggestions, questions, or bug reports, we will be very glad to help.

FAQ

  • How does Aris help me create high-performance user interfaces?

    Our approach to speed is simply to allow easy creation of complex HTML, with minimal overhead.
    Instead of changing the contents of many DOM elements, one after another,
    you will just create and set the combined HTML in one go.

    This minimizes reflows.

    If you know what you are doing, this will be faster than DOM-diffing.

    Because Aris is just plain old Javascript, all HTML creation is extremely close to the metal,
    fully leveraging the native optimized compiled machine code of the JS engine.

    This is much faster than using templating systems based on regex.

  • Why another JS library/framework? How does this compare to <insert name here>?

    The existing solutions in the JS community are simply not good enough.
    Many of them require you to call functions everywhere and remember to provide the correct arguments in order.
    Many of them pollute the namespace of short variable names unnecessarily.
    Many of them require tooling to work well.
    Many of them are just not good enough.

    Just use Aris and you will quickly understand why.

    We adopt a no-compromise approach for Aris. Every part is meticulously and purposefully crafted.
    We aim for a minimal code size, but not at the cost of completeness and ease of use.

  • What does Aris stands for?

    Aris stands for "A Revolution In Syntax".

    We initially wanted to name our library html.js.
    However, when we tried to publicize our library on npm, we found that most of the good short names
    have been already taken by name-squatters or poor-quality libraries.

    Hence, have choosen to settle with another short but memorable name!

    If there is a namespace collision, you can use aris instead of HTML.
    HTML => aris
    HTML.SVG => aris.svg
    HTML.SVG.Path => aris.svg.path

License

MIT

About

Aris - A fast and powerful tool to write HTML in JS easily. Includes syntax highlighting, templates, SVG, CSS autofixing, debugger support and more...

Topics

Resources

Releases

No releases published

Packages

No packages published
You can’t perform that action at this time.