WordPressのプラグイン「Contact Form 7」で出力されたフォームをちょっとカスタマイズ──その③

WordPressのプラグイン「Contact Form 7」で出力されたフォームをちょっとカスタマイズ──その③

セレクトの文字色を選択項目に合わせて変化させる

メールフォームのカスタマイズ内容は以下のとおりで、今回は下記3番目の項目の解説を。

  1. テキストフォーム、テキストエリアに表示される入力見本の文字色を統一
  2. 住所の一部を郵便番号を元に自動入力
  3. セレクト項目の文字色をテキストインプット部分、テキストエリアの色の変化に合わせる
  4. チェックボックス、及びラジオボタンのON/OFFを分かりやすく&スマホでもタップしやすく
  5. ラジオボタンとテキスト入力の組み合わせ項目をできる限り分かりやすく
  6. ファイルの添付のボタンをカスタマイズ
  7. 利用規約に同意することで送信ボタンを活性化

サンプルページ

第1回目の記事で、テキストインプット部分(input[type="text"])やテキストエリア(textarea)の欄に薄い色で表示されている文字の色をブラウザ間で違いが出ないように設定したが、セレクト項目(select)もその挙動に合わせるためにJavaScript(jQuery)とCSSの組み合わせで(完璧じゃないけど)なんとかする方法。

ちなみに、サンプルページ・ソースの800行目~858行目までをWordPressの「Contact Form 7」にぶち込んでもらえれば、サンプルページと同じ内容のフォームが作成できる。

セレクト項目の最初は「選択してください」に

下準備として、セレクト項目の最初はやはり「選択してください」となっていた方がいいよね、ということでContact Form 7にフォームを登録する際のselect部分の書き方(作り方)は以下のように。

[select address-region id:id-region class:p-region first_as_label "選択してください" "北海道" "青森県" "岩手県" ……以下略]

Contact Form 7が用意しているオプションの一つ「first_as_label」を入れてあげると、「最初の値はラベルとして使う」という効果が発動し、選択を促す文言などを設定できるようになる。

セレクト項目の文字色もplaceholderの文字色の変化に合わせる

さて、ここからが本題であるが、セレクト項目の文字色に関して最初の「選択してください」については、テキストインプット部分やテキストエリアの入力見本の文字色のようにうっすらと見える風にして、別の文字列が選択されたらハッキリとした通常の文字色にしたいでしょう、そうでしょう。というのがこのカスタマイズの出発点。

しかし、そう思ったとしてもCSSだけの制御では難しい…というかたぶんできない。

すぐにoption:first-childoption:nth-child(1)に色指定すればいいのでは?と思うが、残念ながらこの方法ではプルダウンが展開している時にしか、optionに指定した文字色が反映されない。プルダウンが展開していない状態ではselectが持っている色情報が優先されるためだと思われる。もしかしたら意図したとおりの挙動をするブラウザがあるかもしれないが、主要なブラウザでは無駄な足掻きらしい。

文字だけではちょっと分かりづらいが、整理してみるとセレクト項目は概ね以下の3つの状態に分類される。

  1. セレクト項目がブラウザに表示されているだけで「選択してください」といったラベル(空の要素)が見えているだけの状態
  2. セレクト項目が選択され、内包しているoptionの各項目がプルダウン形式でユーザーに提示されている状態
  3. ユーザーによって答えや項目が選択され、セレクト項目からフォーカスが外れた状態

