これは私が考え出したものです:
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";
}
}
})();
パフォーマンスを向上させるために、ブラウザーの機能を1回だけテストし、それに応じて適切な関数を割り当てる自己呼び出し関数を作成しました。
最初のテストはほとんどの最新のブラウザーで機能するはずであり、すでにここで説明されています。要素がインスタンスであるかどうかをテストするだけですHTMLElement
。非常に簡単です。
2番目は最も興味深いものです。これがコア機能です。
return el instanceof (document.createElement(el.nodeName)).constructor
elがそれがふりをしているconstrucorのインスタンスであるかどうかをテストします。そのためには、要素のコンストラクタにアクセスする必要があります。そのため、if-Statementでこれをテストしています。たとえばIE7はIE7にあるため、これ(document.createElement("a")).constructor
は失敗しますundefined
。
このアプローチの問題は、document.createElement
実際には最速の機能ではなく、多数の要素をテストしている場合、アプリケーションの速度が低下しやすいことです。これを解決するために、コンストラクターをキャッシュすることにしました。オブジェクトElementConstructors
は、対応するコンストラクターを値として持つnodeNamesをキーとして持ちます。コンストラクタがすでにキャッシュされている場合は、それをキャッシュから使用します。それ以外の場合は、Elementを作成し、将来のアクセスのためにコンストラクタをキャッシュしてから、それに対してテストします。
3番目のテストは、不快なフォールバックです。elがobject
、nodeType
プロパティがに設定され1
、文字列がnodeName
ます。もちろん、これはそれほど信頼できるものではありませんが、ユーザーの大多数はこれまでにフォールバックするべきではありません。
これは、可能な限り高いパフォーマンスを維持しながら、私が思いついた最も信頼できるアプローチです。