0.8.1 • Published 9 months ago

straylight v0.8.1

Weekly downloads
4
License
MIT
Repository
github
Last release
9 months ago

Straylight

A templating and rendering library for HTML.

Table of Contents

About

As a web developer, how can I dynamically update an HTML document in a way that is safe, simple, readable, and efficient?

🤔

Over the years, web developers have approached this problem in many different ways:

  • innerHTML
  • JQuery's manipulation methods
  • Underscore templates
  • Complex two-way binding frameworks
  • React combined with JSX
  • And so on...

With the introduction of template literals, we finally have the opportunity to express HTML directly in Javascript without an offline compilation process.

😌

Straylight is a small (about 4kb, minified and gzipped), self-contained library that solves the HTML update problem by providing:

  • A Javascript template tag for rendering HTML fragments
  • An update engine for applying a series of template results to the HTML document over time

Let's get started!

Installing

Install with NPM:

npm install straylight

and import:

import { html, applyTemplate } from 'straylight';

Or download from a CDN:

<script type='module'>

import { html, applyTemplate } from 'https://unpkg.com/straylight/dist/straylight.js';

</script>

Guide

Hello World

First, you might want to review the concept of tagged template literals. A template literal tag is a function that is called with the provided template and template values.

Straylight's html function is a template literal tag. You can create a template result with the html tag and then apply that template result to the document using the applyTemplate function:

<div id='mount'></div>
<script type='module'>

import { html, applyTemplate } from 'https://unpkg.com/straylight/dist/straylight.js';

window.onload = () => {
  const world = 'Earth';

  applyTemplate('#mount', html`
    <h1>Hello ${world}</h1>
    <p>How are you?</p>
  `);
};

</script>

The applyTemplate function takes an HTML element or a CSS selector as the first argument and a template result as the second argument.

A Simple Clock

When you apply the same template to a particular HTML container, the HTML tree is updated rather than recreated from scratch.

In the example below, we render a clock and then update the clock's display every second:

<div id='clock'></div>
<script type='module'>

import { html, applyTemplate } from 'https://unpkg.com/straylight/dist/straylight.js';

window.onload = () => {
  function renderTime() {
    return html`
      <span>${new Date().toLocaleString()}</span>
    `;
  }

  // Render the clock for the first time
  applyTemplate('#clock', renderTime());

  // Update the clock once every second
  setInterval(() => {
    applyTemplate('#clock', renderTime());
  }, 1000);
};

</script>

That should give you an idea of what Straylight is all about!

🚀

From here on, we'll skip the HTML examples and just show Javascript modules.

Nested Templates

Templates can be nested within other templates:

import { html } from 'straylight';

function app() {
  return html`
    ${header()}
    <main></main>
    ${footer()}
  `;
}

function header() {
  return html`
    <header>
      <h1>Application Title</h1>
      <nav>Lots of links</nav>
    </header>
  `;
}

function footer() {
  return html`
    <footer>
      Links and small grey text
    </footer>
  `;
}

By nesting templates, we can compose larger applications from smaller components.

Lists

In addition to single values, we can supply an array:

import { html } from 'straylight';

const planets = [
  'Mercury',
  'Venus',
  'Earth',
  'Mars',
  'Jupiter',
  'Saturn',
  'Uranus',
  'Neptune',
];

function renderPlanets() {
  return html`
    <h2>The Planets</h2>
    <p>Our solar system contains eight planets:</p>
    <ul>
      ${planets.map(name => html`<li>${name}</li>`)}
    </ul>
  `;
}

We can also supply an iterable:

import { html } from 'straylight';

function *fruitItems() {
  yield html`<li>Apples</li>`;
  yield html`<li>Pears</li>`;
  yield html`<li>Bananas</li>`;
}

function renderFruit() {
  return html`
    <h2>Fruits</h2>
    <ul>${fruitItems()}</ul>
  `;
}

Async Iterators

Straylight has built-in support for async iterators. If an async iterator is supplied as a template value, then the document will be updated each time a new value is available.

Here is the clock example again, implemented with an async generator function:

import { html } from 'straylight';

async function *generateTime() {
  while (true) {
    yield new Date().toLocaleString();
    await new Promise(r => setTimeout(r, 1000));
  }
}

function renderClock() {
  return html`
    <div class='clock'>
      ${generateTime()}
    </div>
  `;
}

Attributes

