Firefoxでクリックしているときにマウスが移動した場合、HTMLラベルはそれぞれの入力をトリガーしません


13

次の例では、ラベルをクリックすると、入力の状態が変化します。

document.querySelector("label").addEventListener("click", function() {
  console.log("clicked label");
});
label {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
<input type="checkbox" id="1">
<label for="1">Label</label>

Chromeでは、カーソルmousedownmouseupイベントとイベントの間に移動しても入力はトリガーされますが、Firefoxではチェックボックスの状態は変わりません。

これを修正する方法はありますか?(JavaScriptイベントリスナーを使用しない場合)

Firefoxバージョン: 69.0.3 (64-bit)

Chromeを使用する場合のアクションの完全なセット。

  1. ラベルの上にあるボタンを押します
  2. ボタンを押したままカーソルを(ラベルの外側でも)移動します
  3. カーソルをラベルに戻します
  4. ボタンを離します

Chromeでは、マウスダウンイベントとマウスアップイベントの間にカーソルを移動しても、入力は引き続きトリガーされます ->私の場合、Chromeではそうではありません。a click = mousedown + mouseup ..いくつかのアイデアに関連しています:stackoverflow.com/a/51451218/8620333
Temani Afif

@TemaniAfifそれは今あなたのクロムのケースですか?(私が何をしているのかを明確にしたので)。それともチェックボックスの状態は変わりませんか?
ニックzoum

1
ラベルの上にマウスを置いておくと、問題ありません。移動はこれに影響しないはずなので、Firefoxのバグに直面していると思います
Temani Afif

2
これは、firfoxバグポータルで「未確認」ステータスとしてログに記録されるバグです。「「クリック」イベントは、mousedownとmouseupが同じ場所にある場合にのみ発生します」というバグのURLを確認できます:bugzilla.mozilla.org/show_bug。 cgi?id = 319347
Jadli

1
@TemaniAfif同意します1px。カーソルを移動すると、インタラクションが中断されます。
ニックzoum

回答:


3

前書き

質問では答えにJavaScriptを含めるべきではないと具体的に述べましたが、すべての答えはJavaScriptで機能しました。
これはFirefoxのバグのようで、この時点で送信された回答のほとんどでコードの残りの部分も変更する必要があるため、1回実行できるスクリプトを作成し、いつでもすべてのラベルを処理することにしましたそれらはdomに追加され、他のスクリプトへの影響は最小限です。

ソリューション-例

var mutationConfiguration = {
  attributes: true,
  childList: true
};

if (document.readyState === "complete") onLoad();
else addEventListener("load", onLoad);

var managingDoms = [];

function onLoad() {
  document.querySelectorAll("label[for]").forEach(manageLabel);
  if (typeof MutationObserver === "function") {
    var observer = new MutationObserver(function(list) {
      list.forEach(function(item) {
        ({
          "attributes": function() {
            if (!(item.target instanceof HTMLLabelElement)) return;
            if (item.attributeName === "for") manageLabel(item.target);
          },
          "childList": function() {
            item.addedNodes.forEach(function(newNode) {
              if (!(newNode instanceof HTMLLabelElement)) return;
              if (newNode.hasAttribute("for")) manageLabel(newNode);
            });
          }
        }[item.type])();
      });
    });
    observer.observe(document.body, mutationConfiguration);
  }
}

function manageLabel(label) {
  if (managingDoms.includes(label)) return;
  label.addEventListener("click", onLabelClick);
  managingDoms.push(label);
}

function onLabelClick(event) {
  if (event.defaultPrevented) return;
  var id = this.getAttribute("for");
  var target = document.getElementById(id);
  if (target !== null) {
    this.removeAttribute("for");
    var self = this;
    target.click();
    target.focus();
    setTimeout(function() {
      self.setAttribute("for", id);
    }, 0);
  }
}
label {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
  padding: 10px;
  border: 1px solid black;
  cursor: pointer;
}
<input type="checkbox" id="a">
<input type="text" id="b">
<label for="a">A</label>
<script>
  setTimeout(function() {
    var label = document.createElement("label");
    label.setAttribute("for", "b");
    label.textContent = "b";
    document.body.appendChild(label);
  }, 3E3);
</script>

説明

onLabelClick

関数onLabelClickは、ラベルがクリックされるたびに呼び出される必要があり、ラベルに対応する入力要素があるかどうかをチェックします。もしそうなら、それをトリガーし、forブラウザーのバグがそれを再トリガーしないようにラベルの属性を削除し、イベントがバブルアップしたらa setTimeoutを使用し0msfor属性を再び追加します。つまりevent.preventDefault、呼び出される必要がないため、他のアクションやイベントがキャンセルされることはありません。また、この関数をオーバーライドする必要があるEvent#preventDefault場合は、for属性を呼び出すか削除するイベントリスナーを追加する必要があります。

manageLabel

関数manageLabelイベントリスナーが既に追加されていないかどうかのラベルチェックを受け入れ、再度追加されないようにします。追加されていない場合はリスナーを追加し、管理されているラベルのリストに追加します。

onLoad

この関数onLoadは、ページが読み込まれたときに呼び出される必要があるためmanageLabel、その時点でDOM上のすべてのラベルに対して関数を呼び出すことができます。また、この関数は、MutationObserverを使用して、ロードが実行された(およびスクリプトが実行された)後に追加されるラベルをキャッチします。

上記のコードは、Martin Barkerによって最適化されています。


あなたのコードは、私があなたのためにそれを最適化してきた、それはする必要はありませんいくつかのチェックと、いくつかの高価なCPUサイクルをやって少し最適化されていないですpastebin.com/FDXwQL1d
Barkermn01

HTMLLabelElementHTMLElementとtagNameがラベルであるというチェックの代わりに、checkに置き換えるチェックは削除されません
Barkermn01

賢いが非常に複雑すぎる
Steve Tomlin

2

私はあなたがJSイベントリスナーを望んでいなかったのを知っていますが、これはそうではなく、クリックの代わりにマウスダウンを使用している動きを特定することを意味していると思います(マウスダウンの後にマウスアップ)。

これはFirefoxの既知のバグですが、mousedownイベントを使用して回避できます。

私はあなたのIDを有効なものに変更しなければなりませんでしたIDは文字で始まる必要があります

document.querySelector("label").addEventListener("mousedown", function(evt) {
  console.log("clicked label");
  // if you want to to check the checkbox when it happens,
  let elmId = evt.target.getAttribute("for")
  let oldState = document.querySelector("#"+elmId).checked;
  setTimeout(() => {
    if(oldState == document.querySelector("#"+elmId).checked){
      document.querySelector("#"+elmId).checked = !oldState;
    }
  }, 150)
});
label {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;
}
<input type="checkbox" id="valid_1">
<label for="valid_1">Label</label>


問題は、querySelectorAllページの読み込み時にこのスクリプトを(を使用して)実行し、その後、ラベルがdomに追加されるたびに実行する必要があることです。このEvent#preventDefaultバグのないブラウザをダブルクリックすることを避けるために、ラベルやdomに追加されたマウスイベントリスナーを台無しにすることもできます。最後に、click意図したアクションと同じ相互作用があるため、イベントを使用する方が良いでしょう。それ以外は、これが今のところ最良の答えです。
ニックzoum

@nickzoum私はあなたのpreventDefault()問題を修正するためにコードを更新しましたので、ブラウザがそれを変更していないかどうかを確認することで動作します、150 ms後にユーザーが気付かないほど速く、ブラウザが動作するのに十分なほど遅いそれが想定されていることを実行する場合は、インタイム。それは良いですか?
Barkermn01

0

いいえ。Firefoxのバグのように見え、コードの問題ではありません。この動作にCSSの回避策があるとは思いません。

あなたはそれをMozillaに報告して問題を修正することができるかもしれませんが、私はそれには依存しません。https://bugzilla.mozilla.org/home

潜在的な回避策として、代わりにマウスアップでイベントをトリガーすることをお勧めします。


0

JavaScriptがない場合、「for」値が入力「id」値と同じラベルをクリックすると、入力がクリックされますが、これはブラウザー間で一貫していません。

ブラウザが上記に従っている場合、JavaScriptクリックイベントは効果をキャンセルし、結果として何もしません。

解決策

ブラウザー間で一貫性を持たせるには、別の戦略を採用することができます。Onloadは、「data-for」のすべての属性「for」を動的に変更するため、元のブラウザーの影響を無効にします。次に、クリックイベントを各ラベルに適用できます。

var replaceLabelFor = function () {
    var $labels = document.querySelectorAll('label');
    var arrLabels = Array.prototype.slice.call($labels);
    arrLabels.forEach(function (item) {
      var att = document.createAttribute('data-for');
      att.value = String(this.for);
      item.setAttributeNode(att);
      item.removeAttribute('for')
    });
}

var applyMyLabelClick() {
  document.querySelector("label").addEventListener("click", function() {
    console.log("clicked label");
  });
}

// x-browser handle onload
document.attachEvent("onreadystatechange", function(){
  if(document.readyState === "complete"){
    document.detachEvent("onreadystatechange", arguments.callee);
    replaceLabelFor();
    applyMyLabelClick();
  }
});

document.attachEvent("onreadystatechange",私はあなたがどうやってそれを使ったのdocument.addEventListener("DOMContentLoaded",ですか?
Barkermn01

それは、私が見た方法であり、もう少しXブラウザーです。好きな方を選んでください。
スティーブトムリン

-2

イベントをドキュメントに添付し、そこで必要な要素をターゲットにすることで、この問題を分類する必要があります。

$(document).on( 'click'、 '.item'、function(event){});

過去にこのトピックを読んでから、Firefoxがアクションを要素をドラッグする試みとして理解することにかかっていますが、ユーザーの選択は何もないため、デフォルトの動作を妨げるだけです。

これはかなり限られた知識に基づいていますが、それは既知のバグ/癖であると思われ、これをサポートするいくつかの記事が浮かんでいます。


私は質問ではっきり言ったwithout using JavaScript event listeners
ニックzoum

@nickzoumこれはFirefoxの既知のバグなので、あなたは運が悪いと思います。
Benjamin James Kippax
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.