Label HTML tidak memicu input masing-masing jika mouse dipindahkan saat mengklik di Firefox

13

Dalam contoh berikut, ketika Anda mengklik label, input berubah status.

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>

Di Chrome, ketika Anda memindahkan kursor antara mousedowndan mouseupperistiwa input masih akan terpicu, sedangkan di Firefox kotak centang tidak berubah status.

Apakah ada cara untuk memperbaikinya? (tanpa menggunakan pendengar acara JavaScript)

Versi Firefox: 69.0.3 (64-bit)

Set penuh tindakan saat menggunakan chrome.

  1. Tekan tombol di atas label
  2. Gerakkan kursor di sekitar (bahkan di luar label) sambil tetap menahan tombol
  3. Kembalikan kursor ke label
  4. Lepaskan tombol
nick zoum
sumber
Di Chrome, ketika Anda memindahkan kursor antara peristiwa mouse dan mouseup, input masih akan terpicu -> itu tidak terjadi pada saya di chrome dan tidak seharusnya. a click = mousedown + mouseup .. terkait untuk beberapa ide: stackoverflow.com/a/51451218/8620333
Temani Afif
@TemaniAfif Apakah ini masalahnya untuk Anda di chrome sekarang? (Karena saya sudah mengklarifikasi apa yang saya lakukan). Atau apakah masih tidak mengubah kondisi kotak centang?
nick zoum
1
Jika kita menyimpan mouse di atas label maka tidak masalah. langkah ini seharusnya tidak memengaruhi ini, jadi saya kira kita menghadapi bug Firefox
Temani Afif
2
ini adalah bug yang dicatat sebagai status TIDAK TERKONFIRMASI di portal bug firfox, "A" klik "acara hanya akan terjadi jika mouse dan mouseup berada di lokasi yang sama" Anda dapat memeriksa bug di sana url: bugzilla.mozilla.org/show_bug. cgi? id = 319347
Jadli
1
@TemaniAfif Saya setuju, menggerakkan kursor 1pxakan memecah interaksi.
nick zoum

Jawaban:

3

pengantar

Meskipun saya secara khusus menyatakan dalam pertanyaan bahwa jawabannya tidak boleh melibatkan JavaScript, semua jawaban bekerja dengan JavaScript.
Karena ini tampaknya merupakan bug Firefox dan sebagian besar jawaban yang diajukan pada saat ini akan mengharuskan saya untuk juga mengubah sisa kode saya, saya memutuskan untuk membuat skrip yang dapat dijalankan sekali, akan menangani semua label terlepas dari kapan mereka ditambahkan ke dom dan akan memiliki dampak paling kecil pada skrip saya yang lain.

Solusi - Contoh

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>

Penjelasan

onLabelClick

Fungsi onLabelClickperlu dipanggil setiap kali label diklik, itu akan memeriksa apakah label memiliki elemen input yang sesuai. Jika tidak, itu akan memicu itu, menghapus foratribut label sehingga browser tidak memiliki bug tidak akan memicu kembali dan kemudian menggunakan setTimeoutdari 0msuntuk menambahkan forkembali atribut sekali acara telah menggelegak. Ini berarti event.preventDefaulttidak harus dipanggil dan dengan demikian tidak ada tindakan / acara lain yang akan dibatalkan. Juga jika saya perlu mengesampingkan fungsi ini, saya hanya perlu menambahkan event-listener yang memanggil Event#preventDefaultatau menghapus foratribut.

manageLabel

FungsinyamanageLabelmenerima cek label jika telah ditambahkan pendengar acara untuk menghindari menambahkannya kembali, menambahkan pendengar jika belum ditambahkan, dan menambahkannya ke daftar label telah dikelola.

onLoad

Fungsi onLoadperlu dipanggil ketika halaman dimuat sehingga fungsi manageLabeldapat dipanggil untuk semua label pada DOM pada saat itu. Fungsi ini juga menggunakan MutationObserver untuk menangkap label apa pun yang ditambahkan, setelah beban dipecat (dan skrip telah dijalankan).

Kode yang ditampilkan di atas dioptimalkan oleh Martin Barker .

