Skip to content

Instantly share code, notes, and snippets.

@jonathantneal
Last active November 9, 2023 21:10
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save jonathantneal/7935589 to your computer and use it in GitHub Desktop.
Save jonathantneal/7935589 to your computer and use it in GitHub Desktop.
createElement.js // a 300 byte DOM Element creator

createElement.js

createElement.js lets document.createElement use CSS selectors.

This is a pretty useful library for building out DOM elements. The whole thing runs on one regex and a for loop, so it’s plenty fast. The script is 300 bytes when compressed and gzipped. For 524 bytes (advanced), it includes nesting support and can generate entire DOM hierarchies, including text nodes.

Usage

document.createElement(); // generates <div />

document.createElement('li'); // generates <li />

document.createElement('#foo'); // generates <div id="foo" />
document.createElement('.bar'); // generates <div class="bar" />
document.createElement('.alpha.omega'); // generates <div class="alpha omega" />

document.createElement('[tabindex]'); // generates <div tabindex />
document.createElement('[title="Hello"]'); // generates <div title="Hello" />

document.createElement('p.this#thing.also[data-amen].that'); // generates <p id="thing" class="this also that" data-amen />

document.createElement('[data-value="<.it=\"#works[well]\">"]'); // generates <div data-value="&lt;.it=&quot;#works[well]&quot;&gt;" />

Advanced usage

document.createElement('span.field\n\tlabel "To: "\n\t\tinput'); // generates <span class="field"><label>To: <input></label></span>

Compatibility

Works in everything since IE6.

(function (createElement) {
'use strict';
var
MATCH = '(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)',
QUOTE1 = '(["\'])((?:(?=(\\\\?))\\',
QUOTE2 = '[\\W\\w])*?)\\',
REGEX = '^(?:' + MATCH + ')|^#' + MATCH + '|^\\.' + MATCH + '|^\\[' + MATCH + '(?:([*$|~^]?=)' + QUOTE1 + '8' + QUOTE2 + '6' + ')?\\]|^\\s*[\\n\\r]+([\\t]*)\\s*|^(\\s+)|^' + QUOTE1 + '13' + QUOTE2 + '11';
document.createElement = function (selectorUntrimmed) {
var
selector = selectorUntrimmed.replace(/^\s+|\s+$/),
root = document.createDocumentFragment(),
nest = [root, createElement.call(this, 'div')];
for (var frag = root, node = frag.appendChild(nest[1]), index = 1, first = true, match; selector && (match = selector.match(REGEX));) {
// tag
if (match[1]) {
frag.replaceChild(node = createElement.call(this, match[1]), frag.lastChild);
if (first) nest[index] = node;
}
// id
if (match[2]) node.id = match[2];
// class
if (match[3]) node.className += (node.className ? ' ' : '') + match[3];
// attribute
if (match[4]) node.setAttribute(match[4], match[7] || '');
// nesting
if (match[9] !== undefined) {
index = match[9].length;
frag = nest[index];
node = nest[++index] = frag.appendChild(createElement.call(this, 'div'));
}
// child
if (match[10]) {
frag = node;
node = frag.appendChild(createElement.call(this, 'div'));
first = false;
}
// text
if (match[11]) {
frag.replaceChild(node = document.createTextNode(match[12]), frag.lastChild);
if (first) nest[index] = node;
}
selector = selector.slice(match[0].length);
}
return root.childNodes.length === 1 ? root.lastChild : root;
};
})(document.createElement);
(function(e){"use strict";var t="(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)",n="([\"'])((?:(?=(\\\\?))\\",r="[\\W\\w])*?)\\",i="^(?:"+t+")|^#"+t+"|^\\."+t+"|^\\["+t+"(?:([*$|~^]?=)"+n+"8"+r+"6"+")?\\]|^\\s*[\\n\\r]+([\\t]*)\\s*|^(\\s+)|^"+n+"13"+r+"11";document.createElement=function(t){var n=t.replace(/^\s+|\s+$/),r=document.createDocumentFragment(),s=[r,e.call(this,"div")];for(var o=r,u=o.appendChild(s[1]),a=1,f=true,l;n&&(l=n.match(i));){if(l[1]){o.replaceChild(u=e.call(this,l[1]),o.lastChild);if(f)s[a]=u}if(l[2])u.id=l[2];if(l[3])u.className+=(u.className?" ":"")+l[3];if(l[4])u.setAttribute(l[4],l[7]||"");if(l[9]!==undefined){a=l[9].length;o=s[a];u=s[++a]=o.appendChild(e.call(this,"div"))}if(l[10]){o=u;u=o.appendChild(e.call(this,"div"));f=false}if(l[11]){o.replaceChild(u=document.createTextNode(l[12]),o.lastChild);if(f)s[a]=u}n=n.slice(l[0].length)}return r.childNodes.length===1?r.lastChild:r}})(document.createElement)
(function (createElement) {
var
MATCH = '(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)',
REGEX = '^(?:' + MATCH + ')|^#' + MATCH + '|^\\.' + MATCH + '|^\\[' + MATCH + '(?:([*$|~^]?=)(["\'])((?:(?=(\\\\?))\\8.)*?)\\6)?\\]';
document.createElement = function (selector) {
for (var node = createElement.call(this, 'div'), match, className = ''; selector && (match = selector.match(REGEX));) {
if (match[1]) node = createElement.call(this, match[1]);
if (match[2]) node.id = match[2];
if (match[3]) className += ' ' + match[3];
if (match[4]) node.setAttribute(match[4], match[7] || '');
selector = selector.slice(match[0].length);
}
if (className) node.className = className.slice(1);
return node;
};
})(document.createElement);
(function(e){var t="(-?[_a-zA-Z]+[_a-zA-Z0-9-]*)",n="^(?:"+t+")|^#"+t+"|^\\."+t+"|^\\["+t+"(?:([*$|~^]?=)([\"'])((?:(?=(\\\\?))\\8.)*?)\\6)?\\]";document.createElement=function(t){for(var r=e.call(this,"div"),i,s="";t&&(i=t.match(n));){if(i[1])r=e.call(this,i[1]);if(i[2])r.id=i[2];if(i[3])s+=" "+i[3];if(i[4])r.setAttribute(i[4],i[7]||"");t=t.slice(i[0].length)}if(s)r.className=s.slice(1);return r}})(document.createElement)
@elie-g
Copy link

elie-g commented Oct 27, 2019

Hey!
I forked your gist and fixed the regex which didn't allow attributes without " or '.
I also improved the performance by about 25%.
So feel free to add my fixes to your gist... or not! Do as you wish 😉

N̲o̲t̲e̲:̲ I didn't fix the advanced version.

my fork

Performance improvement

@jonathantneal
Copy link
Author

That’s awesome, @DrunkenPoney!

This script ended up becoming https://github.com/jonathantneal/dom-create-node

What’s interesting is how we both switched to the while loop and classList. I should probably update this script to point to that repository.

Here is the current script: https://github.com/jonathantneal/dom-create-node/blob/master/index.es6

Would you be interested in committing any of your improvements there, and then adding your name to the contributors?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment