見出し画像

Google マテリアルデザインに学ぶテキストフィールドの実装方法

僕が書いたHTML/CSSのコードを皆さんに見てもらって、改善出来る箇所を見つけたりナレッジの共有をする「このデザインどうマークアップする?」シリーズの第三弾です!

このデザインどうマークアップする?

早速ですが、このデザインどうやってマークアップしますか?

スクリーンショット 2020-07-18 22.27.42

このデザインに見覚えのある方もいるかと思います。
そうです。Google マテリアルデザインのテキストフィールドです。

今回は第一弾、第二弾とは趣向を変えて、既に実装されているものを自分なりの方法で実装し直してみます。
僕はこのようにマークアップしました。

マテリアルデザインのテキストフィールドはラベルが特徴的な動きをします。
通常時のラベルはプレースホルダーの位置に配置され、フォーカスが当たると上に逃げるように移動します。
テキストが入力されれば上に居続け、テキストが入力されていないのであればプレースホルダーの位置に戻ってきます。

画像2

このようなラベルをフローティングラベルと呼びます。

なぜプレースホルダーではないのか?

テキスト入力のinput要素を扱う時ユーザーに入力して欲しい項目をプレースホルダーで記述するのはよくある実装だと思います。
しかし、マテリアルデザインではなぜフローティングラベルを採用しているのでしょうか?

プレースホルダーは入力を始めると消えてしまいます。これでは、一通り入力を完了した後に、改めて見返した時に何の項目に入力したのかが確認できません。
autocomplete機能などで自動的にテキストが入力された場合には、プレースホルダーを一度も確認できないということも起こりかねません。

画像3

このようなプレースホルダーの弱点を克服するためにフローティングラベルという手法を採用しているということですね!

しかし、フローティングラベルを使う上でいくつか注意点もあります。
以下の記事にまとまっていますので、興味がある方は是非読んでみてください。

フローティングラベルを実装してみよう!

フローティングラベルの実装方法ですが、マテリアルデザインのコードを覗いてみると、JSが絡んでいてめんどくさそうです…。
しかし、ちょっとした工夫でCSSだけで実装ができちゃうんです!

マテリアルデザインからフローティングラベルの動きだけを抜き出したのが以下のデモです。

基本的には、div>input+labelというHTML構造でフローティングラベルが実現できます。
ポイントとしては、ラベルにinput要素の状態を伝えるコードです。

// input要素にテキストが入力されたことをラベルに伝える
.textField_input:required:valid ~ .textField_label {...}
// input要素がフォーカスされたことをラベルに伝える
.textField_input:focus ~ .textField_label {...}

*:required:valid ~(兄弟セレクタ) *と書くとテキストが入力された要素の兄弟要素を指定できるので、ラベルにinput要素の入力の有無を伝えることができます。

*:focus ~ * と書くとフォーカスされた要素の兄弟要素を指定できるのでラベルにinput要素のフォーカス状態を伝えることができます。

これでJSを使わず、CSSだけでフローティングラベルが実装できました!しかし、この方法には問題点があります。

CSSのみのフローティングラベルでの問題点

*:required:validという指定は「required属性(必須項目)が設定されているinput」と「入力値が正しい要素」を組み合わせることで、input要素の入力の有無を判別しています。
これでは、必須項目ではない項目でフローティングラベルを実現できません。
input要素の入力有無の判別は、簡単なJSで実装可能なので、JSでクラスを付け替えることでフローティングラベルを実現するのが良いと思います。

*:focus ~ *という指定はフォーカスされたinputの兄弟要素に対してのみ、スタイルが指定できます。
例えば、div>input+labelという構造でinputにフォーカスが当たった際に、div要素のスタイルを変更したいという時には使えないということです。
マテリアルデザインでは、フォーカス時にdiv要素の背景色を変更していますが、このままで再現できません。
こちらも簡単なJSを書くことでも解決できますが、:focus-withinを使いましょう。

:focus-withinを活用しよう

:focus-withinはフォーカスを持っているか、フォーカスを持った要素を含む要素を指定する疑似クラスです。

つまり、div>input+labelという構造でdiv要素に:focus-withinを利用することで、input要素のフォーカス状態を検知できます。

// inputがフォーカスされた時のスタイル
div:focus-within {...}
div:focus-within input {...}
div:focus-within label {...}

IE11を除く全てのモダンブラウザで利用可能なので、是非おさえておきましょう。

以上のように僕がフローティングラベルを実装するときには、テキスト入力はJSで、フォーカスは:focus-withinで指定することにしました。

aria-labelledbyって何?

マテリアルデザインの元のコードを覗いてみると、以下のような記述がありました。

<label>
  <input type="text" aria-labelledby="my-label-id"></input>
  <span id="my-label-id">Label</span>
</label>

aria-labelledbyってなんだろう?

「aria-labelledby属性を用いることで、制作者は、ページ上の可視テキスト要素を、フォーカス可能な要素 (フォームコントロール又はリンク) のラベルとして使用できる。」らしいです!

labelタグで全体を囲っており、input要素に対応するラベルが存在しなくなっています。そこで、spanタグをラベルとして明示的に指定するためにaria-labelledby属性を利用している感じみたいですね!

まとめ

マテリアルデザインのような既存コードを自分流に書き直してみると、かなり勉強になるのでオススメです!
今回はフローティングラベルにしている理由を調べたり、知らなかったaria-labelledby属性を知ることができました。

是非、皆さんも気になったコードを覗いてみて、どうしてその実装になったのか考えてみたり、知らない属性やプロパティがあれば調べてみたりしてみましょう!

それでは最後まで読んでいただきありがとうございました!
少しでも参考になりましたら、是非いいねとフォローをよろしくお願いします🍤

ここまで読んでいただきありがとうございました! もし気に入っていただければ投げ銭よろしくお願いします🍤 投げ銭でエビフライを食べに行きます