🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
DOM

DOMは、Document Object Modelの略で、HTML文書やXML文書をアプリケーションから利用するためのAPIです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

Q&A

解決済

5回答

1103閲覧

labelのクリックで入力エレメントが起こすclickイベントを区別する方法があるか

maisumakun

総合スコア145957

DOM

DOMは、Document Object Modelの略で、HTML文書やXML文書をアプリケーションから利用するためのAPIです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

React.js

Reactは、アプリケーションのインターフェースを構築するためのオープンソースJavaScriptライブラリです。

3グッド

1クリップ

投稿2023/10/11 08:42

実現したいこと

  • 内部に<label>があろうが、外側の<div>で1クリックあたり1回のイベントを拾えるようにする

前提

html

1<div> 2 文章などで外側のスペースも確保 3 <label> 4 hoge 5 <input type="text" /> 6 </label> 7</div>

このようなHTMLに対して、<div>onclickを拾うようにした上で、<label>内の文言をクリックすると、<label><input>の2回クリックイベントが<div>まで上がってきます。

やりたいこととしては、<div>側のイベントハンドリングの実装だけで、このような<label>のクリックに対しても1回だけの処理を行いたい、という状況です。

やりたくないこと

  • <label><input>の側に処理を仕掛ける(実際には個数が増減したりもあるので煩雑となります)
  • <label>のクリックでpreventDefaultを行う、あるいは<div>のキャプチャフェーズでイベントを処理して、<input>の反応自体を抑える(入力要素がフォーカスしなくなるのは避けたい)

現状の考え

とりあえず、現状では「<label>のクリックから派生した<input>のクリックイベントでは処理を割愛する」という手法を考えていて、いったん「event.timeStampを見比べて、ごく直近で発生したイベントなら除外する」というような条件による判定がありそうではあるのですが、安定して稼働する条件なのか、そしてもっと確実な判定法がないのか気になっているところです。

その他

Reactで実装を行っているので、純粋なDOMだけではなくReactによる解決策も歓迎です。

Lhankor_Mhy, arcxor👍を押しています
think49を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Lhankor_Mhy

2023/10/11 10:24

ご質問のコードですと『Event.target が label なら処理を除外する』でよさそうな気もしますが、これだと、フォーム部品と関連づいていない label 要素などを拾い損ねる、ということでしょうか?
maisumakun

2023/10/11 10:35

> フォーム部品と関連づいていない label 要素などを拾い損ねる、ということでしょうか? そのとおりですね。
arcxor

2023/10/11 10:42

参考までにお聞きしたいのですが、これが実現できると何が嬉しいのでしょうか。どのような場面でこれによって困っているのでしょうか。 『Event.target が「フォーム部品と関連づいている label」なら処理を除外する』という実装でもダメそうでしょうか。
maisumakun

2023/10/12 00:05

> どのような場面でこれによって困っているのでしょうか。 フォーム部品の載ったパネル全体をクリックで選ばせるような場面で、label内をクリックすると2回の切り替えになってしまう、という感じです。
guest

回答5

0

MouseEventoffsetX offsetY プロパティが target プロパティの offsetWidth offsetHeight の範囲外だったらコントロール側で発生した疑似クリックと言えそうです。コントロールの上に <label> が重なっていると上手くいきませんが。

投稿2023/10/11 21:52

int32_t

総合スコア21668

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2023/10/12 00:06

> コントロールの上に <label> が重なっていると上手くいきませんが。 そうですね、上のHTMLのように<label>内に<input>を入れる紐づけ方法もあるので、それですんなりと行く場面ばかりではなさそうです。
int32_t

2023/10/12 00:28

コントロールがラベルより手前に表示されていてコントロールをクリックした場合は疑似クリックは起きないので、この回答の手法で大丈夫かと思います。
Lhankor_Mhy

2023/10/12 00:40

Document.elementFromPoint() の方が楽かもしれませんね。
int32_t

2023/10/12 01:18 編集

たしかに、document.elementFromPoint(e.pageX - window.scrollX, e.pageY - window.scrollY) == e.target のほうが確実そうですね。
guest

0

ベストアンサー

フォーム部品の載ったパネル全体をクリックで選ばせるような場面で、label内をクリックすると2回の切り替えになってしまう、という感じです。

Webアクセシビリティの観点で、クリックを代替するようなキーボード操作は用意されているのでしょうか。キーボード操作でクリック相当のイベントが発生した場合、MouseEventを参照するような回避策は採用できなそうに思いました。

