La etiqueta HTML no activa la entrada respectiva si el mouse se mueve al hacer clic en Firefox

13

En el siguiente ejemplo, cuando hace clic en la etiqueta, la entrada cambia de estado.

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>

En Chrome, cuando mueve el cursor entre los eventos mousedowny mouseup, la entrada aún se activa, mientras que en Firefox la casilla de verificación no cambia de estado.

¿Hay alguna manera de arreglar esto? (sin usar oyentes de eventos de JavaScript)

Versión de Firefox: 69.0.3 (64-bit)

Conjunto completo de acciones cuando se usa Chrome.

  1. Presione el botón sobre la etiqueta
  2. Mueva el cursor (incluso fuera de la etiqueta) mientras mantiene presionado el botón
  3. Regrese el cursor a la etiqueta
  4. Suelta el botón
Nick Zoum
fuente
En Chrome, cuando mueve el cursor entre los eventos de mousedown y mouseup, la entrada aún se activa -> no es el caso para mí en Chrome y no debería serlo. un clic = mousedown + mouseup ... relacionado con algunas ideas: stackoverflow.com/a/51451218/8620333
Temani Afif
@TemaniAfif ¿Es el caso para ti en Chrome ahora? (ya que he aclarado qué es lo que estoy haciendo). ¿O todavía no cambia el estado de la casilla de verificación?
Nick Zoum
1
Si mantenemos el mouse sobre la etiqueta, entonces está bien. el movimiento no debería afectar esto, así que supongo que estamos enfrentando un error de Firefox
Temani Afif
2
este es un error que se registra como estado NO CONFIRMADO en el portal de errores de firfox, el evento "A" clic "solo debería ocurrir si el mousedown y mouseup se encontraban en la misma ubicación" puede verificar allí url de error: bugzilla.mozilla.org/show_bug. cgi? id = 319347
Jadli
1
@TemaniAfif Estoy de acuerdo, mover el cursor incluso 1pxinterrumpirá la interacción.
Nick Zoum

Respuestas:

3

Introducción

Aunque expresé específicamente en la pregunta que la respuesta no debería involucrar JavaScript, todas las respuestas funcionaron con JavaScript.
Dado que esto parece ser un error de Firefox y la mayoría de las respuestas enviadas en este punto requerirían que yo también altere el resto de mi código, decidí crear un script que se pueda ejecutar una vez, se ocupará de todas las etiquetas independientemente de cuándo se agregan al dom y tendrán el menor impacto en mis otros scripts.

Solución - Ejemplo

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>

Explicación

onLabelClick

La función onLabelClickdebe llamarse cada vez que se hace clic en una etiqueta, verificará si la etiqueta tiene un elemento de entrada correspondiente. Si lo hace, dará lugar a que, quitar el foratributo de la etiqueta de modo que los navegadores no tienen el insecto no volver a activar el y después use una setTimeoutde 0msañadir la forvuelta atributo una vez que el evento ha burbujeaba. Esto significa event.preventDefaultque no tiene que ser llamado y, por lo tanto, no se cancelarán otras acciones / eventos. Además, si necesito anular esta función, solo tengo que agregar un detector de eventos que llame Event#preventDefaulto elimine el foratributo.

manageLabel

La funciónmanageLabelacepta una etiqueta comprueba si ya se ha agregado un detector de eventos para evitar volver a agregarlo, agrega el detector si aún no se ha agregado y lo agrega a la lista de etiquetas que se han administrado.

onLoad

Es función para todas las etiquetas en el DOM en ese momento. La función también usa un MutationObserver para capturar cualquier etiqueta que se agregue, después de que se haya disparado la carga (y se haya ejecutado el script).onLoad necesita ser llamado cuando la página se carga para que la función manageLabel

El código que se muestra arriba fue optimizado por Martin Barker .

Nick Zoum
fuente
Su código no está optimizado haciendo algunas comprobaciones que no necesita y algunos ciclos de CPU costosos, lo he optimizado para usted en pastebin.com/FDXwQL1d
Barkermn01
No elimina el cheque, lo reemplaza con un HTMLLabelElementcheque en lugar de un elemento HTMLE y un cheque de que tagName era una etiqueta
Barkermn01
inteligente pero enormemente complicado
Steve Tomlin
2

Sé que no querías oyentes de JS Event, pero estoy pensando que quieres identificar el movimiento que no es así, pero está usando mousedown en lugar de clic (mousedown seguido de mouseup).

Si bien este es un error conocido en Firefox, puede solucionarlo utilizando el evento mousedown

He tenido que cambiar su identificación para que sea válida. Una identificación debe comenzar con un personaje

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
fuente
El problema es que tendré que ejecutar este script (usando querySelectorAll) en la carga de la página y luego, cada vez que se agregue una etiqueta al dom. Esto también puede estropear cualquier oyente de eventos de mouse agregado a las etiquetas o al dom en general, ya que lo usa Event#preventDefaultpara evitar hacer doble clic en los navegadores que no tienen este error. Finalmente, usar el clickevento sería mejor, ya que tendría la misma interacción que la acción prevista. Aparte de eso, esta es la mejor respuesta hasta ahora.
Nick Zoum
@nickzoum He actualizado el código para solucionar su preventDefault()problema, por lo que funciona comprobando si el navegador lo ha cambiado si no lo cambia, después de 150 ms lo suficientemente rápido para que el usuario no lo note, y lo suficientemente lento como para que el navegador haya actuado intime si va a hacer lo que se supone que debe hacer. ¿eso está mejor?
Barkermn01
0

No. Esto parece un error de Firefox y no un problema con su código. No creo que haya una solución css para este comportamiento.

Es posible que pueda informarlo a Mozilla y solucionar el problema, pero no confiaría en eso. https://bugzilla.mozilla.org/home

Para una posible solución alternativa, sugeriría activar el evento en el mouseup.

Garet Webster
fuente
0

Sin javascript, cuando hace clic en la etiqueta que tiene su valor "for" igual que el valor de "id" de las entradas, se hace clic en la entrada, pero esto no es coherente entre los navegadores.

Si un navegador sigue lo anterior, su evento de clic de JavaScript cancelará el efecto, lo que termina sin hacer nada.

Una solución

Para tener coherencia entre los navegadores, puede adoptar una estrategia diferente: Onload cambia dinámicamente todos los atributos 'para' para 'datos para', por lo que anula el efecto del navegador original. Luego puede aplicar su evento de clic a cada etiqueta.

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
fuente
document.attachEvent("onreadystatechange",Solo estoy intrigado, ¿cómo es que fuiste con eso y no document.addEventListener("DOMContentLoaded",?
Barkermn01
Era solo un método que vi, que es un poco más x-browser. Elige el que prefieras.
Steve Tomlin
-2

Adjuntar el evento al documento y apuntar al elemento que necesita allí debería resolver este problema.

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

Sin embargo, al leer sobre este tema en el pasado, Firefox debe entender su acción como un intento de arrastrar el elemento, sin embargo, dado que la selección del usuario es ninguno, solo evita el comportamiento predeterminado.

Esto se basa en un conocimiento bastante limitado, pero parece ser un error / peculiaridad conocida y hay algunos artículos que lo respaldan.

Benjamin James Kippax
fuente
Lo dije explícitamente en la pregunta without using JavaScript event listeners.
nick zoum
@nickzoum ya que este es un error conocido en Firefox, creo que no tienes suerte.
Benjamin James Kippax