Using JavaScript, is it possible to obtain a list of the tags that a browser supports?
Every JavaScript html element object, created by document.createElement(), has a constructor function which always starts with "HTML..." and ends with "...Element". All these are inherit from HTMLElement. Each of these are reachable in the window object via Object.getOwnPropertyNames().
So you can get all valid tag names by iterate over all property names in window and filter them with HTML...ELement:
function getAllTagNames()
{
let names = [];
Object.getOwnPropertyNames(window).forEach(name =>
{
if(name.startsWith('HTML') && name.endsWith('Element') && Object.getPrototypeOf(window[name]) == HTMLElement)
{
names.push(name.substr(4, name.length - 11).toLowerCase());
}
});
names.sort((left, right) =>
{
if(left.toLowerCase) { left = left.toLowerCase(); }
if(right.toLowerCase) { right = right.toLowerCase(); }
return left == right ? 0 : (left < right ? -1 : 1);
});
return names;
}
How to use it:
console.log(getAllTagNames()); // [anchor", "area", "base", "body", "br", ...]
edit
Some constructor functions for html elements are just base constructor functions (for example HTMLMediaElement). In this case the base constructor function of HTMLAudioElement (<audio></audio>) and HTMLVideoElement (<video></video>) are not directly inherit from HTMLElement. So it's necessary to run through the complete protype chain, the instanceof operator is suitable for this:
window['HTMLAudioElement'].prototype instanceof HTMLElement
Another aspect is that some constructor function names fit not to the equivalent html tag name (<a> => HTMLAnchorElement) and some other names fit to several tags (for example <h1></h1>, <h2></h2>, <h3></h3>, <h4></h4>, <h5></h5>, <h6></h6>) with the same constructor function. > see mdn.
Currently there is no other way in JavScript to create the wrong identified tag name with document.createElement(tagName);, check whether the constructor is an HTMLUnknownElement and fixed these manually:
function getAllTagNames()
{
let items = [];
Object.getOwnPropertyNames(window).forEach(name =>
{
if(name.startsWith('HTML') && name.endsWith('Element') && window[name].prototype instanceof HTMLElement)
{
items.push({ constructorName: name, tagName: name.substr(4, name.length - 11).toLowerCase() });
}
});
items.sort((leftItem, rightItem) =>
{
let left = leftItem.tagName;
let right = rightItem.tagName;
if(left.toLowerCase) { left = left.toLowerCase(); }
if(right.toLowerCase) { right = right.toLowerCase(); }
return left == right ? 0 : (left < right ? -1 : 1);
});
function insertSorted(item)
{
let index = 0;
while(item.tagName > items[index].tagName) { index++; }
items.splice(index, 0, item);
}
let disagreements = [];
items = items.filter(item =>
{
let tagName = item.tagName;
switch(tagName) // deprecated
{
case "keygen": return false;
}
let filter = tagName == "unknown" || document.createElement(tagName).constructor == HTMLUnknownElement;
if(filter && tagName != "unknown") { disagreements.push(item); }
return !filter;
});
disagreements = disagreements.filter(item =>
{
switch(item.tagName) // base constructor
{
case "media": return false;
}
return true;
});
disagreements.forEach(item =>
{
let tagName = item.tagName;
function exchange(tagName)
{
insertSorted({ constructorName: item.constructorName, tagName: tagName });
}
switch(tagName)
{
case 'anchor':
exchange('a');
break;
case 'directory':
exchange('dir');
break;
case 'dlist':
exchange('dl');
break;
case 'heading':
exchange('h1');
exchange('h2');
exchange('h3');
exchange('h4');
exchange('h5');
exchange('h6');
break;
case 'image':
exchange('img');
break;
case 'mod':
exchange('del');
exchange('ins');
break;
case 'olist':
exchange('ol');
break;
case 'paragraph':
exchange('p');
break;
case 'quote':
exchange('blockquote');
exchange('q');
break;
case 'tablecaption':
exchange('caption');
break;
case 'tablecell':
exchange('th');
exchange('td');
break;
case 'tablecol':
exchange('col');
exchange('colgroup');
break;
case 'tablerow':
exchange('tr');
break;
case 'tablesection':
exchange('tfoot');
exchange('thead');
exchange('tbody');
break;
case 'ulist':
exchange('ul');
break;
default:
console.log('disagree', tagName);
if(console.warn && tagName != "") { console.warn("unknown tag name for " + item.constructorName); }
break;
}
});
return items.map(item => item.tagName);
}
edit 2:
let tagNames =
[
{ name: "a", constr: "HTMLAnchorElement" },
{ name: "area", constr: "HTMLAreaElement" },
{ name: "audio", constr: "HTMLAudioElement" },
{ name: "base", constr: "HTMLBaseElement" },
{ name: "body", constr: "HTMLBodyElement" },
{ name: "br", constr: "HTMLBRElement" },
{ name: "button", constr: "HTMLButtonElement" },
{ name: "canvas", constr: "HTMLCanvasElement" },
{ name: "content", constr: "HTMLContentElement" },
{ name: "data", constr: "HTMLDataElement" },
{ name: "datalist", constr: "HTMLDataListElement" },
{ name: "details", constr: "HTMLDetailsElement" },
{ name: "dialog", constr: "HTMLDialogElement" },
{ name: "dir", constr: "HTMLDirectoryElement" },
{ name: "div", constr: "HTMLDivElement" },
{ name: "dl", constr: "HTMLDListElement" },
{ name: "embed", constr: "HTMLEmbedElement" },
{ name: "fieldset", constr: "HTMLFieldSetElement" },
{ name: "font", constr: "HTMLFontElement" },
{ name: "form", constr: "HTMLFormElement" },
{ name: "frame", constr: "HTMLFrameElement" },
{ name: "frameset", constr: "HTMLFrameSetElement" },
{ name: "head", constr: "HTMLHeadElement" },
{ name: "h1", constr: "HTMLHeadingElement" },
{ name: "h2", constr: "HTMLHeadingElement" },
{ name: "h3", constr: "HTMLHeadingElement" },
{ name: "h4", constr: "HTMLHeadingElement" },
{ name: "h5", constr: "HTMLHeadingElement" },
{ name: "h6", constr: "HTMLHeadingElement" },
{ name: "hr", constr: "HTMLHRElement" },
{ name: "html", constr: "HTMLHtmlElement" },
{ name: "iframe", constr: "HTMLIFrameElement" },
{ name: "img", constr: "HTMLImageElement" },
{ name: "input", constr: "HTMLInputElement" },
{ name: "label", constr: "HTMLLabelElement" },
{ name: "legend", constr: "HTMLLegendElement" },
{ name: "li", constr: "HTMLLIElement" },
{ name: "link", constr: "HTMLLinkElement" },
{ name: "map", constr: "HTMLMapElement" },
{ name: "marquee", constr: "HTMLMarqueeElement" },
{ name: "menu", constr: "HTMLMenuElement" },
{ name: "meta", constr: "HTMLMetaElement" },
{ name: "meter", constr: "HTMLMeterElement" },
{ name: "del", constr: "HTMLModElement" },
{ name: "ins", constr: "HTMLModElement" },
{ name: "object", constr: "HTMLObjectElement" },
{ name: "ol", constr: "HTMLOListElement" },
{ name: "optgroup", constr: "HTMLOptGroupElement" },
{ name: "option", constr: "HTMLOptionElement" },
{ name: "output", constr: "HTMLOutputElement" },
{ name: "p", constr: "HTMLParagraphElement" },
{ name: "param", constr: "HTMLParamElement" },
{ name: "picture", constr: "HTMLPictureElement" },
{ name: "pre", constr: "HTMLPreElement" },
{ name: "progress", constr: "HTMLProgressElement" },
{ name: "q", constr: "HTMLQuoteElement" },
{ name: "script", constr: "HTMLScriptElement" },
{ name: "select", constr: "HTMLSelectElement" },
{ name: "shadow", constr: "HTMLShadowElement" },
{ name: "slot", constr: "HTMLSlotElement" },
{ name: "source", constr: "HTMLSourceElement" },
{ name: "span", constr: "HTMLSpanElement" },
{ name: "style", constr: "HTMLStyleElement" },
{ name: "td", constr: "HTMLTableCellElement" },
{ name: "th", constr: "HTMLTableCellElement" },
{ name: "col", constr: "HTMLTableColElement" },
{ name: "colgroup", constr: "HTMLTableColElement" },
{ name: "table", constr: "HTMLTableElement" },
{ name: "tr", constr: "HTMLTableRowElement" },
{ name: "tbody", constr: "HTMLTableSectionElement" },
{ name: "tfoot", constr: "HTMLTableSectionElement" },
{ name: "thead", constr: "HTMLTableSectionElement" },
{ name: "template", constr: "HTMLTemplateElement" },
{ name: "time", constr: "HTMLTimeElement" },
{ name: "title", constr: "HTMLTitleElement" },
{ name: "track", constr: "HTMLTrackElement" },
{ name: "ul", constr: "HTMLUListElement" },
{ name: "video", constr: "HTMLVideoElement" }
];
You can get a decent idea of what is supported by introspection on the window.
Try this out:
props = Object.getOwnPropertyNames(window)
for (var idx in props) {
if (props[idx].indexOf("HTML") == 0) {
//do something here
console.log(props[idx]);
}
}
This is in no way exhaustive as far as I know but it will tell you in most browsers what tags have a DOM object type.
Here is sample output by running this in my Chrome console:
HTMLUnknownElement
HTMLOptionsCollection
HTMLFormControlsCollection
HTMLAllCollection
HTMLCollection
HTMLUListElement
HTMLTitleElement
HTMLTextAreaElement
HTMLTemplateElement
HTMLTableSectionElement
HTMLTableRowElement
HTMLTableElement
HTMLTableColElement
HTMLTableCellElement
HTMLTableCaptionElement
HTMLStyleElement
HTMLSpanElement
HTMLSelectElement
HTMLScriptElement
HTMLQuoteElement
HTMLProgressElement
HTMLPreElement
HTMLParamElement
HTMLParagraphElement
HTMLOutputElement
HTMLOptionElement
HTMLOptGroupElement
HTMLObjectElement
HTMLOListElement
HTMLModElement
HTMLMeterElement
HTMLMetaElement
HTMLMenuElement
HTMLMarqueeElement
HTMLMapElement
HTMLLinkElement
HTMLLegendElement
HTMLLabelElement
HTMLLIElement
HTMLKeygenElement
HTMLInputElement
HTMLImageElement
HTMLIFrameElement
HTMLHtmlElement
HTMLHeadingElement
HTMLHeadElement
HTMLHRElement
HTMLFrameSetElement
HTMLFrameElement
HTMLFormElement
HTMLFontElement
HTMLFieldSetElement
HTMLEmbedElement
HTMLDivElement
HTMLDirectoryElement
HTMLDataListElement
HTMLDListElement
HTMLCanvasElement
HTMLButtonElement
HTMLBodyElement
HTMLBaseElement
HTMLBRElement
HTMLAreaElement
HTMLAppletElement
HTMLAnchorElement
HTMLElement
HTMLDocument
If you're willing to start with a known list of candidate tags, you could try something like this:
document.createElement("asdf") instanceof HTMLUnknownElement
true
document.createElement("canvas") instanceof HTMLUnknownElement
false
If you need to support IE8, you could use this approach:
function browserSupports(elementTagName) {
var el = document.createElement(elementTagName);
return !((el instanceOf HTMLUnknownElement) || (el instanceof HTMLGenericElement));
}
Here's another approach that doesn't rely on specific named constructors.
function browserSupports(elementTagName) {
var unknownel = document.createElement("zzxcv");
var el = document.createElement(elementTagName);
return unknownel.constructor !== el.constructor;
}
It still doesn't seem to work in IE8 though.
There is no general way but each element has specific way to see if it's supported
Canvas element support:
var canvasSupported = "getContext" in document.createElement("canvas");
Input type support:
var input = document.createElement("input");
input.type = "color"
var colorInputSupported = input.type === "color";
//The above relies on the fact that type is enumerated and
//falls back to "text" with invalid value
//The technique doesn't necessarily carry over to other properties
Audio element support:
var audioElementSupported = "play" in document.createElement("audio");
Progress element support
var progressElementSupported = "max" in document.createElement("progress");