If you are seeking an even lighter version (e.g., for inclusion in a stand-alone library) while still getting some benefits of the syntax-highlighter-friendly pure JS approach for DOM construction, see Jamilih Lite.
separation_of_concerns !== separation_of_syntaxes!
One can very legitimately build HTML DOM using Jamilih as with any other
isn't suitable for building structural and styling design logic. On
the contrary, it provides flexibility for designers to utilize their
the designer can maintain the discipline to avoid adding business
logic. (A future "JSON mode" should allow more security but less control.)
The following functions are available:
jml()- For building DOM objects (and optionally appending into an existing DOM node). Arguments demoed and explained below.
configis an object and which supports a
stringOutputproperty which can be set to
truein order to JSON-stringify the converted Jamilih JSON object. Note that element results will be in array form.
jml.toJMLString(objOrString, config)- Works like
jml.toJMLbut stringifies the resulting Jamilih object.
jml.toHTML()- Works like
jml()except that the resulting DOM object is converted into an HTML string.
jml.toXML()- Works like
jml()except that the resulting DOM object is converted into an XML-serialized string.
jml.toDOM()- An alias for
jml.toDOMString()- An alias for
jml.toHTML()(for parity with
jml.toXMLDOMString()- An alias for
jml.toXML()(for parity with
jml.weak(obj, ...args)- Returns a two-item array with the first item as a new
jml.WeakMapobject on which an association is made between
objand a Jamilih element created out of passing
jml()and the second item is the new Jamilih elemnet
jml.strong(obj, ...args)- Same as
jml.weakbut creates a new
jml.Mapobject instead of a
WeakMapsubclass with an
invokemethod that should be passed a DOM element (such as one created by
jml.weak()), the name of a method to invoke (on an object previously associated with the supplied element (e.g., via
jml.weak())), and any number of optional arguments to be supplied to that method. The user method will have its
thisvalue set to that of the previously associated object and in addition to accepting the arguments supplied to
invoke, it will have the element itself supplied as the first argument. This class also has its
setmethods enhanced to accept a string selector to represent the element used to find the associated object.
jml.Map()- Same as
jml.WeakMapbut is a subclass of
Simply access the above methods via the global
npm install jamilih
const jml = require('jamilih');
const input = ;
Simple element with attributes...
const input = ;
Simple element with just child elements...
const div =;
Simple element with attributes and child elements...
const div =;
Simple element with attributes, child elements, and text nodes...
const div =;
const simpleAttachToParent = ;
Returning first element among siblings when appending them to a DOM element (API unstable)...
const firstTr =;
Returning element siblings as an array (API unstable)...
const trsFragment =;
Inclusion of regular DOM elements...
const div =;
Document fragments addable anywhere within child elements...
const input =;
const input2 =;
The events attached via
$on are added through
Comments, processing instructions, entities, decimal and hexadecimal character references, CDATA sections...
const div =;
Namespace definitions (default or prefixed)...
$shadow property can be added to an element to attach Shadow DOM content.
Its allowable properties include:
true. May also be set in place of
content(with the same allowable values) to serve as the shadow DOM contents.
open. Defaults to
false. May also be used (as with
open) to directly build the contents (see
templateis not present, this optional array of arguments will be passed as fragment contents to
jml()for direct attachment to the shadow root of this element. May also be set to a string or DOM element in which case, it is passed to
jml()as the first argument (the element or element name).
templatemay optionally be present to indicate a template for cloning. If
templateis a string selector or a DOM
<template>element, the indicated element will be cloned and added as the shadow root contents. If
templateis an array, its contents will be passed to
jml()for first creating a
<template>element, and then it will be appended to the document body, and then it will be cloned for use with the shadow DOM. If the first (or only) item in the array is a regular object, these will become the attributes of the
<template>element while the subsequent item in the array will be passed as the template children. If the first item is not a regular object, the whole array will be assumed to represent the
<template>children (without attributes).
One may attach functions or objects to elements via a
which accepts a two-item array, with the first item either being a string
to be used with
Symbol.for() or a
Symbol instance, and the second
item being the function or object. If a function is supplied, its
will be set to the element on which the symbol was added, while if an
object is supplied, its
this will remain as the object itself, but an
elem property will be added to the object which can be used to get the
element on which the symbol was added. If you do not wish to add such a
reference, consider using a symbol with
// Then elsewhere get and use the symbol function for the DOM objectSymbol'arg1';// Or using the `jml.sym` utility (accepting selector or// DOM element as first argument):jml'arg1';jml'arg1';
Or using an example with a (private)
Symbol instance and
an object instead of a function:
const privateSym = Symbol'a private symbol';;// Obtaining the element with symbol or using the utility:privateSym;jml;
Symbol attachment is particularly convenient for templates where you wish to keep a lot of inline children (avoiding defining the children separately, adding the symbol to the variables, and then reassembling them together) and without the overhead of defining a custom element.
For attachment of custom properties (or setting of standard properties) to an element, supply
an object with the desired properties (including symbols) to
The advantage of this approach is that one doesn't need to manage symbols, maps, or define elements,
this works as expected to refer to the element (including the other properties on the
object which will also be added to the element instance), but one disadvantage is that the
properties (like methods) will be added to each instance of the element rather than to a prototype.
(In such a case, you can extend, the relevant
HTMLElement interface like
The object properties could also conflict with future methods added to the built-in element.
const mySelect =;console;console;
While symbols are somewhat more convenient to use, you may wish to
associate elements with any number of
and take advantage of those objects' methods (or our enhanced
version of these methods
(TODO: Adapt examples from tests)
While there is some extra overhead to creating a custom element (in
terms of performance at registering an element and for the need to
give a unique name), among other benefits, custom elements allow
its methods to have
this not only reference the element, but also
to call other custom methods on the element in the same manner (unlike
the approach we use with maps and symbols).
You have a number of options.
You may supply an object to have its prototype copied (onto
const myEl =;console;
You may supply a (plain) function to be used within a
constructor (it will be executed after a call to the dynamically-created class'
You may supply a class (though it must extend
HTMLElement and invoke
per (autonomous) custom element requirements).
It may be an inline class expression or a reference to a class declaration.
You may supply a two-element array with the function (or class) and prototype methods.
let constructorSetVar4;const myel4 =;console;myel4;myel4;
jml()must be either:
nullas the last argument.)
!followed by a string to create a comment
&followed by an HTML entity reference (e.g.,
#followed by a decimal character reference as a string or number, e.g.,
#xfollowed by a hexadecimal character reference as a string, e.g.,
?followed by a processing instruction target string and string value (XML)
'![followed by CDATA content as a string (XML), e.g.,
&test <CDATA> content
#indicating a document fragment; see array children below for allowable contents of this array.
$textset to a string to create a bare text node (this is only necessary if one wishes jml() to return a sole text node; otherwise, text nodes are created with simple strings belonging to an element's children array).
$aset to an array of attribute name-value arrays (this is only necessary if one requires and the environment allows a fixed attribute order but may not support first-declared-first-iterated for-in object iteration).
$documentset to an object with properties
childNodesand, where present, a child object
standAlone. In place of
childNodes, one may instead add to any of the array properties,
body. One may also add a string
titleproperty in which case, a
<head>will be automatically created, with a
<meta charset="utf-8"/>element (as expected by HTML5) and a
<title>element, and any additionally supplied
headarray items appended to that
titleare supplied, an empty "html" DOCTYPE will be auto-created (as expected by HTML5) as well as an
<html>element with the XHTML namespace. If
headis supplied, a
<meta charset="utf-8">will also be added as the first child of
$DOCTYPEobject with properties
name, and, where present,
internalSubsetis not currently supported in
$attributeset to an array of a namespace, name, and value (for a namespaced attribute node) or a two-item name-value array for a non-namespaced attribute node.
$NOTATIONset to an object with properties
$ENTITYset to an object with the properties
encodingfor an external parsed entity with a declaration present) and
$onexpects a subject of event types mapped to a function or to an array with the first element as a function and the second element as a boolean indicating whether to capture or not.
indeterminate), making them useful in templates as they can be set with a variable, and if falsey (including
undefined), they will be unset (rather than would be the case with
setAttributewhich would always set them if present).
htmlForare also provided to avoid the need for quoting the reserved keywords
onfollowed by any string will be set as a property (for events).
xmlnsfor namespace declarations (not needed in HTML)
datasetis a (nestable) object whose keys are hyphenated or camel-cased properties used to set the dataset property (note that no polyfill for older browsers is provided out of the box)
#with an array of children (following these same rules) as its value to represent a fragment. (Useful if embedding the return result of a function amidst other children.)
nullis currently undefined behavior and should not be used; these may be allowed for some other purpose in the future, however.
jml()is usually the parent node to which to append the contents, with the following exceptions:
null(at the end) will cause an element or fragment to be returned
nullis the last argument, in which case, it returns a fragment of all added elements or, if only one element was present, the element itself.
The only work which comes close to meeting these goals as far as I have been able to find is JsonML. JsonML even does a better job of goal #1 in terms of succinctness than my proposal for Jamilih (except that Jamilih can represent empty elements more succinctly). However, for goal #3, I believe Jamilih is slightly more flexible for regular usage in templates, and to my personal sensibilities, more clear in goal #8 (and with a plan for goal #5 and #7?).
WeakMapTemplates to define and invoke functions/objects tied to an element
$symbolto accept array of arrays for attaching multiple symbols to an element
testsfolder into nodeunit
valuecan take place after the options are added