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:
- 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).
- 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... - childrens to append to the element.
All other arguments are considered children to be appended to the element,
using
appendChild
if they'reNode
s orinsertAdjacentHTML
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 ofReact.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 bind
ing 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:
- injected as HTML, sanitised to not inject malicious elements
- set as properties on the element, without polluting its prototype
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