script.aculo.usを読み解く

第2回 controls.js(前編)Autocompleter

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

controls.js

それでは,controls.jsの前半部分のAutocompleterのコードの中身を実際に見ていきます。

0001: // script.aculo.us controls.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
0002: 
0003: // Copyright (c) 2005-2007 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
0004: //           (c) 2005-2007 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
0005: //           (c) 2005-2007 Jon Tirsen (http://www.tirsen.com)
0006: // Contributors:
0007: //  Richard Livsey
0008: //  Rahul Bhargava
0009: //  Rob Wills
0010: // 
0011: // script.aculo.us is freely distributable under the terms of an MIT-style license.
0012: // For details, see the script.aculo.us web site: http://script.aculo.us/
0013: 

著作権表示です。

0014: // Autocompleter.Base handles all the autocompletion functionality 
0015: // that's independent of the data source for autocompletion. This
0016: // includes drawing the autocompletion menu, observing keyboard
0017: // and mouse events, and similar.
0018: //
0019: // Specific autocompleters need to provide, at the very least, 
0020: // a getUpdatedChoices function that will be invoked every time
0021: // the text inside the monitored textbox changes. This method 
0022: // should get the text for which to provide autocompletion by
0023: // invoking this.getToken(), NOT by directly accessing
0024: // this.element.value. This is to allow incremental tokenized
0025: // autocompletion. Specific auto-completion logic (AJAX, etc)
0026: // belongs in getUpdatedChoices.
0027: //
0028: // Tokenized incremental autocompletion is enabled automatically
0029: // when an autocompleter is instantiated with the 'tokens' option
0030: // in the options parameter, e.g.:
0031: // new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
0032: // will incrementally autocomplete with a comma as the token.
0033: // Additionally, ',' in the above example can be replaced with
0034: // a token array, e.g. { tokens: [',', '\n'] } which
0035: // enables autocompletion on multiple tokens. This is most 
0036: // useful when one of the tokens is \n (a newline), as it 
0037: // allows smart autocompletion after linebreaks.
0038:

このコメント文を日本語に訳すと,次のとおりです。

このAutocompleter.Baseに,入力補完の機能が集約されています。補完のデータ元に応じた動作は別になっています。 この中身は,補完メニューの描画,キーボードやマウスのイベントの監視,などです。

Autocompleterは,getUpdatedChoices関数を最低限,提供する必要があります。この関数は,監視しているテキストボックスの内容の変更のつど,呼び出されます。そのとき,補完の対象になる文字列を取り出すのにthis.getToken()を呼んでください(this.element.valueに直接アクセスしないでください⁠⁠。これでインクリメンタルなトークンによる入力補完になります。各々の補完の仕組み(Ajax,その他)は,getUpdatedChoicesに納められています。

インクリメンタルなトークンによる入力補完は,Autocompleterの生成時に'tokens'オプションを指定することで自動的に有効になります。このオプションは,例えばこのように与えます。

new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });

さらに,上の例の ',' のところは配列にしてもよいので,例えば { tokens: [',', '\n'] } とすると,複数の区切りで補完できるようになります。区切りとして, '\n' ⁠改行)だけは必ず入れておくのがよいでしょう。改行の前後では補完が働くようにしたほうが便利だからです。

0039: if(typeof Effect == 'undefined')
0040:   throw("controls.js requires including script.aculo.us' effects.js library");
0041: 

effect.jsがロードされているかチェックしています。

Autocompleter.Base

