転職にともないフルスタックエンジニアとして仕事をするようになったので、フロントエンド技術にも手を出すことが増えてきた。この領域は不得手で苦労している。

先日、仕事の一部としてボタン画像 (i.e., <button /> の中に <img /> を入れて、画像をクリック可能にしたもの) を実装した。このとき、クリック後のボタンには「選択済み」であることを示すために枠線をつけたいという要望があり、この課題にどうアプローチしたかについて、メモを残そうと思う。

パターン1 (JavaScript を使わないパターン)

先に実装を示す。

ボタンの縦幅と横幅を 104px 取っている。これは 100px の画像に、二本ずつ 2px のボーダーを入れるため 100 + (2 * 2) で 104px と計算している。

初期状態ではボタンに透明なボーダーを入れており、ボタンが押されたとき (= :focus 擬似クラスが有効になったとき) にボーダーの色を red に変更している。

次の部分はボタン内の画像を中央寄せするために書いている。

.button {
    display: flex;
    justify-content: center;
    align-items: center;
    ...
}

画像は縦幅と横幅が 100px で表示しているため、透明なボーダーの中にぴったり収まる。

パターン2 (JavaScript を使うパターン)

こちらも先に実装を示す。

JavaScript でボタンがクリックされると buttonClick クラスが付くようにしている。buttonClick::before で疑似要素を設定している。これは display: block;content: ""; が設定されているので、空のブロック要素として振る舞う。また position: absolute; かつ上下左右 (e.g., topleft など) が指定されていないため、この疑似要素はボタンを覆いかぶさるように配置される。この疑似要素の border: 2px solid red; の指定により、全体としてボタンに枠線が描かれる。

ところで box-sizing: border-box; については MDN から説明を引用しよう:

border-box は、境界およびパディングを、要素に指定した width および height の中で取るようブラウザーに指示します。要素の width を 100 ピクセルに設定した場合、100 ピクセルの中に追加した境界やパディングが含まれ、コンテンツ領域はそれらの幅を吸収するために縮小されます。これで普通は、要素に対するサイズ設定をもっと簡単になります。

つまり box-sizing: border-box; を指定することで 100px の中にボーダーを含めて疑似要素のすべての内容が収められる。このケースでは上下左右 2px ずつボーダーがボタン画像を上書きすることになる。