Cara terbaik untuk mendapatkan simpul anak

233

Saya bertanya-tanya, JavaScript menawarkan berbagai metode untuk mendapatkan elemen anak pertama dari elemen apa pun, tetapi mana yang terbaik? Maksud saya, sebagian besar browser yang kompatibel, tercepat, terlengkap, dan dapat diprediksi dalam hal perilaku. Daftar metode / properti yang saya gunakan sebagai alias:

var elem = document.getElementById('container');
var child = elem.children[0];
var child = elem.firstElementChild; // == children[0]

Ini berfungsi untuk kedua kasus:

var child = elem.childNodes[0]; // or childNodes[1], see below

Itu dalam hal bentuk, atau <div>iterasi. Jika saya mungkin menemukan elemen teks:

var child = elem.childNodes; // treat as NodeList
var child = elem.firstChild;

Sejauh yang saya bisa, firstChildgunakan NodeList dari childNodes, dan firstElementChildgunakan children. Saya mendasarkan asumsi ini pada referensi MDN:

childNodeadalah referensi ke elemen anak pertama dari simpul elemen, atau nulljika tidak ada.

Saya menduga bahwa, dalam hal kecepatan, perbedaannya, jika ada, akan hampir tidak ada, karena firstElementChildsecara efektif referensi children[0], dan childrenobjek sudah ada dalam memori pula.

Apa yang melemparku, adalah childNodesobjeknya. Saya telah menggunakannya untuk melihat formulir, di elemen tabel. Sementara childrendaftar semua elemen bentuk, childNodesjuga tampaknya termasuk spasi dari kode HTML:

console.log(elem.childNodes[0]);
console.log(elem.firstChild);

Keduanya log <TextNode textContent="\n ">

console.log(elem.childNodes[1]);
console.log(elem.children[0]);
console.log(elem.firstElementChild);

Semua log <input type="text"... >. Bagaimana bisa? Saya mengerti bahwa satu objek akan memungkinkan saya untuk bekerja dengan kode HTML "mentah", sementara yang lain menempel pada DOM, tetapi childNodeselemen tampaknya bekerja di kedua level.

Untuk kembali ke pertanyaan awal saya, tebakan saya adalah: jika saya ingin objek yang paling komprehensif, childNodesadalah cara untuk pergi, tetapi karena kelengkapannya, itu mungkin bukan yang paling dapat diprediksi dalam hal mengembalikan elemen yang saya inginkan / harapkan pada saat tertentu. Dukungan lintas-browser mungkin juga terbukti menjadi tantangan dalam kasus itu, meskipun saya bisa saja salah.

Adakah yang bisa menjelaskan perbedaan antara benda-benda yang ada? Jika ada perbedaan kecepatan, betapapun bisa diabaikan, saya juga ingin tahu. Jika saya melihat ini semua salah, jangan ragu untuk mendidik saya.


PS: Tolong, saya suka JavaScript, jadi ya, saya mau berurusan dengan hal semacam ini. Jawaban seperti "penawaran jQuery dengan ini untuk Anda" bukan yang saya cari, karenanya tidak menandai.

Elias Van Ootegem
sumber
50
>> tolong, tolong, saya suka JavaScript, jadi ya, saya ingin berurusan dengan hal semacam ini. jawaban seperti 'penawaran jQuery dengan ini untuk Anda' bukan yang saya cari << upvotes untuk ini
david.barkhuizen

Jawaban:

211

Sepertinya Anda terlalu memikirkannya. Anda telah mengamati perbedaan antara childNodesdan children, yang childNodesberisi semua simpul, termasuk simpul teks yang seluruhnya terdiri dari spasi, sedangkan childrenkumpulan hanya simpul anak yang merupakan elemen. Hanya itu yang ada di sana.

Tidak ada yang tak terduga tentang koleksi mana pun, meskipun ada beberapa masalah yang harus diperhatikan:

  • IE <= 8 tidak menyertakan simpul teks khusus spasi putih di dalamnya childNodessementara browser lain melakukannya
  • IE <= 8 termasuk node komentar di dalam childrensementara browser lain hanya memiliki elemen