0042: var Autocompleter = { }
0043: Autocompleter.Base = Class.create({
0044:   baseInitialize: function(element, update, options) {
0045:     element          = $(element)
0046:     this.element     = element; 
0047:     this.update      = $(update);  
0048:     this.hasFocus    = false; 
0049:     this.changed     = false; 
0050:     this.active      = false; 
0051:     this.index       = 0;     
0052:     this.entryCount  = 0;
0053:     this.oldElementValue = this.element.value;
0054: 
0055:     if(this.setOptions)
0056:       this.setOptions(options);
0057:     else
0058:       this.options = options || { };
0059: 
0060:     this.options.paramName    = this.options.paramName || this.element.name;
0061:     this.options.tokens       = this.options.tokens || [];
0062:     this.options.frequency    = this.options.frequency || 0.4;
0063:     this.options.minChars     = this.options.minChars || 1;
0064:     this.options.onShow       = this.options.onShow || 
0065:       function(element, update){ 
0066:         if(!update.style.position || update.style.position=='absolute') {
0067:           update.style.position = 'absolute';
0068:           Position.clone(element, update, {
0069:             setHeight: false, 
0070:             offsetTop: element.offsetHeight
0071:           });
0072:         }
0073:         Effect.Appear(update,{duration:0.15});
0074:       };
0075:     this.options.onHide = this.options.onHide || 
0076:       function(element, update){ new Effect.Fade(update,{duration:0.15}) };
0077: 
0078:     if(typeof(this.options.tokens) == 'string') 
0079:       this.options.tokens = new Array(this.options.tokens);
0080:     // Force carriage returns as token delimiters anyway
0081:     if (!this.options.tokens.include('\n'))
0082:       this.options.tokens.push('\n');
0083: 
0084:     this.observer = null;
0085:     
0086:     this.element.setAttribute('autocomplete','off');
0087: 
0088:     Element.hide(this.update);
0089: 
0090:     Event.observe(this.element, 'blur', this.onBlur.bindAsEventListener(this));
0091:     Event.observe(this.element, 'keydown', this.onKeyPress.bindAsEventListener(this));
0092:   },
0093:

42~93行目はbaseInitializeです。ここでは様々な初期化が行われます。

48行目はhasFocusフラグの初期化です。 hasFocusがtrueのとき,つまり,フォーカスがあるときにだけ,候補の検索とメニューの表示が行われます。 onKeyPressイベントでtrueになり,onBlurイベントでfalseになります。

49行目はchangedフラグの初期化です。changedがtrueのとき,つまり,内容が更新されたときだけ,候補の検索が行われます。onKeyPressイベントでtrueになり,onObserverEventイベントで,改めて候補の検索が行われたとき,またfalseになります。

50行目はactiveフラグの初期化です。候補メニューを表示中かどうかを表します。activeがtrueのとき,つまりメニューの表示中には,候補選択などのキー操作が有効になります。

onBlurイベントで入力エリアがフォーカスを失ったときfalseになり,
hasFocusでフォーカスがあるときtrueになり,
候補がひとつもないときにfalseになり,
補完が行われた直後にfalseになり,
入力内容がまだ短すぎて,補完をするには不十分であるときfalseになります。

51行目はindexの初期化です。何番目の候補を内部的に選択中かを表します。候補の検索が行われた直後は0に設定され,これで,一番目の候補が内部的に選択中になります。

ユーザーが以下の入力方法で候補メニューを選択するのに応じて変わります。

  • マウスクリック
  • マウスオーバー
  • 上矢印キー,下矢印キー

52行目はentryCountの初期化です。これは候補の数を表します。候補の検索のたび,変わります。

53行目はoldElementValueの初期化です。以前の内容を保存しておき,キャレット位置の計算に使います。Ajaxが失敗したときの内容の復帰にも使います。

55行目は,optionsの初期化です。setOptionsフックがある場合は,それを呼んでやります。

60行目はoptions.paramNameの初期化です。hoge.cgi?paramName=fooの"paramName"の部分を設定します。デフォルトではAutocompleterになる要素のnameプロパティの値が使われます。

61行目はoptions.tokensの初期化です。トークン区切り文字の設定です。

62行目はoptions.frequencyの初期化です。デフォルトでは0.4秒を小休止の閾値とします。

63行目はoptions.minCharsの初期化です。デフォルトでは,入力エリアの内容が1文字以下のときは,短すぎるとして補完をしません。

65行目は,options.onShowの初期化です。候補メニューを動的に表示する関数を与えています。0.15秒でフェードインするようにしています。候補メニューのHTML要素のCSSのposition属性が設定されていない,もしくは'absolute'と設定されている場合は,Position.cloneメソッドで候補メニューの位置を調整し,入力エリアの直下に表示します。'relative'と設定されている場合は位置を調整しません。

75行目は,options.onHideの初期化です。候補メニューの表示をやめる関数を与えています。0.15秒でフェードアウトするようにしています。

81行目で,トークンの区切り文字に改行だけは必ず設定しています。

86行目で,ブラウザが持っているオートコンプリート機能が適用されないようにします。

88行目で,候補メニューを非表示にします。

90行目で,フォーカスを失ったときのイベントハンドラを設定します。

91行目で,キー押下のときのイベントハンドラを設定します。

著者プロフィール

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

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

ブログ:Gemmaの日記