script.aculo.usを読み解く

第13回 dragdrop.js(後編)

この記事を読むのに必要な時間:およそ 14 分

今回のSortableクラスは,ulタグ要素を与えるだけで,中のliタグ要素に全自動でドラッグ&ドロップ機能を適切に割り当てて,並び替えできるリストに変身させてしまう機能を提供します。また,並び替えできるツリーという機能もあり,これはulタグ要素の中のulタグ要素といったツリー構造を扱うために,コードが複雑です。ツリー構造の走査には,再帰呼び出しが活躍します。

SortableObserver

ドラッグの開始,途中,終了という3種のフックをまとめたクラスです。将来的にはObserverクラスを作ろうと考えているのでしょうが,その必要性を計りかねているようです。

0576:var SortableObserver = Class.create({
0577:  initialize: function(element, observer) {
0578:    this.element   = $(element);
0579:    this.observer  = observer;
0580:    this.lastValue = Sortable.serialize(this.element);
0581:  },
0582:  

576~582行目のinitializeは,インスタンスの初期化をする関数です。引数にドラッグする要素のDOM idと,ドラッグの終了時に呼ぶフックをとります。

580行目で,lastValueプロパティには,serialize関数の結果が入ります。これは並び順を文字列に出力したものです。

0583:  onStart: function() {
0584:    this.lastValue = Sortable.serialize(this.element);
0585:  },
0586:  

583~586行目のonStart関数は,ドラッグの開始時に呼ばれるフックです。このときの並び順を保存しておきます。

0587:  onEnd: function() {
0588:    Sortable.unmark();
0589:    if(this.lastValue != Sortable.serialize(this.element))
0590:      this.observer(this.element)
0591:  }
0592:});
0593:

587~593行目のonEnd関数は,ドラッグの終了時に呼ばれるフックです。

588行目のunmark関数は,後述するように,マークを非表示にする関数です。

589行目で,ドラッグ開始時と比べて,並び順に変化があれば,observerフック(実体は734行目にあるように,options.onUpdateです)を呼びます。

Sortable

0594:var Sortable = {
0595:  SERIALIZE_RULE: /^[^_\-](?:[A-Za-z0-9\-\_]*)[_](.*)$/,
0596:  
0597:  sortables: { },
0598:  

595行目で,SERIALIZE_RULEは,この正規表現に一致するDOM idを持ったアイテム要素だけを処理の対象にします。この正規表現から分かるように,アイテム要素のDOM idは"名称_アイテムID"の形になっている必要があります。

597行目のsortablesは,このクラスのインスタンスを保持するハッシュテーブルです。キーにインスタンスのコンテナ要素のDOM idを,値にそのoptionsをとります。

0599:  _findRootElement: function(element) {
0600:    while (element.tagName.toUpperCase() != "BODY") {  
0601:      if(element.id && Sortable.sortables[element.id]) return element;
0602:      element = element.parentNode;
0603:    }
0604:  },
0605:

599~605行目の_findRootElementは,引数の要素のDOM階層を上にたどって,特に,並び替えリストのコンテナになっている親要素を求める関数です。

0606:  options: function(element) {
0607:    element = Sortable._findRootElement($(element));
0608:    if(!element) return;
0609:    return Sortable.sortables[element.id];
0610:  },
0611:  

606~611行目のoptionsは,引数の要素に上述の_findRootElement関数を使って,そのコンテナになっている親要素のoptionsを返す関数です。

0612:  destroy: function(element){
0613:    var s = Sortable.options(element);
0614:    
0615:    if(s) {
0616:      Draggables.removeObserver(s.element);
0617:      s.droppables.each(function(d){ Droppables.remove(d) });
0618:      s.draggables.invoke('destroy');
0619:      
0620:      delete Sortable.sortables[s.element.id];
0621:    }
0622:  },
0623:

612~623行目のdestroyは,引数の要素に上述のoptions関数を使って,そのコンテナになっている親要素から並び替えリストの機能を取り除く関数です。

616行目で,DraggablesクラスのremoveObserver関数でフックを外します。

617行目で,その並び替えリストを構成するDroppablesクラスのインスタンスたちを,それぞれremove関数で削除します。ここで618行目のようにinvoke('remove')を使えないのは,同名の関数であるPrototype.jsのElement.removeが優先して呼ばれてしまうからです。

618行目で,その並び替えリストを構成するDraggablesクラスのインスタンスたちにinvokeメソッドを使って,それぞれdestroy関数で削除します。

620行目で,sortablesハッシュテーブルからこのインスタンスを削除します。

著者プロフィール

源馬照明(げんまてるあき)

名古屋大学大学院多元数理科学研究科1年。学部生のときにSchemeの素晴らしさを知ったのをきっかけに,関数型言語の世界へ。JavaScriptに,ブラウザからすぐに試せる関数型言語としての魅力と将来性を感じている。

ブログ:Gemmaの日記

コメント

コメントの記入