L'etichetta HTML non attiva il rispettivo input se il mouse viene spostato mentre si fa clic su Firefox


13

Nel seguente esempio, quando si fa clic sull'etichetta, l'input cambia stato.

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>

In Chrome, quando si sposta il cursore tra gli eventi mousedowne mouseup, l'input viene comunque attivato, mentre in Firefox la casella di controllo non cambia stato.

C'è un modo per risolvere questo problema? (senza utilizzare i listener di eventi JavaScript)

Versione di Firefox: 69.0.3 (64-bit)

Set completo di azioni quando si utilizza Chrome.

  1. Premi il pulsante sopra l'etichetta
  2. Spostare il cursore (anche all'esterno dell'etichetta) mentre si tiene premuto il pulsante
  3. Riporta il cursore sull'etichetta
  4. Rilascia il pulsante

In Chrome, quando sposti il ​​cursore tra gli eventi di mouse e mouseup, l'input viene comunque attivato -> non è il caso per me su Chrome e ot non dovrebbe esserlo. a click = mousedown + mouseup .. correlati per alcune idee: stackoverflow.com/a/51451218/8620333
Temani dopo il

@TemaniAfif È il tuo caso su Chrome ora? (dal momento che ho chiarito cosa sto facendo). O non cambia ancora lo stato della casella?
nick zoum,

1
Se teniamo il mouse sopra l'etichetta, allora va bene. la mossa non dovrebbe influire su questo, quindi immagino che stiamo affrontando un bug di Firefox
Temani dopo il

2
questo è un bug che è registrato come stato UNCONFIRMED nel portale dei bug di firfox, un evento "click" dovrebbe verificarsi solo se il mouse e il mouseup si trovassero nella stessa posizione "puoi controllare lì url bug: bugzilla.mozilla.org/show_bug. cgi? id = 319347
Jadli,

1
@TemaniAf Se sono d'accordo, anche spostando il cursore 1pxsi interromperà l'interazione.
nick zoum,

Risposte:


3

introduzione

Anche se ho espressamente dichiarato nella domanda che la risposta non dovrebbe comportare JavaScript, tutte le risposte hanno funzionato con JavaScript.
Poiché questo sembra essere un bug di Firefox e la maggior parte delle risposte inviate a questo punto mi richiederebbe di modificare anche il resto del mio codice, ho deciso di creare uno script che può essere eseguito una volta, tratterà tutte le etichette indipendentemente da quando vengono aggiunti al dom e avranno il minimo impatto sugli altri miei script.

Soluzione - Esempio

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>

Spiegazione

onLabelClick

La funzione onLabelClickdeve essere chiamata ogni volta che si fa clic su un'etichetta, controllerà se l'etichetta ha un elemento di input corrispondente. In tal caso, lo attiverà, rimuoverà l' forattributo dell'etichetta in modo che i browser non abbiano il bug non lo riattivi e quindi utilizzare un setTimeoutof 0msper aggiungere fornuovamente l' attributo una volta che l'evento è stato gorgogliato. Questo significa event.preventDefaultche non è necessario essere chiamati e quindi non verranno cancellate altre azioni / eventi. Inoltre, se devo sostituire questa funzione, devo solo aggiungere un listener di eventi che chiama Event#preventDefaulto rimuove l' forattributo.

manageLabel

La funzionemanageLabelaccetta un'etichetta controlla se è già stato aggiunto un listener di eventi per evitare di aggiungerlo nuovamente, aggiunge il listener se non è già stato aggiunto e lo aggiunge all'elenco di etichette che sono state gestite.

onLoad

La funzione onLoaddeve essere chiamata quando la pagina viene caricata in modo che la funzione manageLabelpossa essere chiamata per tutte le etichette sul DOM in quel momento. La funzione utilizza anche un MutationObserver per rilevare tutte le etichette che vengono aggiunte, dopo che il caricamento è stato attivato (e lo script è stato eseguito).

Il codice visualizzato sopra è stato ottimizzato da Martin Barker .


Il codice è un po 'non ottimizzato facendo alcuni controlli che non è necessario e alcuni costosi cicli della CPU, l'ho ottimizzato per te su pastebin.com/FDXwQL1d
Barkermn01

Non rimuove il segno di spunta lo sostituisce con un HTMLLabelElementsegno di spunta anziché con un HTMLElement e un controllo che tagName era un'etichetta
Barkermn01

intelligente ma enormemente complicato
Steve Tomlin,

2

So che non volevi ascoltatori di JS Event, ma sto pensando che intendi identificare il movimento, ma non sta usando mousedown invece di click (mousedown seguito da mouseup).

Mentre questo è un bug noto in Firefox, puoi aggirarlo utilizzando l' evento mousedown

Ho dovuto cambiare il tuo id per essere valido, un id deve iniziare con un personaggio

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>


Il problema è che dovrò eseguire questo script (usando querySelectorAll) al caricamento della pagina e quindi, ogni volta che un'etichetta viene aggiunta al dom. Questo può anche rovinare qualsiasi listener di eventi del mouse aggiunto alle etichette o alla dom in generale mentre usi Event#preventDefaultper evitare un doppio clic sui browser che non hanno questo bug. Infine, utilizzare l' clickevento sarebbe meglio, poiché avrebbe la stessa interazione dell'azione prevista. A parte questo, questa è la risposta migliore finora.
nick zoum,

@nickzoum ho aggiornato il codice per risolvere il tuo preventDefault()problema, quindi funziona controllando se il browser lo ha modificato se non lo cambia, dopo 150 ms abbastanza velocemente da non essere notato dall'utente e abbastanza lento da agire sul browser intime se sta per fare quello che dovrebbe. è meglio?
Barkermn01,

0

No. Sembra un bug di Firefox e non un problema con il tuo codice. Non credo che ci sia una soluzione CSS per questo comportamento.

Potresti essere in grado di segnalarlo a Mozilla e risolvere il problema, ma non mi affiderei a questo. https://bugzilla.mozilla.org/home

Per una potenziale soluzione suggerirei invece di attivare l'evento al passaggio del mouse.


0

Senza javascript, quando si fa clic sull'etichetta con il valore "for" uguale al valore "id" degli input, viene fatto clic sull'input, ma questo non è coerente tra i browser.

Se un browser segue quanto sopra, il tuo evento click javascript annullerà l'effetto, che finisce per non fare nulla.

Una soluzione

Per coerenza tra i browser, è possibile adottare una strategia diversa: Onload modifica in modo dinamico tutti gli attributi "for" per "data-for", quindi annulla l'effetto del browser originale. Quindi puoi applicare il tuo evento click a ciascuna etichetta.

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",Sono solo integrato come mai sei andato con quello e no document.addEventListener("DOMContentLoaded",?
Barkermn01,

Era solo un metodo che ho visto che è un po 'più x-browser. Scegli quale preferisci.
Steve Tomlin,

-2

Allegare l'evento al documento e scegliere come target l'elemento richiesto deve risolvere questo problema.

$ (Documento) .on ('click', '.item', funzione (evento) {});

Dalla lettura di questo argomento in passato, spetta a Firefox comprendere la tua azione come un tentativo di trascinare l'elemento, poiché la selezione dell'utente è nessuna, impedisce solo il comportamento predefinito.

Questo si basa su una conoscenza abbastanza limitata, ma sembra essere un bug / stranezza noto e ci sono alcuni articoli in sospeso a supporto di questo.


Ho detto esplicitamente nella domanda without using JavaScript event listeners.
nick zoum,

@nickzoum poiché questo è un bug noto in Firefox, penso che tu sia sfortunato.
Benjamin James Kippax,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.