L'étiquette HTML ne déclenche pas l'entrée respective si la souris se déplace en cliquant dans Firefox

13

Dans l'exemple suivant, lorsque vous cliquez sur l'étiquette, l'entrée change d'état.

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>

Dans Chrome, lorsque vous déplacez le curseur entre les événements mousedownet mouseup, l'entrée est toujours déclenchée, tandis que dans Firefox, la case à cocher ne change pas d'état.

Y'a t'il un moyen d'arranger cela? (sans utiliser les écouteurs d'événement JavaScript)

Version de Firefox: 69.0.3 (64-bit)

Ensemble complet d'actions lors de l'utilisation de Chrome.

  1. Appuyez sur le bouton sur l'étiquette
  2. Déplacez le curseur (même en dehors de l'étiquette) tout en maintenant le bouton enfoncé
  3. Remettez le curseur sur l'étiquette
  4. Relâchez le bouton
nick zoum
la source
Dans Chrome, lorsque vous déplacez le curseur entre les événements mousedown et mouseup, l'entrée est toujours déclenchée -> ce n'est pas le cas pour moi sur Chrome et cela ne devrait pas l'être. un clic = mousedown + mouseup .. lié pour quelques idées: stackoverflow.com/a/51451218/8620333
Temani Afif
@TemaniAfif Est-ce le cas pour vous sur Chrome maintenant? (puisque j'ai clarifié ce que je fais). Ou ne modifie-t-il toujours pas l'état de la case à cocher?
nick zoum
1
Si nous gardons la souris sur l'étiquette, ça va. le mouvement ne devrait pas affecter cela, donc je suppose que nous sommes confrontés à un bug Firefox
Temani Afif
2
il s'agit d'un bogue qui est consigné en tant qu'état NON CONFIRMÉ dans le portail des bogues de Firefox, "Un événement" clic "ne devrait se produire que si la souricière et la souris se trouvaient au même emplacement", vous pouvez y vérifier l'url du bogue: bugzilla.mozilla.org/show_bug. cgi? id = 319347
Jadli
1
@TemaniAfif Je suis d'accord, le fait de déplacer le curseur 1pxinterrompra même l'interaction.
nick zoum

Réponses:

3

introduction

Bien que j'aie spécifiquement indiqué dans la question que la réponse ne devrait pas impliquer JavaScript, toutes les réponses fonctionnaient avec JavaScript.
Étant donné que cela semble être un bogue de Firefox et que la plupart des réponses soumises à ce stade nécessiteraient que je modifie également le reste de mon code, j'ai décidé de créer un script qui peut être exécuté une fois, traitera toutes les étiquettes indépendamment du moment ils sont ajoutés au dom et auront le moins d'impact sur mes autres scripts.

Solution - Exemple

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>

Explication

onLabelClick

La fonction onLabelClickdoit être appelée chaque fois qu'une étiquette est cliquée, elle vérifiera si l'étiquette a un élément d'entrée correspondant. Si c'est le cas, il le déclenchera, supprimera l' forattribut de l'étiquette afin que les navigateurs n'aient pas le bogue ne le déclenchera pas à nouveau, puis utilisez un setTimeoutde 0mspour rajouter l' forattribut une fois que l'événement s'est propagé. Cela signifie qu'il event.preventDefaultn'est pas nécessaire d'être appelé et donc aucune autre action / événement ne sera annulée. De plus, si j'ai besoin de remplacer cette fonction, je dois simplement ajouter un écouteur d'événements qui appelle Event#preventDefaultou supprime l' forattribut.

manageLabel

La fonctionmanageLabelaccepte une étiquette vérifie s'il a déjà été ajouté un écouteur d'événements pour éviter de l'ajouter à nouveau, ajoute l'écouteur s'il n'a pas déjà été ajouté et l'ajoute à la liste des étiquettes gérées.

onLoad

La fonction onLoaddoit être appelée lorsque la page est chargée afin qu'elle manageLabelpuisse être appelée pour toutes les étiquettes du DOM à ce moment. La fonction utilise également un MutationObserver pour intercepter toutes les étiquettes ajoutées après le lancement de la charge (et l'exécution du script).

Le code affiché ci-dessus a été optimisé par Martin Barker .

nick zoum
la source
Votre code n'est pas optimisé en faisant des vérifications inutiles et des cycles de processeur coûteux, je l'ai optimisé pour vous sur pastebin.com/FDXwQL1d
Barkermn01
Il ne supprime pas le chèque qu'il le remplace par HTMLLabelElementchèque au lieu d'un HTMLElement et d'un chèque que le tagName était une étiquette
Barkermn01
intelligent mais massivement trop compliqué
Steve Tomlin
2

Je sais que vous ne vouliez pas d'écouteurs JS Event, mais je pense que vous voulez identifier le mouvement, ce n'est pas le cas, mais utilisez mousedown au lieu de cliquer (mousedown suivi de mouseup).

Bien qu'il s'agisse d'un bug connu dans Firefox, vous pouvez le contourner en utilisant l' événement mousedown

J'ai dû changer votre identifiant pour être un identifiant valide doit commencer par un caractère

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
la source
Le problème est que je devrai exécuter ce script (en utilisant querySelectorAll) au chargement de la page et ensuite, chaque fois qu'une étiquette est ajoutée au dom. Cela peut également gâcher les écouteurs d'événements de souris ajoutés aux étiquettes ou au dom en général lorsque vous utilisez Event#preventDefaultpour éviter de double-cliquer sur les navigateurs qui n'ont pas ce bogue. Enfin, il clickserait préférable d' utiliser l' événement, car il aurait la même interaction que l'action envisagée. À part cela, c'est la meilleure réponse jusqu'à présent.
nick zoum
@nickzoum j'ai mis à jour le code pour résoudre votre preventDefault()problème, donc cela fonctionne en vérifiant si le navigateur l'a changé sinon le change, après 150 ms assez vite pour que l'utilisateur ne le remarque pas, et assez lentement pour que le navigateur ait agi intime si ça va faire ce qu'il est censé faire. Est-ce mieux?
Barkermn01
0

Non. Cela ressemble à un bogue firefox et non à un problème avec votre code. Je ne crois pas qu'il existe une solution de contournement CSS pour ce comportement.

Vous pourrez peut-être le signaler à Mozilla et résoudre le problème, mais je ne m'y fierais pas. https://bugzilla.mozilla.org/home

Pour une solution de contournement potentielle, je suggérerais plutôt de déclencher l'événement sur mouseup.

Garet Webster
la source
0

Sans javascript, lorsque vous cliquez sur l'étiquette dont la valeur "pour" est identique à la valeur "id" des entrées, l'entrée est cliquée, mais cela n'est pas cohérent entre les navigateurs.

Si un navigateur suit ce qui précède, votre événement de clic javascript annulera l'effet, ce qui finira par ne rien faire.

Une solution

Pour avoir une cohérence entre les navigateurs, vous pouvez adopter une stratégie différente: Onload change dynamiquement tous les attributs "for" pour "data-for", de sorte qu'il annule l'effet du navigateur d'origine. Ensuite, vous pouvez appliquer votre événement de clic à chaque étiquette.

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
la source
document.attachEvent("onreadystatechange",Je suis juste perplexe document.addEventListener("DOMContentLoaded",.
Barkermn01
C'était juste une méthode que j'ai vue qui est un peu plus x-browser. Choisissez celui que vous préférez.
Steve Tomlin
-2

Attacher l'événement au document et cibler l'élément dont vous avez besoin devrait trier ce problème.

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

De la lecture de ce sujet dans le passé, il revient à Firefox de comprendre votre action comme une tentative de faire glisser l'élément, cependant, puisque la sélection de l'utilisateur est nulle, cela empêche simplement le comportement par défaut.

Ceci est basé sur des connaissances assez limitées mais il semble que ce soit un bug / bizarrerie connu et il y a quelques articles qui flottent autour de cela.

Benjamin James Kippax
la source
J'ai dit explicitement dans la question without using JavaScript event listeners.
nick zoum
@nickzoum puisqu'il s'agit d'un bug connu dans Firefox, je pense que vous n'avez pas de chance.
Benjamin James Kippax