試しに上記1番目の状態を基本に、selectに対して薄いグレーの文字色を設定し、optionに対してoption:nth-child(n+2) { color: #272727; }(optionの2項目目以降の文字列を黒に)という色の指定をしたとしても、上記2番目のプルダウンが展開されている状態以外では、文字の色は薄いグレーのままという事になる。

というわけで、option最初の項目「選択してください」以外が選択されたら先祖や親要素にクラスを追加し、その追加されたクラスの有無によってselectに指定している色そのものを変更する事に。サンプルページ・ソースの727~745行目までがそれ。

  // select の文字色を input[type="text"]、textarea の挙動に揃えるためのアコレコ
  $('select option:first-child').addClass('select-label');
  $('select').on('change', function(){
    var itemSelect = $(this).find('option:selected').hasClass('select-label');
    if (itemSelect){
      $(this).parents('.select-field').removeClass('select-option');
    } else {
      $(this).parents('.select-field').addClass('select-option');
    }
  });
  // 住所自動入力では上のアレコレが発動しないため、別途郵便番号を7文字以上入力したら発動
  $('input[type="text"].p-postal-code').on('keyup', function(){
    var codeCount = $(this).val().length;
    if (codeCount > 6) {
      $(this).parents('.text-field').next().addClass('select-option');
    } else {
      $(this).parents('.text-field').next().removeClass('select-option');
    }
  });

まずは、728行目において最初のoptionはラベルとしての項目のため、他のoptionと区別する意味で.select-labelというクラスを付与する。イチから手書き(?)でhtmlを書く場合、optionに識別用のクラスを最初から付与しておけば省けるが、今回のサンプルページはContact Form 7が出力したコードという前提なので、そこもjQueryのチカラを借りる。

729行目、selectの変化を切っ掛けにして、730行目で選択されたoptionの項目(option:selected)が.select-labelというクラスを持っているかどうかの戻り値を変数「itemSelect」に格納している。

そして、731行目で戻り値が「true」の場合は、.select-labelを持っている =「選択してください」が選択されているという事になるため、先祖要素のli.select-fieldに、もし.select-optionというクラスが付与されているならばそれを削除してね、という指示を出している。

逆に戻り値が「false」だった場合は、optionが.select-labelというクラスを持っていないという事になり、optionの最初の項目以外が選択されていると判断されるため、734行目でli.select-field.select-optionというクラスを追加してね、という事になる。

737行目からの記述は、コメントにもあるように郵便番号入力でselectが自動的に変わった場合は、729行目からの$('select').on('change', function(){ ... });ではその変化が感知できない。よって「郵便番号に変化があれば、都道府県のselect部分も当然変化するでしょう」という前提で、郵便番号の入力にハイフンの有無などのシバリを設けていないこともあり、まぁ、記入欄に7文字以上入力されたら──というのを発動条件にしている。

さらに色々想定し出すと、郵便番号を一度入力して削除した場合の挙動なども考慮する必要があるかもしれないが、このサンプルページでは必須項目だし、必ず7桁ないし8桁の数字(&ハイフン)は入力しなければならないのだからと、深く考えないようにしたw

そして、このjQueryの挙動に合わせてCSSは以下のように。

    /* form - select */
    select {
      background-color: transparent;
      color: #afafaf;
      padding-right: 1.5em;
      position: relative;
      z-index: 2;
      cursor: pointer;
    }
    select:focus,
    .select-option select {
      color: #272727;
    }
    select::-ms-expand {
        display: none;
    }
    .select-field .wpcf7-form-control-wrap {
      background-color: #fff;
      display: inline-block;
      position: relative;
    }
    .select-field .wpcf7-form-control-wrap::after {
      border-top: 6px solid #666;
      border-right: 4px solid transparent;
      border-left: 4px solid transparent;
      content: '';
      display: block;
      position: absolute;
      top: 43%;
      right: .7em;
      z-index: 1;
    }

selectの背景色は透明(background-color: transparent;)にして、文字色をplaceholderに合わせて薄いグレー(color: #afafaf;)にする。375~377行目の3行(padding-right: 1.5em;position: relative;z-index: 2;)は、下向きの三角形に関連する記述なので詳細は後で。

続けて、セレクト項目が選択された(select:focus)時と「選択してください」以外の項目が選択された時(jQueryによってselectの先祖要素に.select-optionが付与される)において、selectの文字色を黒(color: #272727;)にする指定が、380~383行目までの記述である。

384行目からのselect::-ms-expand { display: none; }は、IE 11が持っているネイティブのプルダウンを示すスタイル(アイコン?)が、appearance: none;では消えてくれないため、ここで改めて指定した。

387~402行目で、セレクト項目に表示される文字列の横にある下向きの三角形を作り位置を指定している。そしてここで、375~377行目の3行が関連してくる。

padding-right: 1.5em;は、三角形が表示される場所の確保のための余白作り。

position: relative;z-index: 2;は、三角形とセレクト項目の重なり順を変更するために必要な措置。これらの記述がないとセレクト項目の手前に三角形が描画されるため、カーソルで▼をクリックしたとしても、それは.select-field .wpcf7-form-control-wrap::after部分を選択している事となり、selectの外側の部分をクリックしている事と一緒で、セレクト項目を選択しているわけではないためプルダウンも展開しない。▼からカーソルがちょっとでもズレればOKなんだけど、人ってそういうアイコンを狙ってクリックしたがるものだから、面積的には極々僅かでもココは放っておけない。

まぁ、selectに疑似要素が設定できればいいんだけど、それは出来ないからね。

そういえば、selectを使う項目が複数あった場合というのも考慮してないやw まぁ泥臭いやり方だとselectの項目ごとにIDを振って、そのIDで識別するとかでいいかも。自動でやるならば、each()による繰り返し処理で連番付きIDやクラスをで付与して識別するとか。この後の記事で紹介する、ラジオボタンとテキスト入力の組み合わせや、ファイル送信では複数設置も考えてJavaScriptを書いたんだけどね、selectの方は忘れていた。