Hatena::ブログ(Diary)

フリーフォーム フリークアウト Twitter

2010-02-13

XULのtextboxのオートコンプリート 【追記あり】

| 00:16 |

XULのtextboxには"Toolkit autocomplete"という仕組みがあります.

textbox (Toolkit autocomplete) - XUL | MDN

これを使うと, 入力履歴からの自動補完などが簡単に実現できます. しかし, フォームの入力履歴からの補完で少しつまってしまったので, ここにメモしときます.

基本

<textbox type="autocomplete" autocompletesearch="history"/>

こんな風に, type属性に"autocomplete"と指定してあげるだけで自動補完してくれるようになります. autocompletesearch属性には, どの情報から補完するかを指定します. 指定できるのは以下の3種類のようです.

  • history: ロケーションバーのurl入力履歴から補完します.
  • form-history: フォームの入力履歴から補完します.
  • file: ファイル名から補完します. ローカルのファイルからのようです.

スペース区切りで複数指定もできます. これらとは別に独自のデータから補完させたい場合は, XPCOMコンポーネントを書かないといけません. 詳しくは下のチュートリアルをどうぞ.

How to implement a custom autocomplete search component | MDN

form-historyの使い方

autocompletesearchが"history"の場合は, 本当にtextboxの属性を加えるだけでokなんですが, form-historyの場合は少しjsの方にも手を加える必要があります.

まずはxulの方の説明から. "type", "autocompletesearch"に加えて"autocompletesearchparam"という属性も追加します. これはフォームを識別するラベルのようなもののようです.

<textbox id="my-form" type="autocomplete" autocompletesearch="form-history" autocompletesearchparam="my-form-history"/>

次にjsのコード. "Components.interfaces.nsIFormHistory2"というコンポーネントのaddEntry()というメソッドに, 先ほどのautocompletesearchparamで指定した名前と追加したい文字を渡します. こうすることで, 渡した文字列が履歴として保存されます. textboxのonkeypressやフォームのsubmitボタンなどに, このコードを呼び出す関数を渡しておくと, フォームにテキストが入力される度にそのテキストが履歴として保存されていきます. これだけで補完してくれるようになります.

function addFormHistory() {
  var entry = document.getElementById('my-form').value;
  var formHistory = Components.classes["@mozilla.org/satchel/form-history;1"]
    .getService(Components.interfaces.nsIFormHistory2 || Components.interfaces.nsIFormHistory);
  formHistory.addEntry("my-form-history", entry);
}

こちらのコード例も参考にしてください.

xul - Save drop-down history in a Firefox Toolbar - Stack Overflow

こちらのchaikaという2chブラウザのコードも参考にさせてもらいました.

Error 404 (Not Found)!!1

nsIFormHistory

(下の節に追記あり. あわせてお読みください. 2010-02-20)

一つ謎だったのが, nsIFormHistoryというコンポーネントです. 手元の環境(Firefox 3.6, Mac OSX)でこのコンポーネントを呼び出すと, 以下のエラーが返ってきました.

Error: Component returned failure code: 0x80570018 (NS_ERROR_XPC_BAD_IID) [nsIJSCID.getService]

代わりにnsIFormHistory2を呼び出すとうまく動作しました. よくわからないのですが, 上記のstackoverflowの解答で,

  .getService(Components.interfaces.nsIFormHistory2 || Components.interfaces.nsIFormHistory);

と両方に対応できるようにしていたので, 現状これを真似しています.

追記(2010-02-20)

"nsIFormHistory"は現在もう無いそうです.

mozilla-central mozilla/toolkit/components/satchel/public/nsIFormHistory.id

Firefox1.5までは"nsIFormHistory"だったのですが, Firefox2.0より"nsIFormHistory2"に変わったようです.

mozilla1.8 mozilla/toolkit/components/satchel/public/nsIFormHistory.idl (Firefox2.0)

mozilla1.8.0 mozilla/toolkit/components/satchel/public/nsIFormHistory.idl (Firefox1.5)

というわけで, Firefox2.0以降に対応するだけだったら, nsIFormHistory2にしておけば大丈夫のようです.

  var formHistory = Components.classes["@mozilla.org/satchel/form-history;1"]
    .getService(Components.interfaces.nsIFormHistory2);

コメントにてid:piro_orさんに教えていただきました. ありがとうございます!


参考リンク

piro_orpiro_or 2010/02/15 18:57 nsIFormHistoryという名前のインターフェースは、現在ではそもそも定義されていないようです。

http://mxr.mozilla.org/mozilla-central/source/toolkit/components/satchel/public/nsIFormHistory.idl (Trunk)

古いソースを見てみた感じでは、Firefox 1.5まではnsIFormHistoryだったのが、Firefox 2からnsIFormHistory2になったみたいです。

http://mxr.mozilla.org/mozilla1.8/source/toolkit/components/satchel/public/nsIFormHistory.idl (Firefox 2)
http://mxr.mozilla.org/mozilla1.8.0/source/toolkit/components/satchel/public/nsIFormHistory.idl (Firefox 1.5)

ということで、Firefox 2以降専用のコードなのであれば Components.interfaces.nsIFormHistory2 だけ書いておけばよさそうです。

cou929_lacou929_la 2010/02/20 18:24 なるほど, 名前が変わっていたんですね. ありがとうございます! 早速本文の方にも追記させていただきました.