2007年06月15日

Javascript:DOMオブジェクトかECMAオブジェクトかを判定する。

ドキュメントオブジェクトなのかスクリプトのオブジェクトなのか判定したい時って通常どうするのでしょう? typeof() ではどちらも "object" なのですが、実際のところ別物ですよね。(※1)

ということで、幾つか判定用関数を作成してみました。

・ Element かどうかの判定
要は、div とか span とかいったタグ物かどうか?です。
■ 最新はこちら(2009/05/02)
Javascript:element の判定とそれに関わる諸事


・ DOM Node かどうかの判定
attribute (id や value)、テキストは Element ではないんですが DOM Node を受け継ぐオブジェクトです。

・ ECMA object かどうかの判定
v = {apple : 'りんご'};
なんぞのオブジェクトを判定します。
ちなみにアトミックなもの。数値や文字列もオブジェクトである場合があります。そのような場合も真として検出します。

・ イベント object かどうかの判定

(※1) opera はかなり特殊です。Element をオブジェクトとして他のブラウザ以上に統一的に扱おうとしているようです。

確認環境
ie 6.0, 7.0
ff 1.5
ns 7.1
op 8.53



 ソースコード

// Element かどうかの判定
function isElement(e){
  if(e && e.nodeType === 1){
    try{
      e.nodeType = e.nodeType;
    }catch(n){
      return true;
    }
  }
  return false;
}

// DOM Node かどうかの判定
function isDomNode(e){
  if((e && typeof(e.nodeType) == 'number') || e === null){
    try{
      e.nodeType = e.nodeType;
    }catch(n){
      return true;
    }
  }
  return false;
}

// ECMA object かどうかの判定
function isEcmaObject(o){
  if(o === null) return true;
  // ie の場合、DOM object でなければここではじかれる
  if(typeof(o) == 'object' && typeof(o.constructor) == 'function'){
    if(isEcmaObject.exist(o.constructor)) return true;
    try{
      // ここは実際無くてもいい。
      // ff の場合、DOM object でなければここではじかれる
      // node names など抜けてゆくものもある
      var i = ("" + o.constructor.prototype).toLowerCase();
      if(i.indexOf('native', 0) > -1) return false;
      // op の場合、DOM object でなければここではじかれる
      // ff の場合、上で抜けてきたものもはじかれる
      var n = ("" + o).toLowerCase();
      if(n.indexOf('object object', 0) > -1) return true;
    }catch(e){}
  }
  return false;
}
isEcmaObject.GlobalObjects = [// Object は別扱い
  "Function", "Array", "String", "Boolean", "Number",
  "Date", "RegExp", "Error", "EvalError", "RangeError",
  "ReferenceError", "SyntaxError", "TypeError", "URIError"
]
isEcmaObject.exist = function (c){
  var G = isEcmaObject.GlobalObjects;
  for(var i = 0; G.length > i; i++){
    if(window[G[i]] && window[G[i]] == c) return true;
  }
}

// イベント object かどうかの判定
function isEvent(e){
  if(typeof(e) != 'object' || e == null) return false;
  var c = 'constructor';
  var p = 'prototype';
  // ff
  if(typeof(Event) == 'function' && Event == e[c]){
    try{
      var i = ("" + e[c][p]).toLowerCase();
      if(i.indexOf('native', 0) > -1){
        return true;
      }
    }catch(n){
      return ture;
    }
  }
  // ns
  if(typeof(e[c]) == 'object' && ("" + e).indexOf('Event', 0) > -1){
    return true;
  }
  // ie
  if(typeof(e[c]) == 'undefined' && typeof(e.srcElement) != 'undefined'){
    return true;
  }
  // op
  if(window.opera && e.target && e.srcElement){
    try{
      e.target = e.target;
    }catch(n){
      return true;
    }
  }
  return false;
}

 使い方

isElement(ele);
ele がエレメントなら true を返す。

isDomNode(nod);
nod が DOM Node インターフェースを持つなら true を返す。

isEcmaObject(obj);
obj がオブジェクトなら true を返す。

isEvent(evt);
evt がイベントオブジェクトなら true を返す。

 補足

動作確認はこちら (※ null はオブジェクトなので true を返します)

今後、この関数を使って色々UPしようと思ってるのでUPしましたが・・・。
ちょっと無様な事してます。

その1 try{}catch(e){} を使っている。

これは readOnly かどうか実際に試してエラーの発生を確認しているためです。
ま。使う際に obj.nodeType == 1 などの obj がありえないなら try は不要です。
element をパースする事がある場合や、よそ様の作ったライブラリを使わざる得ない場合なんかに良い事があるかもw

その2 valueOf などで返ってくる文字列による比較。

element のコンストラクタなど、element と ECMA object の切り分けなど泣く泣く文字列で判定しています。特に opera には泣かされました。

その3 コンストラクタを列挙して判定。

判定を簡便にするためにコンストラクタ名を列挙したものと比較して決定しています。

うーん。スマートじゃない。
このあたり良い方法があれば、ぜひどなたか教えてください。

 参考

