Questo è quello che ho capito:
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";
}
}
})();
Per migliorare le prestazioni ho creato una funzione auto-invocante che verifica le funzionalità del browser una sola volta e assegna la funzione appropriata di conseguenza.
Il primo test dovrebbe funzionare nella maggior parte dei browser moderni ed è già stato discusso qui. Verifica solo se l'elemento è un'istanza di HTMLElement
. Molto semplice.
Il secondo è il più interessante. Questa è la sua funzionalità principale:
return el instanceof (document.createElement(el.nodeName)).constructor
Verifica se el è un'istanza del costruttore che finge di essere. Per fare ciò, abbiamo bisogno dell'accesso al contruttore di un elemento. Ecco perché lo stiamo testando nell'istruzione if. IE7 ad esempio non riesce, perché (document.createElement("a")).constructor
è undefined
in IE7.
Il problema con questo approccio è che in document.createElement
realtà non è la funzione più veloce e potrebbe facilmente rallentare l'applicazione se stai testando molti elementi con essa. Per risolvere questo, ho deciso di mettere in cache i costruttori. L'oggetto ElementConstructors
ha nodeNames come chiavi con i suoi corrispondenti costruttori come valori. Se un costruttore è già memorizzato nella cache, lo utilizza dalla cache, altrimenti crea l'Elemento, memorizza nella cache il costruttore per un accesso futuro e quindi esegue il test su di esso.
Il terzo test è lo spiacevole fallback. Verifica se el è un object
, ha una nodeType
proprietà impostata su 1
e una stringa come nodeName
. Questo non è molto affidabile, ovviamente, ma la stragrande maggioranza degli utenti non dovrebbe nemmeno fare un passo indietro.
Questo è l'approccio più affidabile che mi è venuto in mente mantenendo le prestazioni il più alto possibile.