diff --git a/cjs/html/document.js b/cjs/html/document.js index 85f1a4f8..1274dd98 100644 --- a/cjs/html/document.js +++ b/cjs/html/document.js @@ -10,9 +10,10 @@ const {customElements} = require('../interface/custom-element-registry.js'); const {HTMLElement} = require('./element.js'); const createHTMLElement = (ownerDocument, builtin, localName, options) => { - if (!builtin && htmlClasses.has(localName)) { + if (!builtin) { const Class = htmlClasses.get(localName); - return new Class(ownerDocument, localName); + if (Class) + return new Class(ownerDocument, localName); } const {[CUSTOM_ELEMENTS]: {active, registry}} = ownerDocument; if (active) { diff --git a/cjs/shared/parse-from-string.js b/cjs/shared/parse-from-string.js index 84a9fdf3..f85f205c 100644 --- a/cjs/shared/parse-from-string.js +++ b/cjs/shared/parse-from-string.js @@ -27,12 +27,23 @@ const append = (self, node, active) => { return node; }; +/** Same tokens as a DOMTokenList would hold: no empty entries, no duplicates, single spaces. */ +const classTokens = value => { + const tokens = []; + for (const token of value.split(/\s+/)) { + if (token && !tokens.includes(token)) + tokens.push(token); + } + return tokens.join(' '); +}; + const attribute = (element, end, attribute, value, active) => { attribute[VALUE] = value; attribute.ownerElement = element; knownSiblings(end[PREV], attribute, end); + // the token list is built on demand: while it does not exist, only the value needs the tokens if (attribute.name === 'class') - element.className = value; + attribute[VALUE] = classTokens(value); if (active) attributeChangedCallback(element, attribute.name, null, value); }; diff --git a/esm/html/document.js b/esm/html/document.js index 47e23bca..ea6a9a3c 100644 --- a/esm/html/document.js +++ b/esm/html/document.js @@ -9,9 +9,10 @@ import {customElements} from '../interface/custom-element-registry.js'; import {HTMLElement} from './element.js'; const createHTMLElement = (ownerDocument, builtin, localName, options) => { - if (!builtin && htmlClasses.has(localName)) { + if (!builtin) { const Class = htmlClasses.get(localName); - return new Class(ownerDocument, localName); + if (Class) + return new Class(ownerDocument, localName); } const {[CUSTOM_ELEMENTS]: {active, registry}} = ownerDocument; if (active) { diff --git a/esm/shared/parse-from-string.js b/esm/shared/parse-from-string.js index 08c6fe39..af30aac2 100644 --- a/esm/shared/parse-from-string.js +++ b/esm/shared/parse-from-string.js @@ -26,12 +26,23 @@ const append = (self, node, active) => { return node; }; +/** Same tokens as a DOMTokenList would hold: no empty entries, no duplicates, single spaces. */ +const classTokens = value => { + const tokens = []; + for (const token of value.split(/\s+/)) { + if (token && !tokens.includes(token)) + tokens.push(token); + } + return tokens.join(' '); +}; + const attribute = (element, end, attribute, value, active) => { attribute[VALUE] = value; attribute.ownerElement = element; knownSiblings(end[PREV], attribute, end); + // the token list is built on demand: while it does not exist, only the value needs the tokens if (attribute.name === 'class') - element.className = value; + attribute[VALUE] = classTokens(value); if (active) attributeChangedCallback(element, attribute.name, null, value); }; diff --git a/test/html/element.js b/test/html/element.js index 3c3fedd8..88107f3f 100644 --- a/test/html/element.js +++ b/test/html/element.js @@ -159,3 +159,18 @@ assert(node.innerHTML, ''); node.innerHTML = '