Cara melakukan iterasi dengan benar melalui getElementsByClassName

101

Saya pemula Javascript.

Saya memulai halaman web melalui window.onload, saya harus menemukan banyak elemen dengan nama kelas mereka ( slide) dan mendistribusikannya ke node yang berbeda berdasarkan beberapa logika. Saya memiliki fungsi Distribute(element)yang mengambil elemen sebagai input dan melakukan distribusi. Saya ingin melakukan sesuatu seperti ini (seperti diuraikan misalnya di sini atau di sini ):

var slides = getElementsByClassName("slide");
for(var i = 0; i < slides.length; i++)
{
   Distribute(slides[i]);
}

Namun ini tidak melakukan keajaiban bagi saya, karena getElementsByClassNamesebenarnya tidak mengembalikan array, tapi a NodeList, yang ...

... ini spekulasi saya ...

... diubah di dalam fungsi Distribute(pohon DOM sedang diubah di dalam fungsi ini, dan kloning node tertentu terjadi). For-eachstruktur lingkaran juga tidak membantu.

Variabel slide bertindak benar-benar tidak deterministik, melalui setiap iterasi itu mengubah panjang dan urutan elemennya secara liar.

Apa cara yang benar untuk melakukan iterasi melalui NodeList dalam kasus saya? Saya sedang berpikir untuk mengisi beberapa larik sementara, tetapi tidak yakin bagaimana melakukan itu ...

EDIT:

Fakta penting yang lupa saya sebutkan adalah bahwa mungkin ada satu slide di dalam yang lain, ini sebenarnya yang mengubah slidesvariabel seperti yang baru saja saya ketahui berkat pengguna Alohci .

Solusi bagi saya adalah mengkloning setiap elemen ke dalam array terlebih dahulu dan meneruskan array satu per satu ke Distribute()setelahnya.

Kupto
sumber
3
Ini sebenarnya cara untuk melakukannya, jadi Anda pasti mengacaukan sesuatu yang lain!
adeneo
yang Distribute()berfungsi untuk panjang dan kompleks untuk disalin di sini, tapi saya yakin bahwa saya mengubah dalam struktur DOM, saya juga duplikasi (kloning) unsur sana. Ketika saya men-debugnya, saya bisa melihat variabel slidesberubah setiap kali dilewatkan di dalam.
Kupto
Itu tidak berubah kecuali Anda benar-benar mengubahnya di suatu tempat.
adeneo
5
Saya percaya itu getElementsByClassName()mengembalikan siaran langsung nodeList, sehingga elemen dengan kelas itu ditambahkan panjang di nodeListmana Anda mengulangi perubahan.
David mengatakan mengembalikan Monica
2
@ Kupto- looping secara terbalik sering memecahkan masalah semacam ini, di mana fungsi Distribute menghapus atau memindahkan elemen sedemikian rupa sehingga tidak lagi cocok dengan panggilan getElementsByClassName, karena alasan yang diberikan oleh David Thomas.
Alohci

Jawaban:

130

Menurut MDN, cara mengambil item dari a NodeListadalah:

nodeItem = nodeList.item(index)

Jadi:

var slides = document.getElementsByClassName("slide");
for (var i = 0; i < slides.length; i++) {
   Distribute(slides.item(i));
}

Saya belum mencoba ini sendiri ( forloop normal selalu berhasil untuk saya), tetapi cobalah.

Albert Xing
sumber
Ini adalah solusi yang tepat, kecuali jika Anda mencoba mencari dan mengubah elemen yang memiliki kelas yang sama dan berada di dalam satu sama lain. Saya menjelaskan solusi saya di edit untuk pertanyaan saya.
Kupto
Tentu, tidak memperhitungkannya.
Albert Xing
Mengapa demikian, jika saya boleh bertanya? Mengapa ini tidak diimplementasikan sehingga Anda dapat melakukan iterasi pada node seperti ini for(var el in document.getElementsByClassName("foo")){}?
Nearoo
3
for ... ofmemungkinkan Anda untuk beralih melalui NodeList sekarang seperti di for (slide of slides) Distribute(slide). Dukungan browser tidak lengkap, tetapi jika Anda transpiling maka for ... ofakan dikonversi, tetapi NodeList.forEachtidak.
Mr5o1
68

Jika Anda menggunakan querySelectorAll baru, Anda dapat memanggil forEach secara langsung.

document.querySelectorAll('.edit').forEach(function(button) {
    // Now do something with my button
});

Sesuai komentar di bawah. nodeLists tidak memiliki fungsi forEach.

Jika menggunakan ini dengan babel Anda dapat menambahkan Array.fromdan itu akan mengubah daftar non node menjadi array forEach. Array.fromtidak berfungsi seperti aslinya di browser di bawah dan termasuk IE 11.

