クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【pointer-events】フォームの送信ボタンに擬似要素を重ねてもイベントを発火させる

フォームの送信ボタンのinputタグにアイコン(擬似要素)を加えたかったのですが、できませんでした。それで、ボタンの外をdivで囲んで擬似要素を加えました。そしたら、なんということでしょう。ボタンの上に重なったアイコン部分は送信などのイベントが発火しないではありませんか!解決方法はpointer-eventsというCSSでした!!

【目次】

デフォルトの送信ボタンはhover設定がない

フォームの送信ボタンはinputタグのtype属性をsubmitにすると作ることができます。

HTMLのコード

<h1>クリエイター川柳</h1>
<form method="" action="" class="senryu">
  <input type="text" name="一句" class="ikku">
  <input type="submit" value="詠む" class="yomu">
</form>
  • formタグ:method属性もacion属性も空(データ送信無効)
  • 上のinputタグ:type属性がtext(1行テキスト)
  • 下のinputタグ:type属性がsubmit(送信ボタン)

今回はダミーのフォームなのでformタグのmethod属性もacion属性を空にしています。formタグの詳細は前回記事参照。

※参考:フォームPHPはじめの一歩($_GETと$_POSTの違い) - クモのようにコツコツと

送信ボタンをよりボタンとわかりやすくしたいのでCSSを当ててます。

.yomu {
  margin: 0 auto;
  width: 50%;
  height: 2em;
  margin: 0 auto;
  display: block;
  color: #fff;
  border: none;
  background: #666;
  border-radius: 5px;
  font-size: 14px;
}

詳細は割愛しますが、ボタン幅は50%、文字色は#fff、背景色は#666、角丸は5px、などのスタイルです。

CSSの基本についてはこちら

※参考: 【CSSの基本】書ける前に読む!HTML、CSS、JSの書式-3 - クモのようにコツコツと

できたフォームはこちら。1行川柳を送信する簡単なフォームです。

このままでも一応、フォームの体は成しているのですが、ちょっと問題が。PCブラウザでボタンにカーソルを重ねても矢印になりません。

ボタンはaタグではなくinputタグのため、初期値ではhover設定がないんですな。このままだとボタンということがわかりづらい…*1

CSSでhover設定を加える

カーソルを重ねたときに送信ボタンとわかるよう、CSSを加えてみましたよ。

.yomu:hover {
  cursor:pointer;
  opacity: 0.7;
}
  • .yomuセレクタの擬似クラス:hover(カーソルを重ねたときのスタイル)
  • cursorプロパティをpointerで、カーソルが矢印に
  • opacityプロパティを0.7で不透明度を70%に

カーソルをボタンに重ねてみてください。カーソルが矢印になりましたね!あと、半透明で薄い色にした。「押せそう」感が出てきた!

JSで未入力送信時のクリックイベントを加える

もうひとつよくあるアクション。アラートによるバリデーションをJSのクリックイベントで仕込む。

JSのイベントについてはこちら

※参考:【JSの基本-後編】書ける前に読む!HTML、CSS、JSの書式-5 - クモのようにコツコツと

const ikku = document.querySelector(".ikku");
const yomu = document.querySelector(".yomu");

function yonde() {
   if(ikku.value === "") {      
    alert('いやん。ちゃんと詠んでね。。');
    return false;
  }
} 

yomu.addEventListener('click', yonde, false);
  • 変数ikku.ikkuセレクタの紐付け
  • 変数yomu.yomuセレクタの紐付け
  • 関数yonde()設定
  • if文、もしikkuの値が空だったら
  • いやん。ちゃんと詠んでね。。というアラートを表示せよ
  • 戻り値falseで送信を無効にせよ。
  • yomuのクリックイベントで関数yonde()を実行

送信無効のためにHTMLの送信ボタンにonsubmit属性を追加。値はreturn 関数名();

 <input type="submit" value="詠む" class="yomu" onsubmit="return yonde();">

2019/02/17修正
このままだとアラートを閉じたあとに送信が行われるようだったため、送信無効の処理を追加しました。

※参考:JSでsubmitイベント時のreturn falseの扱い方 - ぽんぽこ開発日記

ボタンにイベントを仕込むとこうなった。

テキストを何も入れない状態で送信ボタンを押してみてください。「いやん。ちゃんと詠んでね。。」というアラートが表示されますね。

inputタグには擬似要素が加えられない

さて、ここで送信ボタンに擬似要素::afterで「>」アイコンをつけたくなりました。

.yomu {
  /*中略*/
  potition: relative;
}

