Isto é o que eu descobri:
var isHTMLElement = (function () {
if ("HTMLElement" in window) {
// Voilà. Quick and easy. And reliable.
return function (el) {return el instanceof HTMLElement;};
} else if ((document.createElement("a")).constructor) {
// We can access an element's constructor. So, this is not IE7
var ElementConstructors = {}, nodeName;
return function (el) {
return el && typeof el.nodeName === "string" &&
(el instanceof ((nodeName = el.nodeName.toLowerCase()) in ElementConstructors
? ElementConstructors[nodeName]
: (ElementConstructors[nodeName] = (document.createElement(nodeName)).constructor)))
}
} else {
// Not that reliable, but we don't seem to have another choice. Probably IE7
return function (el) {
return typeof el === "object" && el.nodeType === 1 && typeof el.nodeName === "string";
}
}
})();
Para melhorar o desempenho, criei uma função auto-invocadora que testa os recursos do navegador apenas uma vez e atribui a função apropriada de acordo.
O primeiro teste deve funcionar nos navegadores mais modernos e já foi discutido aqui. Apenas testa se o elemento é uma instância de HTMLElement
. Muito simples.
O segundo é o mais interessante. Esta é sua principal funcionalidade:
return el instanceof (document.createElement(el.nodeName)).constructor
Ele testa se el é uma instância do construtor que finge ser. Para fazer isso, precisamos acessar o contratador de um elemento. É por isso que estamos testando isso na instrução if. O IE7, por exemplo, falha com isso, porque (document.createElement("a")).constructor
está undefined
no IE7.
O problema com essa abordagem é que ela document.createElement
realmente não é a função mais rápida e pode desacelerar facilmente seu aplicativo se você estiver testando vários elementos com ele. Para resolver isso, decidi armazenar em cache os construtores. O objeto ElementConstructors
possui nodeNames como chaves e seus construtores correspondentes como valores. Se um construtor já estiver armazenado em cache, ele o usará do cache; caso contrário, ele criará o Elemento, armazenará em cache seu construtor para acesso futuro e, em seguida, testará contra ele.
O terceiro teste é o fallback desagradável. Ele testa se el é um object
, tem uma nodeType
propriedade definida como 1
e uma sequência como nodeName
. Isso não é muito confiável, é claro, mas a grande maioria dos usuários nem deveria voltar tão longe.
Essa é a abordagem mais confiável que eu criei, mantendo o desempenho o mais alto possível.