Nwht0xn1

At.jsで絵文字やmentionを自動補完するCreated on 2016-07-15 by r7kamura

絵文字やmentionを自動補完できるようになりました - WikiHub Help で利用した実装についての話です。

horsey

最初は https://github.com/bevacqua/horsey を利用して実装しましたが、補完候補確定後に不要なポップアップが現れてしまう問題があり、これはデモでも発生していたので、At.jsに乗り換えました。horseyのデモは https://bevacqua.github.io/horsey/ にあります。

At.js

WikiHubではこの実装に At.js というライブラリを使いました。At.jsの実装は https://github.com/ichord/At.js に、デモは http://ichord.github.io/At.js/ にあります。デモの見た目がhorseyの方が落ち着いていて良いなと思ったんですが、実際使ってみると案外そうでもなく、At.jsも少しだけ調整するといい感じになったかなと思います :lemon:

コード

At.js はjQueryのプラグインという形式で実装されているので、このように使います。WikiHubでは自動補完を利用したいtextarea要素に js-autocompletion というclass属性を付けてることになっています。At.jsによってjQuery.fn.atwhoというFunctionが提供されており、これを利用します。コード見たほうが早いですね。これはmentionとemojiについてそれぞれ設定を施しており、@ のあとに文字が入力されるとmentionが、: のあとに文字が入力されるとemojiが自動補完されるという状態です。

jQuery(function ($) {
  $('.js-autocompletion').atwho({
    at: '@',
    callbacks: {
      remoteFilter: function (query, callback) {
        if (query.length) {
          $.ajax({
            data: {
              query: query
            },
            credentials: 'include',
            method: 'GET',
            url: '/users',
          }).done(callback);
        }
      }
    },
    displayTpl: function (user) {
      return '<li>' + '<img class="avatar-img avatar-img-small" src="' + user.image_url + '" width="20" height="20" />' + user.name + '</li>';
    },
    insertTpl: function (user) {
      return '@' + user.name;
    }
  }).atwho({
    at: ':',
    callbacks: {
      remoteFilter: function (query, callback) {
        if (query.length) {
          $.ajax({
            data: {
              query: query
            },
            credentials: 'include',
            method: 'GET',
            url: '/emojis',
          }).done(callback);
        }
      }
    },
    displayTpl: function (emoji) {
      return '<li>' + '<img src="' + emoji.image_url + '" width="20" height="20" />' + emoji.name + '</li>';
    },
    insertTpl: function (emoji) {
      return ':' + emoji.name + ':';
    }
  });
});

remoteFilter という名前でサーバから補完候補を取ってこれるということが分かりづらく、ドキュメントでもそういうサーバと通信するサンプルが少なかったんですが、試してみたところ上手くいきました。

サーバ側

ちなみにサーバ側も書いておくとこういう状態です。Railsで書いてあって、EmojisControllerクラスのindexメソッドが対応しています。

class EmojisController < ApplicationController
  EMOJI_TABLE = ::Emoji.all.each_with_object({}) do |character, hash|
    character.aliases.each do |string|
      hash[string] = character.name
    end
  end

  before_action :forbid_access_from_guest, only: :index
  before_action :require_query_parameter, only: :index

  def index
    keys = EMOJI_TABLE.keys.select { |key| key.include?(params[:query]) }
    values = EMOJI_TABLE.values_at(*keys)
    names = values.uniq.sort
    render(
      json: names.map do |name|
        ::Wikihub::Representations::Api::Emoji.new(name)
      end
    )
  end

  private

  def require_query_parameter
    unless params[:query].is_a?(String)
      head 400
    end
  end
end