浏览器支持的所有HTML标签

12
使用JavaScript,是否可以获得浏览器支持的标签列表?

1
那会被用于什么? - twinlakes
5
为了满足提问者的好奇心,希望这位提问者不是只猫。 - dc5
6
检查支持标签的代码可能会以以下方式开始:if (IE) return false - zzzzBov
1
@cale_b 基本上你只需要运行 document.createElement('foo');,它就可以被样式化。 - Wesley Murch
2
“支持”是什么意思?您想知道特定浏览器中哪些标签(包括非标准标签)具有特殊含义,或者特定规范中哪些标签已完全实现(在DOM接口、行为等方面)? - Ilya Streltsyn
显示剩余9条评论
4个回答

5
如果您愿意从已知的候选标签列表开始,可以尝试类似以下的方法:
document.createElement("asdf") instanceof HTMLUnknownElement
true
document.createElement("canvas") instanceof HTMLUnknownElement
false

如果您需要支持IE8,可以使用以下方法:
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中似乎仍然无法正常工作。


1
这并不是很健壮,因为旧的浏览器无法识别HTMLUnknownElement。而且,如果我在IE 8模式下使用IE 10进行测试,以此方式进行测试,即使测试任何元素(包括肯定支持的元素,如p),我也会得到false的结果。 - Jukka K. Korpela
@JukkaK.Korpela:好观点。我已经添加了一个新的实现来处理IE8。 - recursive
我的原始想法是不使用列表标签开始,但这也是一种非常酷的方法 =)。 - Hugo

5
你可以通过检查窗口的内省来逐渐了解支持哪些内容。请尝试以下操作:
props = 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

3
很有趣,但它提供的是接口列表,而不是元素类型列表。例如,该列表不包含任何对应于“strong”或“span”的内容,因为这些元素没有自己的接口(它们只是使用通用的HTMLElement接口)。 - Jukka K. Korpela
据我所见,可能没有一种可靠的方法来做到这一点,但我喜欢这种方法,因为它可以在没有初始列表的情况下使用,并且正如@JukkaK.Korpela所说,列表中将会有空缺,但它仍然是一个相当酷的方法。 - Hugo

5
每个由 document.createElement() 创建的 JavaScript html 元素对象,都有一个构造函数,始终以 "HTML..." 开头,并以 "...Element" 结尾。所有这些都从 HTMLElement 继承。可以通过在 window 对象中使用 Object.getOwnPropertyNames() 来访问它们中的每一个。
因此,您可以通过遍历 window 中的所有属性名称并使用 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;
}

如何使用:

console.log(getAllTagNames()); // [anchor", "area", "base", "body", "br", ...]

编辑

一些html元素的构造函数只是基础构造函数(例如HTMLMediaElement)。在这种情况下,HTMLAudioElement (<audio></audio>) 和 HTMLVideoElement (<video></video>) 的基础构造函数不直接继承自HTMLElement。因此需要遍历完整个原型链,instanceof运算符非常适合这个任务:

window['HTMLAudioElement'].prototype instanceof HTMLElement

另一个方面是一些构造函数名称与等效的 HTML 标签名称不匹配(例如 <a> => HTMLAnchorElement),而其他一些名称与多个标签匹配(例如 <h1></h1>, <h2></h2>, <h3></h3>, <h4></h4>, <h5></h5>, <h6></h6>),但使用相同的构造函数。请参阅 MDN 文档>看看

目前在 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" }
];

非常酷。但是还有很多缺失的元素。仅在A,B和C中,就有aside、abbr、address、article、b、bdi、bdo、blockquote、caption、cite、code、col、colgroup等。如果结合手动添加其余缺失的元素,这可能是一种不错的方法。真是太痛苦了... - JSideris
非常感谢。这确实帮助了我的业余项目。如果您有任何生成新列表的新方法,就像在“编辑2”中所示,请告诉我! 现在,<shadow>和<content>已被弃用,并不再受浏览器支持。可能会有新的标签和构造函数不在其中。 - He Nrik
例如,我注意到这些标签不在列表中:header、footer、section、article。 - He Nrik

3

没有通用的方法,但每个元素都有特定的方式来确定它是否被支持。

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类型支持的技术应用于tdalign=char测试支持,则会失败(结果为true,即使没有浏览器实际支持此值)。 - Jukka K. Korpela
@JukkaK.Korpela,是的,我并不是想暗示input类型测试技术适用于其他任何东西。它之所以有效,是因为type是一个枚举属性。我已经在编辑中进行了澄清。 - Esailija
“align”属性也是枚举的。即使是形式上相似的属性(具有枚举值列表),不同的浏览器也可能会以不同的方式处理它们。 - Jukka K. Korpela
@JukkaK.Korpela 我没有发现除了它已经被废弃之外的任何有关信息。 - Esailija

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接