今回は、検索文字列に関連するキーワードを提案するSuggest機能を実装したいと思います。
実装の前に
Suggest機能の実装に入る前に、第3回までのプログラムをちょっと整理しましょう。第3回までの実装では、とりあえずグローバルな名前空間に関数を追加していました。しかし、名前空間がどんどん汚染されよくありません。必要なものだけをグローバルな名前空間に追加しましょう。
スコープを隠蔽する
jQueryにならって、スコープを隠蔽してみることにします。次のように無名関数を使って実装します(リスト1)。
無名関数の定義(function(){})と、実行()を同時に行っています。
リスト3-(1)のlocalという変数は無名関数のローカルスコープにあるため、外部から直接参照することはできません。
リスト3-(2)のglobalのようにwindowオブジェクトに追加することにより、外部に参照を公開することができます。
Youtubeコンストラクタ関数の定義
Youtubeというコンストラクタ関数を作成し、それを基にした新しいオブジェクトをwindowオブジェクトに追加します。
第3回までのページロード時以外の実装はとりあえずここに押し込めることにします(リスト2)。
これで、名前空間の汚染は最小限になりました。
今回は、単純に名前空間 window.yt という名前空間に実装を押し込めただけで、コンストラクタ関数を作成した意味があまりありません。
必要に応じて実装を拡張してください。
外部ファイル化
さらにこの実装を、
Youtube.jsファイルとして外部ファイルに移動しました。
これで少しすっきりしましたね。
Suggest機能
それでは、Suggest機能の実装に入りたいと思います。
動作イメージ
検索テキストボックスに文字列を入力すると、検索テキストボックスの下に関連するキーワードが表示されます(図1)。
Suggest機能とは、このようにキーワードを提案(Suggest)して、検索を補助する機能です。
機能概要
今回実装する機能の要件は次のとおりです。
- 検索テキストボックスに文字列を入力すると、500ミリ秒後に関連するキーワード(以下 Suggest)を表示する。
- Suggestが表示されていない場合、検索テキストボックスで「↓」を押すとSuggestを表示する。
- Suggestが表示されている場合、検索テキストボックスで「↓」を押すとSuggestにフォーカスを移動する。
- Suggestを選択すると、検索テキストボックスに選択したSuggestを設定する。
- Suggestをダブルクリックすると、検索テキストボックスにダブルクリックしたSuggestを設定し、検索する。
- ESCキーを押すとSuggestを閉じる。
- Suggestがない場合は何も表示しない。
Suggestデータ
Youtube APIにはSuggest用のAPIやデータは用意されていません。
今回は、動画に設定されたキーワード(feed.entry[n].media$group.media$keywords.$t)をSuggestのデータとして使います。
Suggestの実装(1)
画面要素
検索テキストボックスの下に、Suggestを表示するselect要素をid=suggestとして追加します(リスト3)。
イベントハンドラ
検索テキストボックス
検索テキストボックスのイベントを定義します(リスト4)。
このイベントハンドラのコールバック関数の引数eには、イベントオブジェクトが渡されます。
イベントオブジェクトのkeyCodeを判別し、検索テキストボックスで押下されたキーに応じて処理を振り分けています。
まず、escキー(keyCodeは27)が押下されたときの処理を実装します。
Suggestを非表示にします。
続けて、↓下矢印キー(keyCodeは40)が押下されたときの処理を実装します。
Suggestが表示されていれば、Suggestの先頭項目を選択し、選択項目の内容を検索ボックスに設定します。
Suggestが表示されていなければ、yt.suggest(true) メソッドを実行してSuggestを表示します。
それ以外のキーが押下された場合は、yt.suggest() メソッドを実行してSuggestを表示します。
Suggestセレクトボックス
Suggestセレクトボックスのイベントを定義します(リスト5)。
Suggestセレクトボックスの選択項目が変更された場合は、選択されたキーワードを検索テキストボックスに設定します。
Enterキー(keyCodeは13)が押下された場合は、Suggestを非表示にし、検索を実行します。
escキー(keyCodeは27)が押下された場合は、Suggestを非表示にし、検索テキストボックスにフォーカスを移します。
Suggestセレクトボックスがダブルクリックされた場合は、Suggestを非表示にし、検索を実行します。
初期処理
ページが読み込まれたときには、Suggestのselect要素を非表示にします(リスト6)。
Suggestの実装(2)
Suggestの実行
Suggest処理を実行し、Suggestを表示します(リスト7)。
まず、実行中のSuggest処理を停止します。
引数forceがtrueであれば、前回のSuggest検索文字列にnullを代入しSuggestが必ず実行されるようにします。
preinputは、Youtubeコンストラクタ関数のprototype プロパティに定義されています。
Suggest処理を実行します。
ただ、キーが押されるたびにYoutubeへリクエストを送ることは効率的ではないので、500ミリ秒後に実行されるようにし、
検索テキストボックスに入力された文字列は、yt.do_suggest()メソッドの内部から取得します。
そして、yt.do_suggest()メソッドでは、検索テキストボックスに入力された文字列に変更があった場合のみ検索処理を実行します。
Youtube.SUGGEST_TIMEは次のように定義されています。
Suggestの停止
clearTimeoutを実行して、キューに登録されているSuggest処理があればそれをクリアします(リスト8)。
Suggest処理
検索テキストボックスに入力された文字列でビデオを検索し、ビデオに設定されているキーワードからSuggestを生成します(リスト9)。
前処理
まず、Suggestが表示されていたら非表示にします。
次に、検索テキストボックスに入力されている文字列を取得し、処理を行います。
もし、検索文字列が空もしくは前回と同じである場合には、何も処理を行いません。
最後に次回の検索文字列と比較するために、検索文字列を保存しておきます。
ajax通信定義
基本的な構造は前回までのビデオ検索のものと変わりません。
よって、successコールバック関数のSuggest生成部分を抜粋して説明します。
各ビデオのエントリをイテレーションし、ビデオに設定されているキーワードを処理します。
ビデオに設定されているキーワードがない場合は、media$keywordsプロパティ自体が存在しません。
よって次のようにしてキーワードが設定されているかどうかを判別し、キーワードがない場合には次のビデオのエントリを処理します。
$.eachのコールバック関数がtrueを返すと、次のイテレーションを実行するのでしたね
(falseを返した場合は、$.eachの処理を終了します)。
キーワードはカンマ+スペース「, 」で区切られて格納されています。扱いやすいように配列に変換します。
キーワードの配列からSuggest文字列を抜き出します。
今回、Suggestの文字列は、検索テキストボックスの文字列で始まるキーワードとしました。
$.inArray(value, array)
は、
配列array
から、value
を検索し、配列内でのインデックス番号を返します。
value
が配列に存在しない場合は、-1 を返します。
ここでは、Suggest文字列の重複を避けるため、Suggest文字列配列suggestsに存在するかどうかを判別しています。
そして、Suggest文字列からoption要素を生成し、Suggestのselect要素の子要素に追加します。
最後に、Suggest文字列が1件以上あれば、Suggestを表示します。
以上で全ての実装が終了しました。サンプルを実行して動作を確認してみてください。
まとめ
今回はSuggestを実装してみました。
第3回までの内容が理解できていれば、思ったより簡単だったと思います。
この実装では、Suggest文字列をビデオに設定されたキーワードから生成しているため、Suggestが表示されるまでに少し時間がかかります。
検索件数を少なくしたり、Suggest文字列の切り出し処理を最適化すればもう少し速くなると思います(YoutubeがSuggestAPIを公開してくれればよいのですが…)。
次回は、ユーザインターフェースライブラリjQuery UIを取り上げたいと思います。