むしろ、通常のクリックを意図せず連続で行ってしまった場合にも1回のクリックと見做すような debounce 処理の方が適しているのかもしれません。これは質問文に書かれている event.timeStamp を参照する実装でも良いと思います。そうでなければ一般的な debounce 実装のように peformance.now() を参照する感じでしょうか。

label 要素を pointer-events: none する方法も考えましたが、これだと入力エレメントがフォーカスされないのでダメなのですよね。

投稿2023/10/13 14:28

arcxor

総合スコア2857

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

Web標準仕様

<label>内の文言をクリックすると、<label><input>の2回クリックイベントが<div>まで上がってきます。

この挙動は「Web標準仕様」で定義された挙動でしょうか。
該当仕様書のURLがわかりましたら、是非教えてください。
仕様に則ってコーディングするのが原則と考えます。

検証結果

手元のGoogle Chromeで検証し、下記仕様とあたりを付けました。

  • label要素でclickイベントが発火した場合、対応するフォーム部品でもclickイベントが発火する

HTML Standardの「4.10 Forms」配下で定義されている要素をフォーム部品と想定し、フォーム部品にlabel要素が対応している状況で、clickイベントが2回発火しないように書いたのが下記コードとなります。

html

1<div id="target"> 2 <fieldset> 3 <legend>label要素配下にフォーム部品がある場合</legend> 4 <p><label>input[type=text]<input type="text" /></label></p> 5 <p><label>button<button>button</button></label></p> 6 <p><label>select<select><option value="dog">Dog</option></select></label></p> 7 <p><label>textarea<textarea>Dog</textarea></label></p> 8 <p><label>output<output >output</output ></label></p> 9 <p><label>progress<progress id="file" max="100" value="70">70%</progress></label></p> 10 <p><label>meter<meter id="fuel" min="0" max="100" low="33" high="66" optimum="80" value="50">at 50/100</meter></label></p> 11 </fieldset> 12 <fieldset> 13 <legend>label要素のfor属性値に対応するフォーム部品がある場合</legend> 14 <p><label for="foo">input[type=text]</label><input id="foo" type="text" /></p> 15 </fieldset> 16 <fieldset> 17 <legend>label要素に対応するフォーム部品がない場合</legend> 18 <p><label>label</label></p> 19 </fieldset> 20</div> 21<script> 22 'use strict'; 23 document.getElementById('target').addEventListener('click', function handleClick (event) { 24 const target = event.target, htmlFor = target.htmlFor ? target.ownerDocument.getElementById(target.htmlFor): null; 25 if (target.tagName === 'LABEL' && 26 (target.querySelector('input,button,select,textarea,output,progress,meter') || 27 htmlFor && ['INPUT','BUTTON','SELECT','TEXTAREA','OUTPUT','PROGRESS','METER'].includes(htmlFor.tagName)) 28 ) return; 29 console.log(event.type, target); 30 }, false); 31</script>

投稿2023/10/12 10:53

think49

総合スコア18189

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2023/10/12 11:14

> この挙動は「Web標準仕様」で定義された挙動でしょうか。 気になって調べていましたが、 https://www.w3.org/TR/uievents/#event-type-click に 「In addition to being associated with pointer devices, the click event type MUST be dispatched as part of an element activation(後略)」とあり、<label>経由で引き起こされるフォーム要素のアクティブ化の際にclickイベントも発生するとのことでした。
guest

0

参考までに

javascript

1<script> 2document.addEventListener('click',e=>{ 3 let div; 4 if(e.target.closest('label')) e.preventDefault(); 5 if(div=e.target.closest('div')){ 6 console.log(div.id); 7 } 8}); 9</script> 10<div id="d1"> 11<label> 12label 13<input type="text" /> 14</label> 15</div> 16<div id="d2"> 17<label for="i2"> 18label 19</label> 20<input type="text" id="i2" /> 21</div> 22<div id="d3"> 23<label for="i3"> 24<span><b>label</b></span> 25</label> 26<input type="text" id="i3" /> 27</div>

投稿2023/10/12 01:28

yambejp

総合スコア116644

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

if (e.target.control) return;とかはどうでしょうか。不安は残りますが……

投稿2023/10/11 10:52

Lhankor_Mhy

総合スコア36928

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2023/10/12 00:12

紐づいた入力要素がdisabledだった場合にはクリックが取れないですね(とはいえ、この場合は入力要素自体を直接クリックしてもイベントが発生しないですが)。 https://jsfiddle.net/yptobj0z/
Lhankor_Mhy

2023/10/12 00:33

うーんなるほど……
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問