タブでフォーカスが当たるようにしたままラジオボタンのスタイルを変える

公開:2021/01/14
更新:2021/01/14
3 min読了の目安(約3200字TECH技術記事

解決したい課題

  • フォームを作成する際にラジオボタンを使うのだが、デフォルトのラジオボタンから見た目を変更したい
  • 当然、選択状態になった場合と未選択の状態でスタイルも変えたい
  • スタイルを変更しつつもtabでフォーカスが当たるままにしたい
  • フォーカスが当たったらスタイルが変化するようにしたい
  • さらに尚且つ、デフォルトのvalidation messageは表示されるようにしたい

実際に動かしてみる(from Runstant)

http://runstant.com/supermuscles/projects/81bc262e

コード

index.html
index.html
  <body>
    <form id="form">
      <div class="box">
        <span class='title'>Radio</span>
        <label class='label'>
          <input type="radio" name="radio" value='type1' required/>
          <span class='radio unchecked'></span>
          <span class='radio checked'></span>
          <span class='radio_text'>type1</span>
        </label>  
        <label class='label'>
          <input type="radio" name="radio" value='type2' required/>
          <span class='radio unchecked'></span>
          <span class='radio checked'></span>
          <span class='radio_text'>type2</span>
        </label>
      </div>
      <div class="box">
        <span class='title'>Text</span>
        <input type="text" name="text" required/>
      </div>
      <button id='button'>submit</button>
      
    </form>
  </body>
main.css
main.css
  input[type=radio] {
    position: absolute;
    opacity: 0;
    appearance: none;
    z-index: -1;
    transform: scale(0.1);
  }
  
  input[type=radio]:checked {
    position: absolute;
    top: 0px;
    left: 0px;
    appearance: none;
  }
  
  input[type=radio]:focus ~.unchecked {
    display: inline-block;
    color: red;
  }
  input[type=radio]:focus ~.checked {
    display: none;
    color: red;
  }
  
  input[type=radio]:checked ~.unchecked {
    display: none;
    color: red;
  }
  
  input[type=radio]:checked ~.checked {
    display: inline-block;
    color: red;
  }
  
  input[type=radio]:invalid {
    appearance: auto;
  }
  
  .radio {
    margin-right: 8px;
  }
  
  .checked {
    display: none;
  }
  
  
  .box {
    display: flex;
    margin-bottom: 16px;
  }
  .label {
    display: flex;
    margin-right: 8px;
    cursor: pointer;
  }
  
  .title {
    margin-right: 16px;
    font-weight: bold
  }

説明

今回やりたいことの要点としては1. デフォルトのラジオボタンを消す2. check状態とfocus状態でスタイルを変える3. デフォルトのバリデーションメッセージは表示されるようにする、の3つです。
それぞれの解決方法は下記の通りです。

デフォルトのラジオボタンを消す

  • こちらについては、最初display: none;で存在を消すようにしていましたが、こうするとラジオボタンにrequiredがついていた場合、既存のformではエラーが出てしまいます。
  • どうやら存在自体は消してはいけないっぽいので、opacity: 0;にして見た目上消すようにしました。
  • ただ、それだけだと存在が邪魔なので、カスタムしたラジオボタンと位置がかぶるようにposition: absolute;z-index: -1;にて隠しました。
  • 加えて、邪魔しないようにtransform: scale(0.1);物理的に小さくしておきました(ここら辺はもっと別にやりようがありそう)。

check状態とfocus状態でスタイルを変える

  • これはとてもシンプルに、デフォルトのCSSのセレクターの:focus:checkedで表示を出し分けることにしました。
  • check状態の時は.uncheckedがついたタグは非表示にして、.checkedがついたタグを表示します。
  • focus状態の時は.uncheckedがついたタグの色を変えたりしました。

デフォルトのバリデーションメッセージは表示されるようにする

  • opacityで表示を消した状態だと、バリデーションメッセージ自体も透明になってしまいます。
  • バリデーションにひっかかった時は出すようにしたかったので、:invalidセレクターでappearance: auto;をセットするようにしました。
  • こうすることで再度ラジオボタンを表示させています(見た目上隠れてははいるが裏側に存在している)。

まとめ

今回作ったものはカスタムボタンというにはとてもダサい☆ですが、ここはmaterial icon使ったり画像使ったりもっと色形をこったりすればどうとでもなります。

最近formタグでぶつかる壁が多いので、いろいろと調べていたらとても勉強になりました。
formタグはうまいこと使えばJavascriptで頑張らなくて良くなるので、何が使えるのか覚えて積極的に使っていきましょう。

以上です。