使用JavaScript,是否可以获得浏览器支持的标签列表?
document.createElement("asdf") instanceof HTMLUnknownElement
true
document.createElement("canvas") instanceof HTMLUnknownElement
false
function browserSupports(elementTagName) {
var el = document.createElement(elementTagName);
return !((el instanceOf HTMLUnknownElement) || (el instanceof HTMLGenericElement));
}
function browserSupports(elementTagName) {
var unknownel = document.createElement("zzxcv");
var el = document.createElement(elementTagName);
return unknownel.constructor !== el.constructor;
}
然而,在IE8中似乎仍然无法正常工作。
HTMLUnknownElement
。而且,如果我在IE 8模式下使用IE 10进行测试,以此方式进行测试,即使测试任何元素(包括肯定支持的元素,如p
),我也会得到false
的结果。 - Jukka K. Korpelaprops = Object.getOwnPropertyNames(window)
for (var idx in props) {
if (props[idx].indexOf("HTML") == 0) {
//do something here
console.log(props[idx]);
}
}
据我所知,这并不是详尽无遗的,但它会告诉你在大多数浏览器中哪些标签具有DOM对象类型。
在我的Chrome控制台中运行此代码的示例输出如下:
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
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;
}
如何使用:
console.log(getAllTagNames()); // [anchor", "area", "base", "body", "br", ...]
编辑
一些html元素的构造函数只是基础构造函数(例如HTMLMediaElement)。在这种情况下,HTMLAudioElement (<audio></audio>) 和 HTMLVideoElement (<video></video>) 的基础构造函数不直接继承自HTMLElement。因此需要遍历完整个原型链,instanceof运算符非常适合这个任务:
window['HTMLAudioElement'].prototype instanceof HTMLElement
目前在 JavaScript 中创建无法识别的标签名称的唯一方法是使用 document.createElement(tagName);,检查构造函数是否为 HTMLUnknownElement 并手动修复这些问题:
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" }
];
没有通用的方法,但每个元素都有特定的方式来确定它是否被支持。
Canvas元素支持:
var canvasSupported = "getContext" in document.createElement("canvas");
输入类型支持:
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
音频元素支持:
var audioElementSupported = "play" in document.createElement("audio");
进度元素支持
var progressElementSupported = "max" in document.createElement("progress");
input
类型支持的技术应用于td
的align=char
测试支持,则会失败(结果为true,即使没有浏览器实际支持此值)。 - Jukka K. Korpelainput
类型测试技术适用于其他任何东西。它之所以有效,是因为type
是一个枚举属性。我已经在编辑中进行了澄清。 - Esailija
if (IE) return false
。 - zzzzBovdocument.createElement('foo');
,它就可以被样式化。 - Wesley Murch