children, firstElementChilddan teman hanyalah kenyamanan, menghadirkan tampilan DOM yang disaring terbatas pada elemen adil.

Tim Down
sumber
Berpikir sama banyak, meskipun satu hal masih mengganggu saya: textnode spasi putih yang diambil childNodes sebenarnya hanya baris baru dan beberapa tab dalam kode. Apakah ini karena DOCTYPE XHTML? dapatkah ini diselesaikan dengan melampirkan spasi putih ke kode struct dalam tag?
Elias Van Ootegem
@EliasVanOotegem: Itu tidak terkait dengan doctype. Node teks spasi putih selalu disertakan dalam DOM (kecuali di IE <9) untuk HTML dan XHTML, di mana pun mereka muncul di sumber. Secara umum itu adalah hal yang baik, meskipun dapat menangkap Anda jika Anda tidak siap untuk itu.
Tim Down
Maksud saya jika itu akan membantu untuk menulis markup saya seperti ini <td\n\t>content</td>. Seperti yang mungkin Anda lakukan dengan XML, untuk menghindari spasi kosong dianggap sebagai bagian dari data (atau DOM, dalam hal ini). Mengapa spasi putih di dalam tag dimasukkan dalam DOM?
Elias Van Ootegem
@EliasVanOotegem: Oh, begitu. Spasi putih setelah nama tag di dalam tag diabaikan, sehingga Anda bisa melakukan hal semacam itu. Saya telah melihat bahwa teknik yang digunakan dalam tata letak yang tepat untuk mencegah browser menambahkan celah kecil untuk ruang putih di antara beberapa jenis elemen.
Tim Down
2
@ Christophe: Ah, titik adil, terima kasih. Saya akan menambahkan catatan untuk jawaban saya. Mengingat bahwa childrenberasal dari IE 4 lebih dari satu dekade sebelum menjadi standar atau diadopsi oleh browser lain, Anda bisa berpendapat bahwa IE <= 8 benar dan browser lain salah.
Tim Down
23

firstElementChild mungkin tidak tersedia di IE <9 (hanya firstChild)

pada IE <9 firstChild adalah firstElementChild karena MS DOM (IE <9) tidak menyimpan node teks kosong. Tetapi jika Anda melakukannya di browser lain, mereka akan mengembalikan node teks kosong ...

solusi saya

child=(elem.firstElementChild||elem.firstChild)

ini akan memberikan anak pertama bahkan pada IE <9

neu-rah
sumber
Jika elem.firstChildbukan elemen node, hasilnya akan berbeda di IE lama, karena elem.firstElementChildselalu elemen node.
duri
Maaf, tapi saya tahu cara mendapatkan anak pertama di semua browser utama. Yang ingin saya ketahui adalah bagaimana berbagai objek disusun, untuk mendapatkan pemahaman yang lebih baik tentang persamaan dan perbedaan di antara mereka. Saya akan mengedit pertanyaan saya nanti untuk membuatnya lebih jelas, maaf atas
campur-baurnya
1
firstElementChildjuga tidak selalu aman di browser non-IE: Firefox hanya mulai mendukungnya dalam versi 3.5.
Tim Down
ini bekerja untuk chrome, IE9 dan IE8 karena jika mereka memiliki firstElementChild maka pemeriksaan lainnya tidak dijalankan, jika tidak (IE lama) kita memiliki firstChild, dan itu adalah elemen node untuk browser yang tidak memiliki firstElementChild (IE <9 ) ... masih saya bertanya-tanya tentang FF
neu-rah
1
Tim benar, ketika saya mencari sesuatu di benda-benda ini di Google, situs yang bagus untuk memeriksa sesekali
Elias Van Ootegem
20

Cara lintas browser yang harus dilakukan adalah menggunakan childNodesuntuk mendapatkan NodeList, lalu membuat array semua node dengan nodeType ELEMENT_NODE .