Template values can be used to update element attributes:

import { html } from 'straylight';

function renderWithClass(className) {
  return html`
    <div class=${className}>Content</div>
  `;
}

Template values can also be used to update only a part of an attribute value:

import { html } from 'straylight';

function renderWithAddedClass(className) {
  return html`
    <div class='avatar ${className}'>Content</div>
  `;
}

Property Collections

In some situations you might want to assign a value to an element property instead of an attribute. A collection of property values can be supplied as an object:

import { html } from 'straylight';

function usernameInput() {
  const properties = {
    id: 'username-input',
    type: 'text',
    name: 'username',
    autocomplete: false,
    className: 'rounded'
  };
  return html`<input ${properties} />`;
}

Property collections can be used to attach event handlers to elements:

import { html } from 'straylight';

function formButton() {
  function onButtonClick() {
    console.log('button clicked!');
  }
  return html`
    <button ${{ onclick: onButtonClick }}>Go</button>
  `;
}

SVG

SVG can be included directly within html tags.

import { html } from 'straylight';

function defaultAvatar() {
  return html`
    <svg width="24" height="24" viewBox="0 0 24 24" fill="#4285f4">
      <path d="
        M12,0C5.376,0 0,5.376 0,12C0,18.624 5.376,24 12,24C18.624,24 24,18.624
        24,12C24,5.376 18.624,0 12,0ZM12,20.64C9,20.64 6.348,19.104
        4.8,16.776C4.836,14.388 9.6,13.08 12,13.08C14.388,13.08 19.164,14.388
        19.2,16.776C17.652,19.104 15,20.64 12,20.64ZM12,3.6C13.992,3.6 15.6,5.208
        15.6,7.2C15.6,9.192 13.992,10.8 12,10.8C10.008,10.8 8.4,9.192
        8.4,7.2C8.4,5.208 10.008,3.6 12,3.6Z" />
      <path d="M0 0h24v24H0z" fill="none" />
    </svg>
  `;
}

Differences From HTML

There are a couple of differences between normal HTML and the HTML you can write inside of Straylight html tags.

First, you can use self-closing tag syntax for any element to keep your code tidy:

import { html } from 'straylight';

function useSelfClosing() {
  return html`
    <div id='first' />
    <div id='second' />
    <div id='third' />
  `;
}

Second, you must use self-closing tag syntax for all HTML "void" tags, like <input> and <br>:

import { html } from 'straylight';

function voidTags() {
  return html`
    <!-- Good! -->
    <input type='text' />
    <br />
    <!-- BAD! -->
    <input type='text'>
  `;
}

Third, only the following HTML named character references are supported:

  • &lt;
  • &gt;
  • &amp;
  • &quot;

Decimal and hexidecimal character references (like &#x1f4a1;) are fully supported.

API Reference

html`template`

A template tag that returns TemplateResult objects.

import { html } from 'straylight';

const result = html`<div>${'hello'}</div>`;

// Prints: ['hello']
console.log(result.values);

applyTemplate(element, templateResult)

Applies a template result to an HTML container element. The element argument can be a DOM Element object or a CSS selector.

import { html, applyTemplate } from 'straylight';

applyTemplate('#mount', html`
  <div>Hi!</div>
`);
0.8.1

9 months ago

0.8.0

1 year ago

0.7.0

1 year ago

0.6.0

1 year ago

0.5.0

4 years ago

0.4.1

4 years ago

0.4.0

4 years ago

0.3.13

4 years ago

0.3.12

5 years ago

0.3.11

5 years ago

0.3.10

5 years ago

0.3.9

6 years ago

0.3.8

6 years ago

0.3.7

6 years ago

0.3.6

6 years ago

0.3.5

6 years ago

0.3.4

6 years ago

0.3.3

6 years ago

0.3.2

6 years ago

0.3.1

6 years ago

0.3.0

6 years ago

0.2.4

6 years ago

0.2.3

6 years ago

0.2.2

6 years ago

0.2.1

6 years ago

0.2.0

6 years ago

0.1.11

7 years ago

0.1.10

7 years ago

0.1.9

7 years ago

0.1.8

7 years ago

0.1.7

7 years ago

0.1.6

7 years ago

0.1.5

7 years ago

0.1.4

7 years ago

0.1.3

7 years ago

0.1.2

7 years ago

0.1.1

7 years ago

0.1.0

7 years ago