Apa gunanya terdekat di javascript?

Pernahkah Anda mengalami masalah dalam menemukan induk dari simpul DOM di JavaScript, tetapi tidak yakin berapa banyak level yang harus Anda lewati untuk mencapainya?

<div data-id="123">
  <button>Click me</button>
</div>

Itu cukup mudah, bukan?

var button = document.querySelector("button");


button.addEventListener("click", (evt) => {
  console.log(evt.target.parentNode.dataset.id);
  // prints "123"
});
_

Dalam hal ini, Node. parentNode API sudah cukup. Apa yang dilakukannya adalah mengembalikan simpul induk dari elemen yang diberikan. Dalam contoh di atas,

var button = document.querySelector("button");


button.addEventListener("click", (evt) => {
  console.log(evt.target.parentNode.dataset.id);
  // prints "123"
});
_9adalah tombol yang diklik;

Tapi bagaimana jika struktur HTML bersarang lebih dalam dari itu?

<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
_

Pekerjaan kami menjadi jauh lebih sulit dengan menambahkan beberapa elemen HTML lagi. Tentu, kita bisa melakukan sesuatu seperti

<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
_0, tapi ayolah… itu tidak elegan, dapat digunakan kembali, atau dapat diskalakan

Cara lama. Menggunakan <div data-id="123">   <article>     <header>       <h1>Some title</h1>       <button>Click me</button>     </header>      <!-- .. -->   </article> </div>_1-loop

Salah satu solusinya adalah dengan menggunakan

<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
1 loop yang berjalan hingga simpul induk ditemukan

function getParentNode(el, tagName) {
  while (el && el.parentNode) {
    el = el.parentNode;
    
    if (el && el.tagName == tagName.toUpperCase()) {
      return el;
    }
  }
  
  return null;
}
_

Menggunakan contoh HTML yang sama dari atas lagi, akan terlihat seperti ini

var button = document.querySelector("button");


console.log(getParentNode(button, 'div').dataset.id);
// prints "123"
_

Solusi ini jauh dari sempurna. Bayangkan jika Anda ingin menggunakan ID atau kelas atau jenis pemilih lainnya, alih-alih nama tag. Setidaknya itu memungkinkan sejumlah variabel simpul anak antara induk dan sumber kami

Ada juga jQuery

