如果在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中,当您在mousedown和mouseup事件之间移动光标时,输入仍会触发 ->在chrome上我不是这种情况,也不应该是ot。单击= mousedown + mouseup ..相关一些想法:stackoverflow.com/a/51451218/8620333
Temani Afif

@TemaniAfif现在在chrome上适合您吗?(因为我已经澄清了自己在做什么)。还是仍然不更改复选框的状态?
nick zoum

1
如果我们将鼠标悬停在标签上,那就可以了。此举应该不会影响这个,所以我想我们正面临着一个Firefox的错误
毯螅阿菲夫

2
这是一个错误,该错误在firfox错误门户中记录为UNCONFIRMED状态,“仅当mousedown和mouseup位于相同位置时,才发生“单击”事件“,您可以在此处检查错误URL:bugzilla.mozilla.org/show_bug。 cgi?id = 319347
Jadli

1
@TemaniAfif我同意,甚至移动光标1px也会中断交互。
nick zoum

Answers:


3

介绍

尽管我在问题中明确指出答案不应该涉及JavaScript,但是所有答案都适用于JavaScript。
由于这似乎是Firefox错误,并且此时提交的大多数答案都需要我也更改其余代码,因此我决定创建一个可以运行一次的脚本,该脚本将处理所有标签,无论何时它们被添加到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标签的属性,以使没有错误的浏览器不会重新触发该标签,然后在事件冒泡时使用setTimeoutof 0msfor属性重新添加回去。这意味着event.preventDefault不必调用,因此不会取消其他任何操作/事件。同样,如果我需要重写此功能,我只需要添加一个调用Event#preventDefault或删除for属性的事件监听器。

manageLabel

功能manageLabel接受标签,检查是否已添加事件侦听器以避免重新添加它;如果尚未添加,则添加侦听器,并将其添加到已被管理的标签列表中。页面加载时需要调用

onLoad

该函数onLoad,以便manageLabel可以在那时为DOM上的所有标签调用该函数。该函数还使用MutationObserver来捕获已触发加载(并且脚本已运行)后添加的所有标签。

上面显示的代码已由Martin Barker优化。


您的代码有些未经优化,不需要做一些检查,并花费了一些昂贵的CPU周期,我已经在pastebin.com/FDXwQL1d上
Barkermn01,19年

它不会删除检查,而是将其替换为HTMLLabelElementcheck而不是HTMLElement以及tagName是标签的检查
Barkermn01

聪明但又过于复杂
史蒂夫·汤姆林 Steve Tomlin),

2

我知道您不希望使用JS Event侦听器,但是我想您的意思是确定运动的方向,但不是使用mousedown而不是click(先单击mousedown,再单击mouseup)。

尽管这是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时运行。通常,这也可能会使添加到标签或dom上的任何鼠标事件侦听器混乱,因为您Event#preventDefault要避免在没有此错误的浏览器上双击。最后,使用click事件会更好,因为它与预期的动作具有相同的交互作用。除此之外,这是迄今为止最好的答案。
nick zoum

@nickzoum我已经更新了代码来解决您的preventDefault()问题,因此它的工作原理是检查浏览器是否已更改(如果未更改),在150毫秒后足以使用户不注意到它,并且速度足够慢以使浏览器能够正常工作如果它打算去做的话,那就及时。那个更好吗?
Barkermn01,19年

0

不。这看起来像是Firefox错误,而不是您的代码有问题。我不认为有此行为的CSS解决方法。

您也许可以将其报告给Mozilla并解决此问题,但是我不会依靠它。https://bugzilla.mozilla.org/home

对于可能的解决方法,我建议改为在mouseup上触发事件。


0

如果没有使用javascript,当您单击其“ for”值与输入“ id”值相同的标签时,则会单击输入,但这在浏览器之间是不一致的。

如果浏览器确实遵循上述要求,那么您的javascript click事件将取消该效果,最终无济于事。

一个解法

为了在浏览器之间保持一致,您可以采用不同的策略:Onload动态地将所有属性“ for”更改为“ data-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,19年

我所看到的只是一种方法,它比X浏览器多一点。选择您喜欢的那个。
史蒂夫·汤姆林

-2

将事件附加到文档并针对您在其中需要的元素进行定位应该解决此问题。

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

过去从阅读本主题开始,取决于Firefox将您的操作理解为尝试拖动元素的尝试,但是,由于用户选择为none,它只会阻止默认行为。

这是基于相当有限的知识,但似乎是一个已知的错误/怪癖,并且有几篇文章支持该知识。


我在问题中明确表示without using JavaScript event listeners
nick zoum

@nickzoum,因为这是Firefox中的已知错误,所以我认为您不走运。
本杰明·詹姆斯·基帕克斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.