GitHubのようなtextareaの補完機能を実装する - カーソル位置の取得 を書いたのも今は昔、いつか続きを書こう書こうと思いながら気がつけば5ヶ月が過ぎました
なんか続きを書くのが面倒くさくなったのと、某日本最大レシピ共有サイトの技術部長の人から「OSSにして欲しい」という要請を人伝に受け取ったこともあって、OSS化した次第です。
ライセンス
MITライセンス
簡単な使い方
簡単に説明します。詳しくは README を読んでください。
まず jQuery.textcomplete は名前からも分かるように jQuery プラグインになっているので、別途 jQuery が必要です。
<script src="path/to/jquery.js"></script>
<script src="path/to/jquery.textcomplete.js"></script>
TEXTAREA 要素の jQuery オブジェクトに対して textcomplete
メソッドを実行します。
$('textarea').textcomplete({
// mentionは単なる名前で意味はありません。
// 分かりやすい名前をつけてください。
mention: {
// 必須設定
match: /(^|\s)@(\w*)$/,
search: function (term, callback) {
// callback には文字列の配列を渡す
$.getJSON('/search', { q: term })
.done(function (resp) { callback(resp); })
.fail(function () { callback([]); });
},
replace: function (value) {
return '$1@' + value + ' ';
},
// 任意設定(下記はいずれもデフォルト値)
index: 2,
maxCount: 10,
template: function (value) {
return value;
}
},
// 複数設定することもできます
emoji: { … }
});
match
は各 Keyup イベントで TEXTAREA の先頭からカーソル位置までの文字列に対して適応される正規表現を指定します。この正規表現がマッチした場合、 index
番目のグループが search
の第一引数 term
として渡されます。
search
では与えられた term
に対応する文字列の配列を作って callback
を実行します。この例では jQuery.getJSON
を使ってリモートからデータを取ってきています。今回の例のように search
の中に非同期処理が入っていたとしても、 search
は同時に1回しか実行されないことが保証されます。また配列に100個の要素が入っていたとしても先頭から maxCount
個だけが表示されます。
callback
が実行されると与えられた配列の個々の要素に対して template
が実行されます。この関数の返り値が補完の各要素になります。上の方に貼り付けられた gif の場合、補完メニューに画像が埋め込まれていますが、これを実現するには例えば下記のようにします。
template: function (value) {
return '<img src="path/to/' + value + '.png"/>' + value;
}
最後に補完が確定したときに replace
に選択された値が与えられて実行されます。この関数の返り値は次のように利用されるので、サンプルを参考に、条件を満たすように指定してください。String.replaceの詳しい仕様については String.replace - JavaScript | MDN を参照してください。
textarea.value = textarea.value.replace(matchRegExp, replaceFunc(value));
デザイン
出力される補完メニューはBootstrapのDropdownと同じHTML構造をしています。なので、Bootstrapを使っている環境では特に何もしなくてもいい感じにスタイルが勝手に適応されます。
Bootstrapを使っていない場合は、BootstrapのDropdownを参考によしなにスタイルを書いてください。リポジトリに jquery.textcomplete.css というファイルがあるので、それを土台に使うといいかも知れません。
今後実装される予定の機能
-
あいまい検索機能
各利用者が
search
関数を実装しなければいけませんが、その中で使えるあいまい検索用の組み込み関数を用意しようと思っています。search: function (term, callback) { var data = this.fuzzySearch(term, /* データ配列 */); callback(data); }
こんな感じで書くと勝手にいい具合に検索してくれる、みたいなイメージです。