Kembali pada hari itu, jika Anda tidak ingin berurusan dengan menulis jenis fungsi yang kami lakukan di atas untuk setiap aplikasi (dan mari kita menjadi nyata, siapa yang mau?), maka perpustakaan seperti jQuery berguna (dan masih . Ini menawarkan metode

<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
_3 untuk hal itu

$("button").closest("[data-id='123']")

Cara baru. Menggunakan <div data-id="123">   <article>     <header>       <h1>Some title</h1>       <button>Click me</button>     </header>      <!-- .. -->   </article> </div>_4

Meskipun jQuery masih merupakan pendekatan yang valid (hei, sebagian dari kita terikat padanya), menambahkannya ke proyek hanya untuk metode yang satu ini adalah berlebihan, terutama jika Anda dapat melakukan hal yang sama dengan JavaScript asli

Dan di situlah

<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
_5 beraksi

var button = document.querySelector("button");


console.log(button.closest("div"));
// prints the HTMLDivElement

Di sana kita pergi. Begitulah mudahnya, dan tanpa pustaka atau kode tambahan apa pun

<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
_4 memungkinkan kita melintasi DOM sampai kita mendapatkan elemen yang cocok dengan pemilih yang diberikan. Kehebatannya adalah kami dapat melewati pemilih mana pun yang juga akan kami berikan kepada
<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
7 atau
<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
8. Itu bisa berupa ID, kelas, atribut data, tag, atau apa pun

element.closest("#my-id"); // yep
element.closest(".some-class"); // yep
element.closest("[data-id]:not(article)") // hell yeah

Jika

<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
_5 menemukan simpul induk berdasarkan pemilih yang diberikan, ia mengembalikannya dengan cara yang sama seperti
function getParentNode(el, tagName) {
  while (el && el.parentNode) {
    el = el.parentNode;
    
    if (el && el.tagName == tagName.toUpperCase()) {
      return el;
    }
  }
  
  return null;
}
0. Jika tidak, jika tidak menemukan induk, ia mengembalikan
function getParentNode(el, tagName) {
  while (el && el.parentNode) {
    el = el.parentNode;
    
    if (el && el.tagName == tagName.toUpperCase()) {
      return el;
    }
  }
  
  return null;
}
1 sebagai gantinya, membuatnya mudah digunakan dengan kondisi
function getParentNode(el, tagName) {
  while (el && el.parentNode) {
    el = el.parentNode;
    
    if (el && el.tagName == tagName.toUpperCase()) {
      return el;
    }
  }
  
  return null;
}
2

var button = document.querySelector("button");


console.log(button.closest(".i-am-in-the-dom"));
// prints HTMLElement


console.log(button.closest(".i-am-not-here"));
// prints null


if (button.closest(".i-am-in-the-dom")) {
  console.log("Hello there!");
} else {
  console.log(":(");
}

Siap untuk beberapa contoh kehidupan nyata?

Gunakan Kasus 1. Dropdown

CodePen Embed Fallback

Demo pertama kami adalah implementasi dasar (dan jauh dari sempurna) dari menu tarik-turun yang terbuka setelah mengeklik salah satu item menu tingkat atas. Perhatikan bagaimana menu tetap terbuka bahkan saat mengklik di mana saja di dalam dropdown atau memilih teks?

<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
5 API adalah yang mendeteksi klik luar itu. Dropdown itu sendiri adalah elemen
function getParentNode(el, tagName) {
  while (el && el.parentNode) {
    el = el.parentNode;
    
    if (el && el.tagName == tagName.toUpperCase()) {
      return el;
    }
  }
  
  return null;
}
_4 dengan kelas
function getParentNode(el, tagName) {
  while (el && el.parentNode) {
    el = el.parentNode;
    
    if (el && el.tagName == tagName.toUpperCase()) {
      return el;
    }
  }
  
  return null;
}
5, jadi mengklik di mana saja di luar menu akan menutupnya. Itu karena nilai untuk
function getParentNode(el, tagName) {
  while (el && el.parentNode) {
    el = el.parentNode;
    
    if (el && el.tagName == tagName.toUpperCase()) {
      return el;
    }
  }
  
  return null;
}
_6 akan menjadi
function getParentNode(el, tagName) {
  while (el && el.parentNode) {
    el = el.parentNode;
    
    if (el && el.tagName == tagName.toUpperCase()) {
      return el;
    }
  }
  
  return null;
}
1 karena tidak ada simpul induk dengan kelas ini

function handleClick(evt) {
  // ...
  
  // if a click happens somewhere outside the dropdown, close it.
  if (!evt.target.closest(".menu-dropdown")) {
    menu.classList.add("is-hidden");
    navigation.classList.remove("is-expanded");
  }
}

Di dalam fungsi callback

function getParentNode(el, tagName) {
  while (el && el.parentNode) {
    el = el.parentNode;
    
    if (el && el.tagName == tagName.toUpperCase()) {
      return el;
    }
  }
  
  return null;
}
8, kondisi memutuskan apa yang harus dilakukan. tutup drop down. Jika tempat lain di dalam unordered list diklik,
<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
5 akan menemukan dan mengembalikannya, menyebabkan dropdown tetap terbuka

Gunakan Kasus 2. Tabel

CodePen Embed Fallback

Contoh kedua ini merender tabel yang menampilkan informasi pengguna, katakanlah sebagai komponen di dasbor. Setiap pengguna memiliki ID, tetapi alih-alih menampilkannya, kami menyimpannya sebagai atribut data untuk setiap elemen

var button = document.querySelector("button");


console.log(getParentNode(button, 'div').dataset.id);
// prints "123"
0

var button = document.querySelector("button");


button.addEventListener("click", (evt) => {
  console.log(evt.target.parentNode.dataset.id);
  // prints "123"
});
_0

Kolom terakhir berisi dua tombol untuk mengedit dan menghapus pengguna dari tabel. Tombol pertama memiliki atribut

var button = document.querySelector("button");


console.log(getParentNode(button, 'div').dataset.id);
// prints "123"
1 dari
var button = document.querySelector("button");


console.log(getParentNode(button, 'div').dataset.id);
// prints "123"
2, dan tombol kedua adalah
var button = document.querySelector("button");


console.log(getParentNode(button, 'div').dataset.id);
// prints "123"
3. Saat kami mengklik salah satunya, kami ingin memicu beberapa tindakan (seperti mengirim permintaan ke server), tetapi untuk itu, ID pengguna diperlukan

Pendengar peristiwa klik dilampirkan ke objek jendela global, jadi setiap kali pengguna mengklik di suatu tempat di halaman, fungsi panggilan balik

function getParentNode(el, tagName) {
  while (el && el.parentNode) {
    el = el.parentNode;
    
    if (el && el.tagName == tagName.toUpperCase()) {
      return el;
    }
  }
  
  return null;
}
8 dipanggil

var button = document.querySelector("button");


button.addEventListener("click", (evt) => {
  console.log(evt.target.parentNode.dataset.id);
  // prints "123"
});
_1

Jika klik terjadi di tempat lain selain salah satu tombol ini, tidak ada atribut

var button = document.querySelector("button");


console.log(getParentNode(button, 'div').dataset.id);
// prints "123"
1, maka tidak ada yang terjadi. Namun, saat mengklik salah satu tombol, tindakan akan ditentukan (omong-omong, itu disebut delegasi acara), dan sebagai langkah berikutnya, ID pengguna akan diambil dengan memanggil
var button = document.querySelector("button");


console.log(getParentNode(button, 'div').dataset.id);
// prints "123"
6

var button = document.querySelector("button");


button.addEventListener("click", (evt) => {
  console.log(evt.target.parentNode.dataset.id);
  // prints "123"
});
_2

Fungsi ini mengharapkan node DOM sebagai satu-satunya parameter dan, saat dipanggil, menggunakan

<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
5 untuk menemukan baris tabel yang berisi tombol yang ditekan. Ini kemudian mengembalikan nilai
var button = document.querySelector("button");


console.log(getParentNode(button, 'div').dataset.id);
// prints "123"
_8, yang sekarang dapat digunakan untuk mengirim permintaan ke server

Gunakan Kasus 3. Tabel di Bereaksi

Mari kita tetap menggunakan contoh tabel dan lihat bagaimana kita menanganinya pada proyek React. Berikut kode untuk komponen yang mengembalikan tabel

var button = document.querySelector("button");


button.addEventListener("click", (evt) => {
  console.log(evt.target.parentNode.dataset.id);
  // prints "123"
});
_3

Saya menemukan bahwa kasus penggunaan ini sering muncul — cukup umum untuk memetakan kumpulan data dan menampilkannya dalam daftar atau tabel, lalu mengizinkan pengguna untuk melakukan sesuatu dengannya. Banyak orang menggunakan fungsi panah sebaris, seperti itu

var button = document.querySelector("button");


button.addEventListener("click", (evt) => {
  console.log(evt.target.parentNode.dataset.id);
  // prints "123"
});
_4

Meskipun ini juga cara yang valid untuk menyelesaikan masalah, saya lebih suka menggunakan teknik

var button = document.querySelector("button");


console.log(getParentNode(button, 'div').dataset.id);
// prints "123"
8. Salah satu kelemahan dari fungsi panah inline adalah bahwa setiap kali React merender ulang daftar, ia perlu membuat fungsi panggilan balik lagi, yang mengakibatkan kemungkinan masalah kinerja saat menangani data dalam jumlah besar.

Dalam fungsi panggilan balik, kami hanya berurusan dengan acara tersebut dengan mengekstraksi target (tombol) dan mendapatkan elemen induk

var button = document.querySelector("button");


console.log(getParentNode(button, 'div').dataset.id);
// prints "123"
0 yang berisi nilai
var button = document.querySelector("button");


console.log(getParentNode(button, 'div').dataset.id);
// prints "123"
8

var button = document.querySelector("button");


button.addEventListener("click", (evt) => {
  console.log(evt.target.parentNode.dataset.id);
  // prints "123"
});
_5

Gunakan Kasus 4. Modal

CodePen Embed Fallback

Contoh terakhir ini adalah komponen lain yang saya yakin pernah Anda temui di beberapa titik. sebuah modal. Modal sering kali menantang untuk diimplementasikan karena harus menyediakan banyak fitur sekaligus dapat diakses dan (idealnya) berpenampilan menarik

Kami ingin fokus pada cara menutup modal. Dalam contoh ini, itu mungkin dengan menekan

$("button").closest("[data-id='123']")
2 pada keyboard, mengklik tombol di modal, atau mengklik di mana saja di luar modal

Dalam JavaScript kami, kami ingin mendengarkan klik di suatu tempat di modal

var button = document.querySelector("button");


button.addEventListener("click", (evt) => {
  console.log(evt.target.parentNode.dataset.id);
  // prints "123"
});
_6

Modal disembunyikan secara default melalui kelas utilitas ________11______3. Hanya ketika pengguna mengklik tombol merah besar, modal terbuka dengan menghapus kelas ini. Dan begitu modal terbuka, mengklik di mana saja di dalamnya — kecuali tombol tutup — tidak akan menutupnya secara tidak sengaja. Fungsi panggilan balik pendengar acara bertanggung jawab untuk itu

var button = document.querySelector("button");


button.addEventListener("click", (evt) => {
  console.log(evt.target.parentNode.dataset.id);
  // prints "123"
});
_7

var button = document.querySelector("button");


button.addEventListener("click", (evt) => {
  console.log(evt.target.parentNode.dataset.id);
  // prints "123"
});
_9 adalah simpul DOM yang diklik yang, dalam contoh ini, adalah seluruh tampilan latar belakang modal,
$("button").closest("[data-id='123']")
5. Node DOM ini tidak berada dalam
$("button").closest("[data-id='123']")
6, karenanya
<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
4 dapat meluapkan semua yang diinginkan dan tidak akan menemukannya. Kondisi memeriksanya dan memicu fungsi ________11______8

Mengklik di suatu tempat di dalam nodal, ucapkan judulnya, akan menjadikan

$("button").closest("[data-id='123']")
6 sebagai simpul induk. Dalam hal ini, kondisinya tidak benar, meninggalkan modal dalam keadaan terbuka

Oh, dan tentang dukungan browser…

Seperti halnya API JavaScript "baru" yang keren, dukungan browser adalah sesuatu yang perlu dipertimbangkan. Kabar baiknya adalah bahwa

<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
_5 bukanlah hal baru dan didukung di semua browser utama selama beberapa waktu, dengan cakupan dukungan sebesar 94%. Saya akan mengatakan ini memenuhi syarat sebagai aman untuk digunakan dalam lingkungan produksi

Satu-satunya browser yang tidak menawarkan dukungan apa pun adalah Internet Explorer (semua versi). Jika Anda harus mendukung IE, Anda mungkin lebih baik menggunakan pendekatan jQuery


Seperti yang Anda lihat, ada beberapa kasus penggunaan yang cukup solid untuk

<div data-id="123">
  <article>
    <header>
      <h1>Some title</h1>
      <button>Click me</button>
    </header>
     <!-- .. -->
  </article>
</div>
5. Pustaka apa, seperti jQuery, yang dibuat relatif mudah bagi kami di masa lalu sekarang dapat digunakan secara native dengan JavaScript vanilla

Berkat dukungan browser yang bagus dan API yang mudah digunakan, saya sangat bergantung pada metode kecil ini di banyak aplikasi dan belum pernah kecewa.

Bagaimana Anda menggunakan terdekat?

Definisi dan Penggunaan . Metode terdekat () dimulai dari elemen itu sendiri, kemudian leluhur (orang tua, kakek nenek,. ) sampai ditemukan kecocokan. Metode terdekat() mengembalikan null() jika tidak ditemukan kecocokan. The closest() method searches up the DOM tree for elements which matches a specified CSS selector. The closest() method starts at the element itself, then the anchestors (parent, grandparent, ...) until a match is found. The closest() method returns null() if no match is found.

Apa perbedaan antara terdekat dan temukan di JavaScript?

Terdekat akan naik di elemen induk untuk menemukan kecocokan dengan pemilih. Find akan turun ke elemen turunan untuk menemukan kecocokan dengan pemilih . Keduanya pertama-tama akan menguji elemen awal sebelum melalui elemen induk/anak.

Bagaimana menemukan kelas terdekat di JavaScript?

Pendekatan 1. Gunakan pemilih Javascript untuk memilih elemen. Gunakan metode terdekat() untuk mendapatkan induk terdekat dengan kelas tertentu .

Bagaimana cara memilih elemen terdekat dalam JavaScript?

Metode terdekat() dimulai dengan targetElement dan melakukan perjalanan ke pohon DOM hingga menemukan elemen yang cocok dengan pemilih . Metode mengembalikan nol jika tidak ada elemen seperti itu.