nick zoum
sumber
Kode Anda sedikit tidak dioptimalkan melakukan beberapa pemeriksaan itu tidak perlu dan beberapa Siklus CPU mahal, saya telah dioptimalkan untuk Anda di pastebin.com/FDXwQL1d
Barkermn01
Itu tidak menghapus cek yang menggantikannya dengan HTMLLabelElementcek alih-alih HTMLElement dan cek bahwa tagName adalah label
Barkermn01
pintar tapi terlalu rumit
Steve Tomlin
2

Saya tahu Anda tidak ingin pendengar Acara JS, tetapi saya berpikir Anda bermaksud mengidentifikasi gerakan ini tetapi tidak menggunakan mousedown alih-alih klik (mousedown diikuti oleh mouseup).

Meskipun ini adalah bug yang dikenal di Firefox Anda bisa mengatasinya dengan menggunakan acara mousedown

Saya harus mengubah id Anda menjadi id yang valid harus dimulai dengan karakter

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
sumber
Masalahnya adalah bahwa saya harus menjalankan skrip ini (menggunakan querySelectorAll) pada halaman memuat dan kemudian, setiap kali label ditambahkan ke dom. Ini juga dapat mengacaukan pendengar acara mouse yang ditambahkan ke label atau dom secara umum seperti yang Anda gunakan Event#preventDefaultuntuk menghindari mengklik dua kali pada browser yang tidak memiliki bug ini. Akhirnya, menggunakan clickacara itu akan lebih baik, karena akan memiliki interaksi yang sama dengan tindakan yang dimaksud. Selain itu, ini adalah jawaban terbaik sejauh ini.
nick zoum
@nickzoum saya telah memperbarui kode untuk memperbaiki preventDefault()masalah Anda , jadi ia bekerja dengan memeriksa apakah browser telah mengubahnya jika tidak mengubahnya, setelah 150 ms cukup cepat bagi pengguna untuk tidak melihatnya, dan cukup lambat bahwa browser akan bertindak Intime jika itu akan melakukan apa yang seharusnya. Apakah itu lebih baik?
Barkermn01
0

Tidak. Ini terlihat seperti bug firefox dan bukan masalah dengan kode Anda. Saya tidak percaya ada solusi css untuk perilaku ini.

Anda mungkin dapat melaporkannya ke Mozilla dan memperbaiki masalahnya, tetapi saya tidak akan bergantung pada itu. https://bugzilla.mozilla.org/home

Untuk solusi yang potensial, saya akan menyarankan memicu acara di mouseup sebagai gantinya.

Garet Webster
sumber
0

Tanpa javascript, ketika Anda mengklik label yang memiliki nilai "untuk" sama dengan nilai "id" input, maka input diklik, tetapi ini tidak konsisten di antara browser.

Jika browser mengikuti hal di atas, maka acara klik javascript Anda akan membatalkan efeknya, yang akhirnya tidak melakukan apa-apa.

Sebuah solusi

Untuk memiliki konsistensi di antara peramban, Anda dapat mengadopsi strategi yang berbeda: Membebani secara dinamis mengubah semua atribut 'untuk' untuk 'data-untuk', sehingga membatalkan pengaruh browser asli. Kemudian Anda dapat menerapkan acara klik Anda untuk setiap label.

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
sumber
document.attachEvent("onreadystatechange",Saya hanya tidak mengerti kenapa Anda bisa melakukannya dan tidak document.addEventListener("DOMContentLoaded",?
Barkermn01
Itu hanya metode yang saya lihat yang sedikit lebih banyak x-browser. Pilih mana yang Anda inginkan.
Steve Tomlin
-2

Melampirkan acara ke dokumen dan menargetkan elemen yang Anda butuhkan di sana harus menyelesaikan masalah ini.

$ (Dokumen) .on ('klik', '.item', fungsi (acara) {});

Dari membaca topik ini di masa lalu, turun ke Firefox untuk memahami tindakan Anda sebagai upaya untuk menyeret elemen, karena pilih pengguna tidak ada, itu hanya mencegah perilaku default.

Ini didasarkan pada pengetahuan yang cukup terbatas tetapi tampaknya bug / kekhasan yang diketahui dan ada beberapa artikel yang beredar mendukung hal ini.

Benjamin James Kippax
sumber
Saya secara eksplisit mengatakan dalam pertanyaan itu without using JavaScript event listeners.
nick zoum
@nickzoum karena ini adalah bug yang dikenal di Firefox, saya pikir Anda kurang beruntung.
Benjamin James Kippax