HTML Label löst die entsprechende Eingabe nicht aus, wenn die Maus beim Klicken in Firefox bewegt wird

13

Wenn Sie im folgenden Beispiel auf die Beschriftung klicken, ändert sich der Status der Eingabe.

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>

Wenn Sie in Chrome den Cursor zwischen den Ereignissen mousedownund bewegen, wird die mouseupEingabe weiterhin ausgelöst, während in Firefox das Kontrollkästchen den Status nicht ändert.

Gibt es eine Möglichkeit, dies zu beheben? (ohne Verwendung von JavaScript-Ereignis-Listenern)

Firefox-Version: 69.0.3 (64-bit)

Vollständiger Aktionssatz bei Verwendung von Chrom.

  1. Drücken Sie die Taste über dem Etikett
  2. Bewegen Sie den Cursor (auch außerhalb des Etiketts), während Sie die Taste gedrückt halten
  3. Setzen Sie den Cursor wieder auf die Beschriftung
  4. Lass den Knopf los
Nick Zoum
quelle
Wenn Sie in Chrome den Cursor zwischen Mousedown- und Mouseup-Ereignissen bewegen, wird die Eingabe immer noch ausgelöst -> bei Chrome ist dies nicht der Fall und sollte es auch nicht sein. ein Klick = Mousedown + Mouseup .. für einige Ideen verwandt: stackoverflow.com/a/51451218/8620333
Temani Afif
@TemaniAfif Ist es jetzt bei Ihnen auf Chrom der Fall? (da ich klargestellt habe, was ich tue). Oder ändert sich der Status des Kontrollkästchens immer noch nicht?
Nick Zoum
1
Wenn wir die Maus über das Etikett halten, ist das in Ordnung. Der Umzug sollte dies nicht beeinflussen, also denke ich, dass wir vor einem Firefox-Fehler stehen
Temani Afif
2
Dies ist ein Fehler, der im Firfox-Fehlerportal als UNCONFIRMED-Status protokolliert wird. "Ein" Klick "-Ereignis sollte nur auftreten, wenn sich Mousedown und Mouseup an derselben Stelle befanden.
Jadli
1
@TemaniAfif Ich stimme zu, wenn Sie den Cursor bewegen, 1pxwird die Interaktion unterbrochen .
Nick Zoum

Antworten:

3

Einführung

Obwohl ich in der Frage ausdrücklich angegeben habe, dass die Antwort kein JavaScript beinhalten sollte, funktionierten alle Antworten mit JavaScript.
Da dies ein Firefox-Fehler zu sein scheint und die meisten an dieser Stelle eingereichten Antworten erfordern würden, dass ich auch den Rest meines Codes ändere, habe ich beschlossen, ein Skript zu erstellen, das einmal ausgeführt werden kann und alle Labels unabhängig vom Zeitpunkt behandelt Sie werden dem Dom hinzugefügt und haben den geringsten Einfluss auf meine anderen Skripte.

Lösung - Beispiel

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>

Erläuterung

onLabelClick

Die Funktion onLabelClickmuss aufgerufen werden, wenn auf eine Beschriftung geklickt wird. Sie prüft, ob die Beschriftung ein entsprechendes Eingabeelement enthält. Ist dies der Fall, wird es auslösen, das entfernen forAttribut des Etiketts , so dass der Browser nicht nicht den Fehler hat , erneut ausgelöst es und dann eine Verwendung setTimeoutvon 0msdem hinzufügen forAttribute zurück , sobald das Ereignis bis gesprudelt hat. Dies bedeutet, event.preventDefaultdass Sie nicht aufgerufen werden müssen und somit keine anderen Aktionen / Ereignisse abgebrochen werden. Auch wenn ich diese Funktion überschreiben muss, muss ich nur einen Ereignis-Listener hinzufügen, Event#preventDefaultder das forAttribut aufruft oder entfernt .

manageLabel

Die FunktionmanageLabelAkzeptiert eine Beschriftung, prüft, ob bereits ein Ereignis-Listener hinzugefügt wurde, um ein erneutes Hinzufügen zu vermeiden, fügt den Listener hinzu, falls er noch nicht hinzugefügt wurde, und fügt ihn der Liste der verwalteten Beschriftungen hinzu.

onLoad

Die Funktion onLoadmuss beim Laden der Seite aufgerufen werden, damit die Funktion manageLabelzu diesem Zeitpunkt für alle Beschriftungen im DOM aufgerufen werden kann. Die Funktion verwendet auch einen MutationObserver , um alle Labels abzufangen, die hinzugefügt werden, nachdem das Laden ausgelöst wurde (und das Skript ausgeführt wurde).

Der oben angezeigte Code wurde von Martin Barker optimiert .

