Oto co wymyśliłem:
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";
}
}
})();
Aby poprawić wydajność, stworzyłem funkcję samo-wywołującą, która testuje możliwości przeglądarki tylko raz i odpowiednio przypisuje odpowiednią funkcję.
Pierwszy test powinien działać w większości nowoczesnych przeglądarek i został już tutaj omówiony. Po prostu sprawdza, czy element jest instancjąHTMLElement
. Bardzo proste.
Drugi jest najciekawszy. Oto jego podstawowa funkcjonalność:
return el instanceof (document.createElement(el.nodeName)).constructor
Sprawdza, czy el jest instancją konstruktora, którym udaje. Aby to zrobić, potrzebujemy dostępu do konstruktora elementu. Dlatego testujemy to w instrukcji if. Na przykład IE7 nie działa, ponieważ (document.createElement("a")).constructor
jestundefined
w IE7.
Problem z tym podejściem polega na tym, że document.createElement
tak naprawdę nie jest to najszybsza funkcja i może łatwo spowolnić twoją aplikację, jeśli testujesz z nią wiele elementów. Aby rozwiązać ten problem, postanowiłem buforować konstruktory. ObiektElementConstructors
ma nodeNames jako klucze, a odpowiadające im konstruktory jako wartości. Jeśli konstruktor jest już buforowany, używa go z pamięci podręcznej, w przeciwnym razie tworzy element, buforuje swojego konstruktora do przyszłego dostępu, a następnie testuje na nim.
Trzeci test to nieprzyjemna awaria. Sprawdza, czy el jest an object
, ma nodeType
ustawioną właściwość na 1
oraz string asnodeName
. Oczywiście nie jest to zbyt wiarygodne, ale zdecydowana większość użytkowników nie powinna nawet cofnąć się do tej pory.
Jest to najbardziej niezawodne podejście, jakie wymyśliłem, przy jednoczesnym utrzymaniu jak najwyższej wydajności.