npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

html-script

v0.2.1

Published

Create DOM nodes (real or virtual) using a concise API, similar to hyperscript

Downloads

5

Readme

html-script

Create DOMs (real or virtual) using a concise API, similar to hyperscript.

This is a general purpose utility that uses adapters to generate the backing structure, if you specifically want to work with the real browser DOM using the same API syntax, see dom-script.

Example

'use strict'

const H = require( 'html-script' )

const {
  document, documentType, text, comment, documentFragment, element,
  html, head, body, meta, title, div, p, strong, input
} = H

const dom =
  document(
    documentType('html'),
    html(
      head(
        meta({charset:'utf-8'}),
        title('Hello World!')
      ),
      body(
        comment('Whose line is it anyway?'),
        div({id:'main'},
          p('The quick brown fox jumps over the ',strong('lazy dog')),
          input({type:'text',name:'firstName',placeholder:'Alex'})
        ),
        comment('Fragment not (usually) necessary but make sure it works'),
        documentFragment(
          comment('Text not necessary but etc.'),
          p(text('lol '),'wut')
        ),
        comment('But what if it is not in the spec?'),
        element('customtag',{class:'kk'},
          p('OK that works for me')
        )
      )
    )
  )
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Hello World!</title>
  </head>
  <body>
    <!--Whose line is it anyway?-->
    <div id="main">
      <p>The quick brown fox jumps over the <strong>lazy dog</strong></p>
      <input type="text" name="firstName" placeholder="Alex" />
    </div>
    <!--Fragment not (usually) necessary but make sure it works-->
    <!--Text not necessary but etc.-->
    <p>lol wut</p>
    <!--But what if it is not in the spec?-->
    <customtag class="kk">
      <p>OK that works for me</p>
    </customtag>
  </body>
</html>

Default adapter

Because it is a general purpose utility, html-script ships only with a very simple adapter over a virtual DOM that follows the same structure as JsonML:

const myDiv =
  div( { id: 'myDiv' },
    p( 'Hello world!' )
  )

console.log( JSON.stringify( myDiv ) )
["div",{"id":"myDiv"},["p","Hello world!"]]

Other implementations include dom-script, which generates real DOM nodes in the browser, and mojule-h, which generates nodes for the mojule-dom virtual DOM.

Installation and usage

npm install html-script

const H = require( 'html-script' )

// now destructure out the functions you want - alternatively, use H.div etc.
const { div, p, comment, element } = H

// any objects passed will be treated as attributes
// <div id="main"></div>
const main = div( { id: 'main' } )

// any strings passed will be treated as text nodes
// <p>Hello world!</p>
const hello = p( 'Hello world!' )

// non-element nodes also supported
// <!--Hello world!-->
const helloComment = comment( 'Hello world!' )

// any nodes passed will be appended to the parent
// <div><p>Hello</p><p>World</p></div>
const nested =
  div(
    p( 'Hello' ),
    p( 'World' )
  )

// if html-script doesn't have the element you want
// <custom id="myCustom">Hello world!</custom>
const custom =
  element( 'custom', { id: 'myCustom' },
    'Hello world!'
  )

Attributes

An object passed to an html-script function is treated as though it were an attribute map for the node. For the most part, it is expected to be a simple map of attribute name to attribute value, and the value is expected to be a string, with some exceptions listed below.

const nameField =
  div(
    label( { for: 'firstName' }, 'First Name' ),
    input( { type: 'text', name: 'firstName' } )
  )
<div>
  <label for="firstName">First Name</label>
  <input type="text" name="firstName" />
</div>

boolean attributes

To make working with boolean attributes easier, any attribute that has a boolean value will be treated as though the boolean attribute is present on the node if the value is true, and absent if the value is false:

div(
  input( { type: 'radio', checked: true } ),
  input( { type: 'radio', checked: false } )
)
<div>
  <input type="radio" checked />
  <input type="radio" />
</div>

style

Either a string, or an object of name value pairs:

div(
  p( { style: 'font-family: sans-serif' }, 'Hello' ),
  p( { style: { 'font-family': 'sans-serif', 'font-size': '1rem' }, 'World' )
)
<div>
  <p style="font-family: sans-serif">Hello</p>
  <p style="font-family: sans-serif; font-size: 1rem;">World</p>
</div>

data

An attribute named data with an object value will be treated similarly to the dataSet property on DOM nodes, that is, the object keys will be converted from camelCase to dash-style with a data- prefix. This makes it easy to use your existing models to set data attributes without having to first mangle the names:

div( { data: { firstName: 'Nik', lastName: 'Coughlin' } } )
<div data-first-name="Nik" data-last-name="Coughlin"></div>

functions

If an attribute value is a function and the name starts with 'on', it's considered to be an event handler. Note that the default adapter is a JsonML implementation which contains data only, so events don't make sense and they'll just be removed, but other adapters can handle these as they see fit, see dom-script for an example.

div( { onclick: e => window.alert( 'Clicked!' ) }
  p( 'Hello world!' )
)

other types

All other values are converted to a string via String( value )

Adapters

You can get an instance of html-script that uses your custom adapter by calling it as a function with your adapter as the parameter:

const H = require( 'html-script' )
const adapter = require( './path/to/your/adapter' )

const Mine = H( adapter )

const {
  document, documentType, text, comment, documentFragment, element,
  html, head, body, meta, title, div, p, strong, input
} = Mine

const dom =
  document(
    documentType('html')
    // etc.
  )

An adapter is an object containing the following functions (where Node is your custom backing type). Type notation below should be self explanatory, it's similar to rtype / typescript et al.

{
  isNode: ( node:Node ) => Boolean,
  createElement: ( tagName:String ) => elementNode:Node,
  createText: ( value:String ) => textNode:Node,
  appendChild: ( node:Node, child:Node ) => Void,
  addAttributes: ( node:Node, attributes:Object ) => Void,
  createDocument: () => documentNode:Node,
  createDocumentType: ( name:String, publicId:String?, systemId:String? ) => documentTypeNode:Node,
  createComment: ( value:String ) => commentNode:Node,
  createDocumentFragment: () => documentFragmentNode:Node,
  addEventListener: ( node:Node, name:String, listener:( e:Event? ) => result:Boolean ) => Void
}

See the built in JsonML adapter for an example of how adapters work.

addEventListener is optional and only needs to be implemented by adapters for which is makes sense, eg an adapter over the real DOM, and doesn't need to be implemented in for example adapters that just back a data-only structure.

html-script expects createDocument to return a document node with no children! Note that the default DOM implementation takes a title and adds various children like an html tag element etc. - if you are backing this with the real DOM, you will have to clear out all the children before returning the node.

Using JsonML with other adapters

Because JsonML is a convenient format for transportation and persistence, a helper method is provided to populate your custom backing adapter from JsonML data:

const H = require( 'html-script' )
const adapter = require( './path/to/your/adapter' )
const jsonML = require( './path/to/some/data.json' )

const Mine = H( adapter )

const dom = Mine.fromJsonML( jsonML )