DOM インターフェースの列挙
DOM1(core)
Exception DOMException ※
インターフェイス DOMImplementation ※
インターフェイス DocumentFragment
インターフェイス Document
インターフェイス Node
インターフェイス NodeList ※
インターフェイス NamedNodeMap ※
インターフェイス CharacterData
インターフェイス Attr
インターフェイス Element
インターフェイス Text
インターフェイス Comment
インターフェイス CDATASection
インターフェイス DocumentType
インターフェイス Notation
インターフェイス Entity
インターフェイス EntityReference
インターフェイス ProcessingInstruction
DOM1(HTML)
インターフェイス HTMLCollection ※
インターフェイス HTMLDocument
インターフェイス HTMLElement



以下、HTMLElement インターフェースを持つ。

Node type の列挙
ELEMENT_NODE = 1;
ATTRIBUTE_NODE = 2;
TEXT_NODE = 3;
CDATA_SECTION_NODE = 4;
ENTITY_REFERENCE_NODE = 5;
ENTITY_NODE = 6;
PROCESSING_INSTRUCTION_NODE = 7;
COMMENT_NODE = 8;
DOCUMENT_NODE = 9;
DOCUMENT_TYPE_NODE = 10;
DOCUMENT_FRAGMENT_NODE = 11;
NOTATION_NODE = 12;


仕様上、node インターフェースを持たない DOM インターフェースがあります。仕様を読み違えてなければ、上段テーブルの ※ 印のインターフェースが Node インターフェースを持ちません。nodeList(element の childNodes)とか。
それらは、isDomNode(obj) では false になります。

ちなみに Node インターフェースを持ちつつ Node type が null になるものもあります (undefined ではない)。ただし Node インターフェースは持ってるので、isDomNode(obj) では true になります。

また、DOMException は微妙です。実装に依存するらしい。


posted by HiFa at 14:08 | 🌁 | Comment(3) | TrackBack(0) | JavaScript雑感 | このブログの読者になる | 更新情報をチェックする
>>> スパムコメントは消してますよん。 お互い無駄な労力は避けましょう。 <<<

この記事へのコメント

tryの中でnodeTypeの自己書き込みを行っていますが、オブジェクトがECMA objectでnodeTypeプロパティがない場合や継承したプロパティだった場合、
for( propertyName in <object> ) や
<object>.hasOwnProperty("nodeType")
の挙動が変わってしまうと思います。

IE以外のブラウザなら
<object> instanceof Node や
<object> instanceof Element
を使えばオブジェクトがインターフェースを継承しているかどうか分かります。

IEならば
<object> instanceof Object
を使えばオブジェクトがECMA objectかそれ以外の(HTML要素やActiveXobject)オブジェクトか分かります。
Posted by ピヨピヨ at 2007年09月18日 16:35
一応、無い場合の検査はしてるので良いかと思いますが、継承されていた場合は hasOwnProperty の結果が変わっちゃいますね^^;
hasOwnProperty も確認すべきでした。

と、言うより・・・。

ie 以外は Element なんてあるんですか。勉強不足でした。
opera や firefox ですと、例えエレメントであっても <object> instanceof Object が true になってしまうので、その時点で instanceof の利用を早々に外してしまいました。

当然というか非常にスマートになります。ありがとうございます。
Posted by Hifa at 2007年09月18日 17:42
プログラマ・エンジニア特化型の事業支援プロジェクト
『スプリング・テック・キャンプ2008』(STC2008)の運営開始!



突然のメッセージにて失礼致します。
初めまして、ALITO株式会社と申します。開発者・技術者の方々向けの情報の1つとしてご覧頂けましたら幸いです。


国内外企業のインキュベーション事業を展開する弊社は、開発者及び、技術者に焦点を当てた起業家支援プロジェクト『スプリング・テック・キャンプ2008』(以下、STC2008)を開始しました。このプロジェクトは、開発者及び、技術者がこれまで培った技術を活用し、自分が作りたいサービスを思う存分2ヶ月間で開発して頂くために、当社が最大限に支援するプロジェクトとなります。


【STC2008の詳細ウェブ】
http://www.alito.co.jp/stc2008.html

■ 本プロジェクト概要
 STC2008は、開発者及び、技術者が集中して開発・制作に取り組めるよう、すべての環境を提供します。
■ 受付 2008年3月17日(月)より随時受付
■ 事業テーマ(どのプログラム言語でも可)
・ソフトウェア製品の開発
・ソフトウェアサービス
・ソフトウェア技術開発
・その他、ネット系開発・技術に特化したサービス 
■ 参加条件
・個人での応募、または開発チームでの応募可
・自分の開発・制作に対してフルコミットメントができること
・独創性・オリジナリティのあるアイデアであること
・自分のアイデアを開発するにあたって、エントリー者本人が技術力を持っていること
・現在、起業をしていないが、将来的に起業の意思があること
・年齢が15歳〜40歳までであること
・ALITO株式会社の本社(東京都中央区)で作業が可能なこと
・ビジネス化を予定しているアイデアや技術に対する特許・著作権などの権利をALITO株式会社以外の個人、または団体が保有していないこと


もし、興味がありましたら、このプロジェクトに参加して、色々なプログラマーやエンジニアの方々とそれぞれの目標に向って頑張ってみませんか?

http://www.alito.co.jp/stc2008_entry.html
こちらからご応募頂けますので、是非ご応募をよろしくお願い致します。


長文にて申し訳ございません。


企画・運営・事業支援 ALITO株式会社
スプリング・テック・キャンプ2008(STC2008)事務局 担当:井上
(土・日曜、祝日を除く、午前10時から午後18時まで)
Posted by STC2008 at 2008年03月25日 18:20
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック