Facebookライクにテキストエリアを強調する方法

Facebook にハッシュタグが登場しましたね。まだ私の周りではそこまで目にしませんが、皆様は活用してますでしょうか?

さて、このハッシュタグを入力するフォーム、一見すると通常のテキストエリアですが、入力した後にハッシュタグの所に強調がかかり、とても分かりやすくなっています。このエントリではこの強調がどうやって実現されているかを紹介したいと思います。

まず全体の構造はとてもシンプルです。まず以下の図をご覧ください。

上図のように

  • 元のテキストエリア (A) の下敷きになるレイヤ(B) を生成し、位置と幅をあわせます
  • テキストエリアに文字の入力があった場合、同じ内容を (B) 側にも書き出します
  • ハッシュタグを検知したら、その部分だけ (B) 側では b タグに置換します

ポイントとなる、テキストエリア(A)とレイヤ(B) 、及び (B) 以下の b タグのスタイルを抜粋します。

/* テキストエリア (A) */
textarea {
    background: transparent;
}

 /* レイヤ (B) */
div.Layer {
    color: transparent;
}

/* レイヤ (B) のハッシュタグ */
div.Layer span b {
    background: #d8dfea;
    background: linear-gradient(#dce6fb, #bdcff1);
    box-shadow: 0 0 0 1px #a3bcea;
    border-radius: 2px;
    font-weight: normal;
}

まず (A) は背景を透過にすることで、下にある要素が表示されるようにし、(B) 側では文字そのものは表示しないようにし、そのかわり b タグの部分だけ背景がつくようにしてあります。( 簡単のためブラウザ依存のプロパティは省略しています )

各々が適用されて重なっていくイメージは以下のようになります。

テキストエリアの内容を都度コピーするあたりは大掛かりに思えますが、むしろ強調する領域の位置あわせは一切考えなくてよかったり、ブラウザ間の調整を行いやすくしているあたり、よく考えられたスマートな方法だと思います。ただしその仕組み上、レイヤ側で出来る装飾は文字位置を変えるようなものは使えない、という制約はあります。

折角なので、こちらを jQuery プラグインとして実装してみました。サンプルは以下にあります。興味のある方は是非ソースも含めてご覧ください。

jQuery プラグイン中で、元のテキストエリアから下敷きのレイヤを生成する部分が以下となります。スタイルなども一部あわせてますが、このあたりは CSS 側に切り出してしまってもよいと思います。

var underlay = function(textarea) {

    var $elem = $(textarea), oldval = $elem.val(),
    // レイヤに適用する mixin
    mixin = {
        cssSync : function() {
            return this.css({
                padding : $elem.css('padding'),
                margin : $elem.css('margin'),
                "line-height" : $elem.css('line-height'),
                "font-size" : $elem.css('font-size')
            });             
        },          
        adjust : function() {
            var pos = $elem.position();
            return this.css({
                width : $elem.outerWidth() + 'px',
                left : pos.left + 'px',
                top : pos.top + 'px'
            });             
        },          
        refresh : function() {
            var re = new RegExp(/(^|\s)#(\S+?)(\s|$)/g), v = $elem.val();
            var escaped = $('
‘).text(v).html(); var html = escaped.replace(re, function(m, p1, p2, p3, offset, str) { return p1 + ‘#’ + p2 + ‘‘ + p3; }); return this.children(‘span’).html(html).end(); } }, // レイヤ本体 $div = $(‘

 

‘).insertBefore($elem) .extend(mixin).cssSync().adjust(); return { update : function() { var curval = $elem.val(); if (curval !== oldval) { $div.refresh(); oldval = curval; } } }; };

いかがでしたでしょうか?

Facebook では、ハッシュタグだけでなくメンションも同じ仕組みで実現しており、実際はもう少し複雑 ( テキストエリアの情報から一旦メタテキスト情報を生成し、それを元にオーバーレイエリアを描画しているようなイメージ ) ですが、基本的な構造はこのエントリで紹介した通りです。

Backlog では、担当者の絞り込み機能についてのエントリ でも紹介しましたが、標準のフォーム部品では実現できない事でもより良い UI の機能として提供できるよう、各種サービスやライブラリを調べたり、試したりしています。

今回の Facebook のテキストエリアのアイディアは、年初にリリースした お知らせ機能 の開発時に、お知らせするユーザを選択する UI の仮実装で利用していました。ですが、自分たちで使ってみた結果 Backlog には向かないだろうということでお蔵入りしたものです。今回 Facebook ハッシュタグの登場にあわせて、アイディアだけでも日の目にあたればと思い、紹介いたしました。

この UI を調査した時に痛感したのは、エンジニアといえど、CSS や HTML といったフロントエンドの技術にも精通していないと、これを「思いつく」事は難しいだろうな、ということです。

やはり、世の中「フルスタックエンジニア」が求められているのだな
、、、
、、、
、、、
という事でお後がよろしいようで。

より良いチームワークを生み出す

チームの創造力を高めるコラボレーションツール

製品をみる