Nick Zoum
quelle
Sie Code ist ein wenig nicht optimierten einige Prüfungen tun es nicht braucht und einige teure CPU - Zyklen, ich habe es für Sie optimiert pastebin.com/FDXwQL1d
Barkermn01
Es entfernt nicht die Prüfung, die es durch HTMLLabelElementPrüfung ersetzt , anstatt sowohl ein HTMLElement als auch eine Prüfung, dass der tagName ein Label war
Barkermn01
klug, aber massiv überkompliziert
Steve Tomlin
2

Ich weiß, dass Sie keine JS-Ereignis-Listener wollten, aber ich denke, Sie möchten die Bewegung identifizieren, die dies nicht tut, sondern Mousedown anstelle von Klick verwendet (Mousedown gefolgt von Mouseup).

Obwohl dies ein bekannter Fehler in Firefox ist, können Sie ihn mithilfe des Mousedown-Ereignisses umgehen

Ich musste Ihre ID ändern, um eine gültige ID zu haben. Die ID muss mit einem Zeichen beginnen

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>

Barkermn01
quelle
Das Problem ist, dass ich dieses Skript (mit querySelectorAll) beim Laden der Seite ausführen muss und dann jedes Mal, wenn dem Dom ein Label hinzugefügt wird. Dies kann auch alle Listener von Mausereignissen durcheinander bringen, die Labels oder dem Dom im Allgemeinen hinzugefügt wurden, Event#preventDefaultum ein Doppelklicken auf Browser zu vermeiden, die diesen Fehler nicht aufweisen. Schließlich clickwäre die Verwendung des Ereignisses besser, da es dieselbe Interaktion wie die beabsichtigte Aktion hätte. Davon abgesehen ist dies die bisher beste Antwort.
Nick Zoum
@nickzoum Ich habe den Code aktualisiert, um Ihr preventDefault()Problem zu beheben. Daher wird überprüft, ob der Browser ihn geändert hat, wenn er nicht geändert wurde. Nach 150 ms ist er schnell genug, damit der Benutzer ihn nicht bemerkt, und langsam genug, dass der Browser reagiert hat Intime, wenn es tun wird, was es soll. ist das besser?
Barkermn01
0

Nein. Dies sieht aus wie ein Firefox-Fehler und kein Problem mit Ihrem Code. Ich glaube nicht, dass es eine CSS-Problemumgehung für dieses Verhalten gibt.

Möglicherweise können Sie es Mozilla melden und das Problem beheben, aber darauf würde ich mich nicht verlassen. https://bugzilla.mozilla.org/home

Für eine mögliche Problemumgehung würde ich vorschlagen, das Ereignis stattdessen beim Mouseup auszulösen.

Garet Webster
quelle
0

Wenn Sie ohne Javascript auf die Beschriftung klicken, deren "for" -Wert mit dem "id" -Wert der Eingabe identisch ist, wird auf die Eingabe geklickt, dies ist jedoch unter Browsern nicht konsistent.

Wenn ein Browser den obigen Anweisungen folgt, hebt Ihr Javascript-Klickereignis den Effekt auf, was dazu führt, dass nichts unternommen wird.

Eine Lösung

Um die Konsistenz zwischen den Browsern zu gewährleisten, können Sie eine andere Strategie anwenden: Beim Laden werden alle Attribute 'für' für 'Daten für' dynamisch geändert, sodass der ursprüngliche Browsereffekt auf Null gesetzt wird. Anschließend können Sie Ihr Klickereignis auf jedes Label anwenden.

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();
  }
});
Steve Tomlin
quelle
document.attachEvent("onreadystatechange",Ich bin nur gespannt, wie kommt es, dass du damit umgegangen bist und nicht document.addEventListener("DOMContentLoaded",?
Barkermn01
Es war nur eine Methode, die ich gesehen habe und die etwas mehr X-Browser ist. Wählen Sie, was Sie bevorzugen.
Steve Tomlin
-2

Wenn Sie das Ereignis an das Dokument anhängen und auf das Element abzielen, das Sie dort benötigen, sollte dieses Problem behoben werden.

$ (Document) .on ('click', '.item', function (event) {});

Nach dem Lesen dieses Themas in der Vergangenheit liegt es an Firefox, Ihre Aktion als Versuch zu verstehen, das Element zu ziehen. Da die Benutzerauswahl jedoch keine ist, wird lediglich das Standardverhalten verhindert.

Dies basiert auf ziemlich begrenztem Wissen, aber es scheint ein bekannter Fehler zu sein, und es gibt einige Artikel, die dies unterstützen.

Benjamin James Kippax
quelle
Ich habe in der Frage ausdrücklich gesagt without using JavaScript event listeners.
Nick Zoum
@nickzoum, da dies ein bekannter Fehler in Firefox ist, denke ich, dass Sie kein Glück haben.
Benjamin James Kippax