/**
 * Return direct children elements.
 *
 * @param {HTMLElement}
 * @return {Array}
 */
function elementChildren (element) {
    var childNodes = element.childNodes,
        children = [],
        i = childNodes.length;

    while (i--) {
        if (childNodes[i].nodeType == 1) {
            children.unshift(childNodes[i]);
        }
    }

    return children;
}

http://jsfiddle.net/s4kxnahu/

Ini sangat mudah jika Anda menggunakan perpustakaan utilitas seperti lodash :

/**
 * Return direct children elements.
 *
 * @param {HTMLElement}
 * @return {Array}
 */
function elementChildren (element) {
    return _.where(element.childNodes, {nodeType: 1});
}

Masa depan:

Anda dapat menggunakan querySelectorAlldalam kombinasi dengan :scopepseudo-class (cocok dengan elemen yang merupakan titik referensi pemilih):

parentElement.querySelectorAll(':scope > *');

Pada saat penulisan ini :scopedidukung di Chrome, Firefox dan Safari.

Gajus
sumber
: ruang lingkup dihapus dari spec . Haruskah kita menghapus bagian Masa Depan ?
Thomas Marti
4

Hanya untuk menambah jawaban lain, masih ada perbedaan yang patut diperhatikan di sini, khususnya ketika berhadapan dengan <svg>elemen.

Saya telah menggunakan keduanya .childNodesdan .childrenlebih suka bekerja dengan yang HTMLCollectiondisampaikan oleh .childrenpengambil.

Namun hari ini, saya mengalami masalah dengan IE / Edge gagal saat digunakan .childrenpada Windows Vista <svg>. Meskipun .childrendidukung di IE pada elemen HTML dasar, itu tidak didukung pada dokumen / fragmen dokumen, atau elemen SVG .

Bagi saya, saya dapat dengan mudah mengambil elemen yang dibutuhkan melalui .childNodes[n]karena saya tidak memiliki simpul teks yang asing untuk dikhawatirkan. Anda mungkin dapat melakukan hal yang sama, tetapi seperti yang disebutkan di tempat lain di atas, jangan lupa bahwa Anda mungkin mengalami elemen yang tidak terduga.

Semoga ini bisa membantu seseorang menggaruk-garuk kepala mencoba mencari tahu mengapa .childrenbekerja di tempat lain di js mereka pada IE modern dan gagal pada dokumen atau elemen SVG.

slothluvchunk
sumber
3

Hei teman-teman jangan biarkan ruang putih menipu Anda. coba saja ini di browser konsol. gunakan javascript asli. Berikut adalah dan contoh dengan dua set 'ul' dengan kelas yang sama. Anda tidak perlu memiliki daftar 'ul' Anda semua dalam satu baris untuk menghindari ruang putih cukup gunakan jumlah array Anda untuk melompati ruang putih.

Cara mendapatkan ruang di sekitar putih dengan querySelector()kemudian childNodes[]js Link biola: https://jsfiddle.net/aparadise/56njekdo/

var y = document.querySelector('.list');
var myNode = y.childNodes[11].style.backgroundColor='red';

<ul class="list">
    <li>8</li>
    <li>9</li>
    <li>100</li>
</ul>

<ul class="list">
    <li>ABC</li>
    <li>DEF</li>
    <li>XYZ</li>
</ul>
andre paradise
sumber
Bukan pilihan saya, tetapi saya pikir seseorang memilih karena: a) Pertanyaan ini berusia 4 tahun, document.querySelectortidak didukung dengan baik saat itu. b) Pertanyaannya pada dasarnya adalah menanyakan apa perbedaan antara childrendan secara childNodes internal , mana yang lebih dapat diandalkan, kapan harus memilih satu di antara yang lain. dan c) Karena, seperti ditunjukkan dalam pertanyaan: childNodes tidak termasuk node spasi putih, childrentidak ...
Elias Van Ootegem