Reputation: 120576
The HTML spec describes several kinds of elements stored in the CustomElementRegistry.
<a>
that are represented by a builtin subclass of HTMLElement like HTMLAnchorElement.<my-element>
which extend HTMLElement
and register with the CustomElementRegistry.<a is="my-link">
which extend a subclass of HTMLElement
and use the is="customized-tag-name"
to specify the class.<isindex>
which extend HTMLUnknownElementIs there a way to figure, at runtime, which bucket an element belongs to?
Upvotes: 3
Views: 287
Reputation: 120576
The below [jsbin] kind of does the job and manages without testing constructors for "[native code]"
but there's got to be a better way to tell whether an element is builtin.
//<script>
// https://w3c.github.io/webcomponents/spec/custom/#valid-custom-element-name
var VALID_CUSTOM_ELEMENT_NAME_REGEX = /^(?!(?:annotation-xml|color-profile|font-face|font-face(?:-(?:src|uri|format|name))?|missing-glyph)$)[a-z][a-z.0-9_\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u200C\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uDFFF\uF900-\uFDCF\uFDF0-\uFFFD]*-[\-a-z.0-9_\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u200C\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uDFFF\uF900-\uFDCF\uFDF0-\uFFFD]*$/;
function classify(el) {
var name = el.localName;
// TODO: are elements from a different document dealt with by that
// document's CustomElementRegistry?
// How do we get from a document to a CustomElementRegistry?
// Maybe just assert el.ownerDocument === document?
// Does that screw up elements from <template> content.
if (customElements.get(name)) { return 'custom'; }
// TODO: check that the value of is="..." is registered
if (el.getAttribute('is')) { return 'custom-builtin'; }
var ctor = el.constructor;
if (ctor === HTMLUnknownElement) { return 'legacy'; }
if (ctor === HTMLElement) {
if (VALID_CUSTOM_ELEMENT_NAME_REGEX.test(name)) {
return 'customizable';
}
}
return 'builtin';
}
// A bunch of element names from MDN and elsewhere to test
var names = [
"a", "abbr", "acronym", "address", "applet",
"annotation-xml", "area",
"article", "aside", "audio", "b", "base", "basefont", "bdi", "bdo",
"bgsound", "big", "blink", "blockquote", "body", "br", "button",
"canvas", "caption", "center", "cite", "code", "col", "colgroup",
"command", "content", "data", "datalist", "dd", "del", "details",
"dfn", "dialog", "dir", "div", "dl", "dt", "element", "em", "embed",
"fieldset", "figcaption", "figure", "font", "footer", "form",
"frame", "frameset", "h1", "h6", "head", "header", "hgroup", "hr",
"html", "i", "iframe", "image", "img", "input", "ins", "isindex",
"kbd", "keygen", "label", "legend", "li", "link", "listing", "main",
"map", "mark", "marquee", "menu", "menuitem", "meta", "meter",
"multicol", "nav", "nobr", "noembed", "noframes", "noscript",
"object", "ol", "optgroup", "option", "output", "p", "param",
"picture", "plaintext", "pre", "progress", "q", "rp", "rt", "rtc",
"ruby", "s", "samp", "script", "section", "select", "shadow",
"slot", "small", "source", "spacer", "span", "strike", "strong",
"style", "sub", "summary", "sup", "table", "tbody", "td",
"template", "textarea", "tfoot", "th", "thead", "time", "title",
"tr", "track", "tt", "u", "ul", "var", "video", "wbr", "xmp",
"isindex", // Known legacy element
"un-registered", "reg-istered"]; // Test custom / customizable
//</script><script>
class MyElement extends HTMLElement {
constructor() {
super();
}
}
customElements.define('reg-istered', MyElement);
//</script><script>
var table = document.createElement('table');
document.body.appendChild(table);
var tbody = document.createElement('tbody');
table.appendChild(tbody);
function addRowForElement(el) {
var tr = document.createElement('tr');
tbody.appendChild(tr);
var td0 = document.createElement('td');
tr.appendChild(td0);
td0.appendChild(document.createTextNode(el.outerHTML));
var td1 = document.createElement('td');
tr.appendChild(td1);
td1.appendChild(document.createTextNode('' + classify(el)));
}
for (var i = 0, n = names.length; i < n; ++i) {
addRowForElement(document.createElement(names[i]));
}
var customBi = document.createElement('input');
customBi.setAttribute('is', 'my-custom-input'); // Not yet registered.
addRowForElement(customBi);
//</script><style>td:first-child { font-family: monospace }</style>
Upvotes: 1