Array.from(document.querySelectorAll('.edit')).forEach(function(button) {
    // Now do something with my button
});

Pada pertemuan kami tadi malam, saya menemukan cara lain untuk menangani daftar node yang tidak memiliki forEach

[...document.querySelectorAll('.edit')].forEach(function(button) {
    // Now do something with my button
});

Dukungan Browser untuk [...]

Ditampilkan sebagai Daftar Node

Ditampilkan sebagai Daftar Node

Ditampilkan sebagai Array

Ditampilkan sebagai Array

styks
sumber
4
Gotcha di sini adalah bahwa nodeLists tidak memiliki fungsi forEach pada mereka di setiap browser. Jika Anda ingin bermain-main dengan prototipe, itu cukup sederhana untuk dilakukan:if ( !NodeList.prototype.forEach ) {NodeList.prototype.forEach = Array.prototype.forEach;}
joshcanhelp
Solusi elegan jika saya menggabungkan jawaban Anda dengan komentar dari @joshcanhelp. Terima kasih :) Tentu saja ini hanya akan menghasilkan keuntungan baris dengan banyak loop.
yarwest
1
Anda harus menghindari ini karena mungkin tidak berfungsi di semua browser. Berikut adalah solusi sederhana yang saya gunakan dan tampaknya bekerja dengan sempurna di mana-mana: css-tricks.com/snippets/javascript/…
tixastronauta
Saya pikir yang Anda maksud[...document.getElementsByClassName('.edit')].forEach(function(button) {
wp-overwatch.com
@ wp-overwatch.com titik tidak diperlukan dalam nama kelas. Versi yang benar harus:[...document.getElementsByClassName('edit')].forEach(function(button) {
MXT
11

Anda selalu bisa menggunakan metode array:

var slides = getElementsByClassName("slide");
Array.prototype.forEach.call(slides, function(slide, index) {
    Distribute(slides.item(index));
});
Andrew
sumber
jawaban yang sangat bagus dan indah, terima kasih banyak!
Olga Farber
1
Apa itu Distribusikan?
lesolorzanov
7

Saya mengikuti rekomendasi Alohci untuk melakukan perulangan secara terbalik karena ini live nodeList. Inilah yang saya lakukan untuk mereka yang penasaran ...

  var activeObjects = documents.getElementsByClassName('active'); // a live nodeList

  //Use a reverse-loop because the array is an active NodeList
  while(activeObjects.length > 0) {
    var lastElem = activePaths[activePaths.length-1]; //select the last element

    //Remove the 'active' class from the element.  
    //This will automatically update the nodeList's length too.
    var className = lastElem.getAttribute('class').replace('active','');
    lastElem.setAttribute('class', className);
  }
ayjay
sumber
1
 <!--something like this--> 
<html>
<body>



<!-- i've used for loop...this pointer takes current element to apply a 
 particular change on it ...other elements take change by else condition 
-->  


<div class="classname" onclick="myFunction(this);">first</div>  
<div class="classname" onclick="myFunction(this);">second</div>


<script>
function myFunction(p) {
 var x = document.getElementsByClassName("classname");
 var i;
 for (i = 0; i < x.length; i++) {
    if(x[i] == p)
    {
x[i].style.background="blue";
    }
    else{
x[i].style.background="red";
    }
}
}


</script>
<!--this script will only work for a class with onclick event but if u want 
to use all class of same name then u can use querySelectorAll() ...-->




var variable_name=document.querySelectorAll('.classname');
for(var i=0;i<variable_name.length;i++){
variable_name[i].(--your option--);
}



 <!--if u like to divide it on some logic apply it inside this for loop 
 using your nodelist-->

</body>
</html>
Kushal Desai
sumber
0

Saya memiliki masalah serupa dengan iterasi dan saya mendarat di sini. Mungkin orang lain juga melakukan kesalahan yang sama dengan yang saya lakukan.

Dalam kasus saya, pemilih bukanlah masalah sama sekali. Masalahnya adalah saya telah mengacaukan kode javascript: Saya memiliki loop dan subloop. Subloop juga digunakan isebagai penghitung, bukan j, jadi karena subloop menimpa nilai dari iloop utama, yang ini tidak pernah sampai ke iterasi kedua.

var dayContainers = document.getElementsByClassName('day-container');
for(var i = 0; i < dayContainers.length; i++) { //loop of length = 2
        var thisDayDiv = dayContainers[i];
        // do whatever

        var inputs = thisDayDiv.getElementsByTagName('input');

        for(var j = 0; j < inputs.length; j++) { //loop of length = 4
            var thisInput = inputs[j];
            // do whatever

        };

    };
J0ANMM
sumber