michimani.net

テキストボックスで半角スペース区切りの文字を判別しやすくする

2019-02-06

やりたいこと

Qiita の記事編集画面ではタグを入力するテキストボックスがあります。あそこは半角スペース区切りで文字を入力すると、その区切りごとに文字の背景色がついて、どんなタグが設定されるのか判別しやすくなります。
この UI はタグ付けだけではなく検索窓とかにも使えそうなので、今回はこの UI を再現してみます。

やってみた

ということで、やってみた結果です。下のテキストボックスに文字を入力すると、半角スペース区切りで背景色がつきます。

“背景色” と言いながら、実は被せている

このテキストボックスの HTML と CSS は次のようになっています。

<div id="overlay-sample">
    <div id="overlay-textbox-area">
        <div id="overlay-area"></div>
        <input type="text" autocomplete="off">
    </div>
</div>
#overlay-sample {
    margin: 30px;
    font-family: inherit;
}

#overlay-area {
    color: transparent;
    letter-spacing: .02em;
    margin: 2px;
    overflow-wrap: break-word;
    overflow: hidden;
    padding: 13px 12px;
    position: absolute;
    white-space: pre-wrap;
    width: 100%;
    font-size: 16px !important;
}

#overlay-area span.overlay {
    background-color: #ffcc80;
    border-radius: 2px;
    box-shadow: 0 0 0 1px #ffa726;
    padding-bottom: 1px;
    padding-top: 1px;
}

#overlay-textbox-area input[type=text] {
    font-family: inherit;
    font-size: 16px !important;
    letter-spacing: .02em;
    outline: 0px;
    padding: 6px 12px;
    position: relative;
    width: 100%;
}

色々と細かいスタイルを設定していますが、肝心なのは次の 2 点です。

  1. 背景色は背景ではなくオーバーレイ
  2. テキストボックスとオーバーレイ要素のフォント・サイズを合わせる

1. 背景色は背景ではなくオーバーレイ

背景色と言いながらも、実際は背景色がついた要素を上から被せています。上にかぶせているのが #overlay-area span.overlay で指定されている要素です。例えば Hello World!! こんにちは 世界 と入力したときの HTML は次のようになっています。

<div id="overlay-sample">
    <div id="overlay-textbox-area">
        <div id="overlay-area"><span class="overlay">Hello</span> <span class="overlay">World!!</span> <span class="overlay">こんにちは</span> <span class="overlay">世界</span></div>
        <input type="text" autocomplete="off" value="Hello World!! こんにちは 世界">
    </div>
</div>

テキストボックスの上に span.overlay が乗るようにしています。 span.overlay の背景色は好きな色にします。そして、その親要素である #overlay-area には、透過されるように color: transparent; を指定します。

2. テキストボックスとオーバーレイ要素のフォント・サイズを合わせる

これが一番のポイントですが、テキストボックスとオーバーレイ要素のフォントとサイズは一致させておく必要があります。また、文字幅を指定する letter-spacing についても、同じスタイルを適用する必要があります。
当たり前といえばそれまでですが、テキストボックスに対しては別のスタイルが適用されたりすることもあるので、明示的に同じスタイルが適用されるようにします。上の例では font-family: inherit; でスタイルを継承しています。

テキストの入力・変更は JS で監視

文字入力イベントについては JavaScript で監視します。実際には、テキストエリア内でのキーダウンをトリガーとして、入力された文字を半角スペースで分割 → span 要素を追加 しています。

const textElem = document.querySelector('#overlay-textbox-area input[type=text]');
textElem.addEventListener('keyup', (event) => {
    renderOverLay(event.target.value);
});

function renderOverLay(text) {
    const overlayArea = document.getElementById('overlay-area');
    const charPartsList = text.split(' ');
    let spanSource = '';
    for (let i = 0; i < charPartsList.length; i++) {
        if (spanSource !== '' && !spanSource.match(/ $/)) {
            spanSource += ' ';
        }
        if (charPartsList[i] === '') {
            spanSource += ' ';
        } else {
            spanSource += `<span class="overlay">${charPartsList[i]}</span>`;
        }
    }

    overlayArea.innerHTML = spanSource;
}

このとき、DOM 要素を追加する要素 (今回であれば .overlay-area ) に対して white-space: pre-wrap; を適用しておく必要があります。

まとめ

ソースは gist に置いてます。


comments powered by Disqus