Abstract.Insertionクラス
前提条件:DOMについての理解
Insertion関連のクラスを理解するためには、DOMについての理解が不可欠になります。実は筆者もあまり詳しいとは言えません^^; このため、不適切な記述があるかもしれませんが、ご容赦ください。気がついたらその都度修正します・・・。
【抜粋】一部省略 Abstract.Insertion = function(adjacency) { this.adjacency = adjacency; } Abstract.Insertion.prototype = { initialize: function(element, content) { this.element = $(element); this.content = content.stripScripts(); (省略) }, contentFromAnonymousTable: function() { var div = document.createElement('div'); div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>'; return $A(div.childNodes[0].childNodes[0].childNodes); } }
prototype.jsの最初のほうで作成したAbstractオブジェクトを名前空間にしています*1。名前のとおり、抽象クラス扱いです。後述のInsertion.Before、Insertion.Top、Insertion.Bottom、Insertion.Afterクラスにより継承されます。継承は以下のようになっています。
【参考】Abstract.Insertionクラスの継承 Insertion.Before = Class.create(); Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), { (省略) });
抽象クラス実装方法としては前述Ajax.Baseクラスに似ています。「Ajax.Baseクラス - Backstage of theater.js」を参照してください。ちょっと違うのは、コンストラクタとなるAbstract.Insertionの関数に処理があるということです。次で解読します。
Abstract.Insertionコンストラクタ
【抜粋】 Abstract.Insertion = function(adjacency) { this.adjacency = adjacency; }
Abstract.Insertionクラスのコンストラクタとなる関数*2。これは上で挙げた「Abstract.Insertionクラスの継承」の例の場合、「new Abstract.Insertion('beforeBegin')」で実行されます。initilizeメソッドはこの時点では実行されないので注意してください。initilizeメソッドが実行されるのは、各サブクラスのインスタンスが生成されるときです。
Abstract.Insertionのadjacencyプロパティには、後述の4クラスによる継承時に以下の文字列が設定されます。
クラス | adjacencyプロパティ設定文字列 |
---|---|
Insertion.Before | beforeBegin |
Insertion.Top | afterBegin |
Insertion.Bottom | beforeEnd |
Insertion.After | afterEnd |
次に述べるinitilizeメソッドで使用します。
initilizeメソッド
【抜粋】 initialize: function(element, content) { this.element = $(element); this.content = content.stripScripts(); if (this.adjacency && this.element.insertAdjacentHTML) { try { this.element.insertAdjacentHTML(this.adjacency, this.content); } catch (e) { if (this.element.tagName.toLowerCase() == 'tbody') { this.insertContent(this.contentFromAnonymousTable()); } else { throw e; } } } else { this.range = this.element.ownerDocument.createRange(); if (this.initializeRange) this.initializeRange(); this.insertContent([this.range.createContextualFragment(this.content)]); } setTimeout(function() {content.evalScripts()}, 10); },
後述のInsertion.Before、Insertion.Top、Insertion.Bottom、Insertion.Afterクラス共通のinitilizeメソッドです。第一引数は基準要素、第二引数は挿入するHTMLソースです。
IE、Operaの場合は、要素のinsertAdjacentHTMLメソッドを使用しています。insertAdjacentHTMLについては以下を参照してください。
【参考サイト】:Microsoft API and Reference Catalog
前述のadjacencyプロパティを使用しています。
ただ、「HTMLに誤りがあると、このメソッドは失敗する。」とあるので、tableタグで囲まれていないtrやtdタグの挿入はできないようです。この場合は次のcontentFromAnonymousTableメソッドでNodeを作ってから、各サブクラスが実装するinsertContentメソッドにより挿入しています。
Netscape等のMozilla系ブラウザにはinsertAdjacentHTMLメソッドがないため、別の方法で挿入します。
要素のownerDocumentプロパティはdocumentオブジェクトと(結果的に)ほぼ同義です。
【参考サイト】:http://allabout.co.jp/internet/javascript/closeup/CU20041015A/index.htm
createRangeメソッドによりRangeオブジェクトを作成しています。
【参考サイト】: http://members.jcom.home.ne.jp/jintrick/Personal/DOM_Range.html JavaScript Tips collection - Mozilla's Blues This Document has Moved This Document has Moved
サブクラスが実装するinitializeRangeメソッドを呼び出し、Rangeオブジェクトの位置を設定します。その後、RangeオブジェクトのcreateContextualFragmentメソッドでHTML文字列をDocumentFragment化し、サブクラスが実装するinsertContentメソッドで指定位置に挿入します。
【参考サイト】:http://members.jcom.home.ne.jp/jintrick/Personal/documentFragment.html
すみません、参考サイトばっかりで・・・。
createContextualFragmentメソッドについてですが、詳しいことがすぐには分からないです。どうやらMozilla系独自のメソッドみたいで、参考サイト「This Document has Moved」には記述がありません。推察するに、setStartBeforeメソッド等で位置を指定後、ソースを引数にして使用すると、その位置に適合したDocumentFragmentを返却してくれる、という動作のように思えます。サブクラス実装のinitializeRangeメソッドはそのための準備ということでしょう。
挿入後の0.01秒後、引数content中のスクリプトタグの内容をeval関数で実行しています。
contentFromAnonymousTableメソッド
【抜粋】 contentFromAnonymousTable: function() { var div = document.createElement('div'); div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>'; return $A(div.childNodes[0].childNodes[0].childNodes); }
前述initializeメソッドで、IEでテーブルに行*3を追加するために、HTML文字列からNodeを生成するためのメソッドです。こうやるんですね・・・。各サブクラスのinsertContentメソッドで使用するために、Arrayオブジェクトに変換しています。詳しくは後述の各クラスを参照してください。
例も各サブクラスで提示します。
*1:これ以前は抽象クラスにAbstractを使っていないのですが。抽象クラス実装はなぜか統一性がないですね・・・
*2:prototype.jsではClass.createメソッドを使うことが多いため、コンストラクタを直に記述するのは珍しいです
*3:以前、後続Verでセル追加が可能のように書いていましたが、間違いです・・・