.bg::after {
  content: "";
  display: block;
  width: 10px;
  height: 10px;
  border-top: 2px solid #ccc;
  border-right: 2px solid #ccc;
  position: absolute;
  top: 10px;
  right: 10px;
  transform:rotate(45deg);
}

これは以前の記事の、CSSだけで作る「>」アイコンの方法です。

※参考:リンクでよくある「>」アイコンを画像じゃなくてCSSだけで作る - クモのようにコツコツと

どうだ?

むむっ!?*2 アイコン出ないじゃん!どうやら、inputタグは擬似要素が非対応のようです。。

送信ボタンをdivで囲って擬似要素を当てる

仕方がないので送信ボタンをdivタグで囲ってdivタグに擬似要素を当てる方法をとりました。

※参考:input要素に擬似要素を適用させたい | コトダマウェブ

HTMLのコード

<div class="bg">
  <input type="submit" value="詠む" class="yomu">
</div>
  • 送信ボタン.yomudivタグ.bgで囲う

CSSのコード

.bg{
  position: relative;
  margin: 0 auto;
  width: 50%;
  height: 2em;
}

.bg::after {
  content: "";
  display: block;
  width: 10px;
  height: 10px;
  border-top: 2px solid #ccc;
  border-right: 2px solid #ccc;
  position: absolute;
  top: 10px;
  right: 10px;
  transform:rotate(45deg)
}
  • .bgpositionrelativeにしてサイズなどを設定
  • 擬似要素::after.bgに設定する

どうだ!

おお、アイコン出ました!やった!

ただし…アイコンにカーソルを重ねたりクリックしてみてください。細かい部分ですが下記のような問題があります。

  • 「>」アイコンにカーソルを乗せるとhoverアクションのスタイルが当たらなくなる
  • 「>」アイコンの上でクリックするとアラートのクリックイベントが発火しない(値が空欄のとき)
  • 「>」アイコンの上でクリックするとデータ送信自体も無効になる(値が入っているとき)

アイコンは送信ボタンではなくdivタグの擬似要素のため、ボタンを覆い隠すように重なっている状態です。そのためアイコン上はボタンのイベントが無効になってしまうのです。しかもデータ送信も行われないとは!これは事故の原因になりそうです。うーむ、困った。。

pointer-eventsで擬似要素のイベントを無効にする

調べたところこの問題を解決してくれる方法がわかりました。CSSのpointer-eventsというプロパティです!

※参考:http://nbnote.jp/blog/2012/04/mouse-enabled/

あまり見かけないプロパティですが、値をnoneにするとセレクタのイベントを無効にすることができるようです。これを使うとなぜ解決できるのか?

いまはこんな状態です。

  • 送信ボタンyomuの上に擬似要素.bg::afterが乗っかってる。
  • yomuにはイベントが設定されている。
  • .bg::afterにはイベントが設定されていない。
  • yomuの上ではyomuのイベントが発火する。
  • .bg::afterの上ではなにもイベントが発火しない。

ここで.bg::afterpointer-events: noneを設定すると下記の状態になります。

  • .bg::afterpointer-events: noneを設定
  • .bg::afterのイベントは無効になる
  • .bg::afterの上でも.bg::afterのイベントは無視されてその下のyomuのイベントが発火する

やってみましょう!

.bg::after {
 /*中略*/
  pointer-events: none;
}

どうだ?

おお、やりました!

  • 「>」アイコン上でもホバーアクションが実行される
  • 「>」アイコン上でクリックしてもアラートも表示される(値が空欄のとき)
  • 「>」アイコン上でクリックしてもデータ送信が行われる(値が入っているとき)

「>」アイコンというとても小さい面積の細かい設定ではありますが、ボタンとして自然な動きになり、データが未送信などの事故も防げます。より安全なボタンになりました!

最後に

以上、inputタグの機能制限(ホーバー設定がない、擬似要素が無効など)によって起こった事例と、その対策でした。

ちなみにpointer-eventsは他にもいろいろなことができそうです。

※参考:https://coliss.com/articles/build-websites/operation/css/css-pointer-events.html

例えばある要素をクリックすると、別の要素でイベントを発火させたりということもCSSだけで実現できます。これまでJSでしかできなかったことがどんどんCSSでできるようになっていっていますねぇ。それではまた!


※参考:ネイティブHTML & CSSやってみたシリーズ
qiita.com

*1:タッチデバイスの場合はそもそもhoverアクション自体がないため、見た目だけでボタンとわかる工夫が必要

*2:慈英さん