HTML-мітка не запускає відповідний ввід, якщо миша переміщується під час натискання Firefox


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 при переміщенні курсору між подіями mousedownта mouseupподіями введення все ще спрацьовує, тоді як у Firefox прапорець не змінює стан.

Чи є спосіб це виправити? (без використання слухачів подій JavaScript)

Версія Firefox: 69.0.3 (64-bit)

Повний набір дій при використанні хрому.

  1. Натисніть кнопку над етикеткою
  2. Перемістіть курсор навколо (навіть поза міткою), утримуючи кнопку
  3. Поверніть курсор назад до мітки
  4. Відпустіть кнопку

У Chrome, коли ви переміщуєте курсор між подіями mousedown та mouseup, введення все ще спрацьовує -> для chrome це не так, і ot не повинно бути. клік = MouseDown + MouseUp .. пов'язані деякі ідеї: stackoverflow.com/a/51451218/8620333
теманянина Afif

@TemaniAfif Це стосується вас хрому? (оскільки я уточнив, що це роблю). Або все ще не змінюється стан прапорця?
nick zoum

1
Якщо ми тримаємо мишу над міткою, то це нормально. цей крок не повинен впливати на це, тому я думаю, що ми стикаємося з помилкою Firefox
Temani Afif

2
це помилка, яка зафіксована у статусі НЕУТВЕРДЖЕНО на порталі помилок firfox, подія "клацання" має відбутися лише в тому випадку, якщо поселенці та миші були в одному місці ", ви можете перевірити там URL-адресу помилки: bugzilla.mozilla.org/show_bug. cgi? id = 319347
Ядлі

1
@TemaniAfi Якщо я згоден, переміщення курсору навіть 1pxпорушить взаємодію.
nick zoum

Відповіді:


3

Вступ

Хоча я спеціально зазначив у запитанні, що відповідь не повинен включати JavaScript, усі відповіді працювали з JavaScript.
Оскільки це, здається, помилка Firefox, і більшість відповідей, поданих у цей момент, вимагають від мене також змінити решту мого коду, я вирішив створити сценарій, який можна запустити один раз, буде мати справу з усіма мітками незалежно від того, коли вони додаються до дому та матимуть найменший вплив на мої інші сценарії.

Рішення - приклад

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атрибут етикетки , так що браузери не помилка не буде повторно викликати його , а потім використовувати setTimeoutз 0msдля додавання forатрибута назад після того , як подія міхура. Це означає, event.preventDefaultщо не потрібно телефонувати, і тому інші дії / події не будуть скасовані. Крім того, якщо мені потрібно змінити цю функцію, я просто повинен додати слухача подій, який викликає Event#preventDefaultабо видаляє forатрибут.

manageLabel

ФункціяmanageLabelприймає перевірку міток, якщо до неї вже додано слухача події, щоб уникнути його повторного додавання, додає слухача, якщо він ще не був доданий, і додає його до списку міток, якими керував.

onLoad

Функцію onLoadпотрібно викликати, коли сторінка завантажується, щоб функція manageLabelмогла викликати всі мітки в DOM на той момент. Функція також використовує MutationObserver для лову будь-яких міток, які додаються, після запуску завантаження (і сценарій запущений).

Код, відображений вище, був оптимізований Мартіном Баркером .


Код трохи неоптимізований, роблячи перевірки, які йому не потрібні, а також деякі дорогі цикли процесора, я оптимізував його для вас на pastebin.com/FDXwQL1d
Barkermn01

Він не знімає чек, він замінює його HTMLLabelElementчеком, а не HTMLElement і перевіряє, що tagName був міткою
Barkermn01

розумний, але масово надскладний
Стів Томлін

2

Я знаю, що ви не хотіли слухачів події JS Event, але я думаю, що ви маєте на увазі ідентифікацію руху, це не має, але використовує mousedown замість клацання (mousedown, а потім миша).

Хоча це відома помилка у Firefox, ви могли її обійти, скориставшись подією mousedown

Мені довелося змінити ваш ідентифікатор, щоб він був дійсним, ідентифікатор повинен починатися з символу

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) під час завантаження сторінки, а потім кожного разу, коли мітка додається до дому. Це також може зіпсувати будь-які слухачі подій миші, додані до міток або дому в цілому, як ви використовуєте, Event#preventDefaultщоб уникнути подвійного клацання на браузерах, у яких немає цієї помилки. Нарешті, використання clickподії було б краще, оскільки воно матиме таку саму взаємодію, як і передбачувана дія. Окрім цього, поки що це найкраща відповідь.
нік зум

@nickzoum Я оновив код, щоб виправити вашу preventDefault()проблему, тому він працює, перевіривши, чи змінив його браузер, якщо не змінив, після 150 мс досить швидко, щоб користувач не помітив цього, і досить повільно, що браузер діяв інтим, якщо він збирається робити те, що повинен. це краще?
Barkermn01

0

Ні. Це схоже на помилку Firefox, а не проблему з вашим кодом. Я не вірю, що для цієї поведінки існує рішення css.

Можливо, ви зможете повідомити про це Mozilla і виправити проблему, але я б на це не покладався. https://bugzilla.mozilla.org/home

Для потенційного рішення я б запропонував замість цього запустити подію.


0

Без JavaScript, коли ви клацаєте мітку, яка має значення "для", те саме, що значення "id" для входів, тоді натискають на вхід, але це не відповідає серед браузерів.

Якщо веб-переглядач дотримується вищесказаного, то подія клацання javascript скасує ефект, який у результаті нічого не робить.

Вирішення

Для узгодженості між браузерами ви можете прийняти іншу стратегію: 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

Це був лише метод, який я побачив, який трохи більше x-браузера. Виберіть те, що вам більше подобається.
Стів Томлін

-2

Приєднавши подію до документа та націлюючи на потрібний вам елемент, слід розібрати цю проблему.

$ (Document) .on ('click', '.item', функція (подія) {});

Якщо читати цю тему раніше, Firefox розуміє вашу дію як спробу перетягнути елемент, однак вибору користувача немає, це просто запобігає поведінці за замовчуванням.

Це ґрунтується на досить обмежених знаннях, але, здається, це відома помилка / примха, і існує кілька статей, що плавають навколо цього.


Я прямо сказав у питанні without using JavaScript event listeners.
нік зум

@nickzoum, оскільки це відома помилка у Firefox, я думаю, вам не пощастило.
Бенджамін Джеймс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.