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

domjure

v1.0.0

Published

Conjure DOM elements with ease

Downloads

4

Readme

DOMjure

Conjure DOM elements with ease! Configure your element in a single function call, instead of multiple lines of document.createElement, .setAttribute, .appendChild, ...:

import {createElement} from 'domjure'

const link = createElement('a', {class: 'text-xl', href:'/'}, 'Home')

// Instead of:
//
// const link = document.createElement('a');
// link.setAttribute('class', 'text-xl');
// link.href='/';
// link.innerHTML = 'Home'

Table of contents

Usage

Basic usage

The function accepts the following arguments:

  1. the tagName for the new element or an existing element. This is the only mandatory argument. If providing an existing element, the function will not create a new element, but apply any properties, attributes,... to it and append any children (Note: It's left to the caller to clear any existing children).
  2. the attributes or properties to set on the element. Can be null, as not all element creation needs to set attributes or properties. By default, the library will set the value as a property if its name is in the element's prototype, and fallback to setting it as attribute otherwise. You can configure how the function sets each item on the element. This will allow you to create shorthands, filter what can be assigned...
  3. childrens to append to the element. All other arguments are considered children to be appended to the element, using appendChild if they're Nodes or insertAdjacentHTML if they're text. Like the setting of attributes/properties, you can configure how the function adds each child to the element.
import {createElement} from 'domjure'

createElement('a', {class: 'text-xl', href:'/'}, 'Home')

With its aim being to reduce boilerplace, the library also accepts the following shapes of calls.

// No properties or attributes
createElement('a', 'Home')

// Ignores false-y options
createElement('a', null, 'Home')

// One or multiple children, text or Nodes. Ignores falsey values
createElement('article', {class: 'article-card'}, [
  createElement('h1', 'The heading'),
  '<p>... HTML from a CMS, for ex.</p>'
])

// Multiple children as rest parameters
createElement(
  'article', 
  {class: 'article-card'}, 
  createElement('h1', 'The heading'),
  '<p>... HTML from a CMS, for ex.</p>'
)

Creating SVG or MathML elements

By default, the library creates HTMLElement. You can use the namespaceURI option to make it create SVGElement:

import {createElement, NAMESPACE_URI_SVG} from 'domjure'
// Creating `SVGElement`s
createElement('path', {namespaceURI: NAMESPACE_URI_SVG, d: '...'})

The NAMESPACE_URI_MATHML will let you create MathML elements the same way.

More syntax sugar

If this still feels too verbose, even after aliasing createElement to a shorter name (say h for consistency with other libraries creating elements), there are a couple of options.

htm

createElement is compatible with the htm library, letting you write your markup as tagged template litterals.

import {createElement} from 'domjure'
import htm from 'htm'

const html = htm.bind(createElement);

const classes = "text-xl"
const otherAttributes = {id: 'an-id'}
const element = html`<div class=${classes} ...${otherAttributes}>
    <h1>A heading <span>with HTML</span></h1>
    <p>A paragraph</p>
  </div>`

JSX

htm places the burden of converting the markup to createElement calls to the browser running your code. Using JSX, you can pre-compile that markup into createElement calls ahead of time, saving your users that overhead:

  • set up Babel to pre-process your JSX file using its React preset.
  • prefix your file with /* @jsx createElement */, which will tell Babel to use DOMjure's createElement instead of React.createElement when converting JSX tags into JavaScript calls
/* @jsx createElement */
import {createElement} from 'domjure'

const classes = "text-xl"
const otherAttributes = {id: 'an-id'}
const element = <div class=${classes} {...otherAttributes}>
    <h1>A heading <span>with HTML</span></h1>
    <p>A paragraph</p>
  </div>

Customisation

The library comes with default ways to assign properties and attributes on the element, and to append children to it.

You can provide custom ones by binding the createElement function to an object with a setOnElement and/or addToElement function to do the properties/attributes assignment your own way.

Properties and attributes settings

You can bind your own setOnElement function to createElement. It'll receive:

  • the element that's being created
  • the name of the attribute being set
  • the value of that attribute
  • the attributes being set
// There are a few helpers you may find useful in the `domjure/dom` 
// (or `domjure/src/dom.js`, depending on your bundler support for
// `package.json`'s `export`'s field)
import {createElement} from 'domjure';
import {setAttributes, setStyle} from 'domjure/src/dom.js';

const customSetters = {
  // Abstracts the attributes necessary to make
  // the element a toggle for a Bootstrap modal
  bootstrapModalToggle(element, name, value) {
    // You could add a check that the element is actually
    // a button as well
    setAttributes(
      element,
      {
        toggle: 'modal',
        target: `#${value}`,
      },
      'data-bs-',
    );
  },
  // Provides a shorthand for setting aria attributes
  aria(element, name, value) {
    setAttributes(element, value, 'aria-');
  },
  // Provide support for setting the element styles as both string or object
  style(element, name, value) {
    setStyle(element, value);
  },
  // Provide a shorthand for setting data attributes
  data(element, name, value) {
    setAttributes(element, value, 'data-');
  },
};

function customSetOnElement(element, name, ...args) {
  if (Reflect.has(customSetters, name)) {
    return customSetters[name](element, name, ...args);
  }

  // Fallback to the default implementation if none is found
  return setOnElement(element, name, ...args);
}

const customCreateElement = createElement.bind({
  setOnElement: customSetOnElement,
});

const result = customCreateElement('button', {
  bootstrapModalToggle: 'id-of-a-modal',
  aria: {
    disabled: true,
  },
  style: {
    '--elevation': '0.25rem'
  },
  data: {
    'app-ignore-ctrl-click': '',
  },
  // This will fallthrough to the library's default implementation
  type: 'button',
});

Child addition

You can bind your own addToElement function to createElement. It'll receive:

  • the element that's being created
  • the child to be added
  • the index of the child in the list of all children being added to the element
  • the children being added to the element
import {createElement} from 'domjure';

function customAddToElement(element, child, index, ...args) {
  // Clean up the current HTML before inserting the first node
  if (index == 0) {
    element.textContent = '';
  }

  // Allow to create elements from an object structure
  if (typeof child == 'object' && !(child instanceof window.Node)) {
    element.appendChild(
      customCreateElement(
        child.tagName,
        child.attributes,
        ...(child.children || []),
      ),
    );
  } else {
    addToElement(element, child, ...args);
  }
}

const customCreateElement = createElement.bind({
  addToElement: customAddToElement,
});

// Create an element with existing content to check the cleanup
const existingElement = window.document.createElement('article');
existingElement.innerHTML = '<p>Some content already in there</p>';

const article = customCreateElement(
  existingElement,
  null,
  // This'll be using the custom element creation
  {
    tagName: 'h1',
    children: 'The title of the article',
  },
  // This will fall through too the default implementation
  '<p>Some content, from a CMS, for ex.</p>',
);

Safety warning

This library leaves it to you to ensure the parameters you pass are ready to be:

Requirements

The library code uses features from ES2018, as well as the Reflect API.

If a browser you support doesn't support these API, you can transpile the code and/or polyfill the missing APIs.

Installation

The library is published on npm, which means you can grab it with your favourite package manager

# For npm
npm install domjure
# For yarn
yarn add domjure
# For pnpm
pnpm add domjure

For a quick test, you can also directly import the content in a script tag from one of the many JavaScript CDNs out there. Make sure you self-host the file once you move to working on your actual project though.

Contributing

See CONTRIBUTING.md