Como iterar corretamente por meio de getElementsByClassName

100

Sou iniciante em Javascript.

Estou iniciando uma página da web através do window.onload, tenho que encontrar um monte de elementos por seu nome de classe ( slide) e redistribuí-los em nós diferentes com base em alguma lógica. Tenho uma função Distribute(element)que pega um elemento como entrada e faz a distribuição. Eu quero fazer algo assim (conforme descrito, por exemplo, aqui ou aqui ):

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

no entanto, isso não faz a mágica para mim, porque getElementsByClassNamena verdade não retorna array, mas um NodeList, que é ...

... esta é minha especulação ...

... sendo alterado dentro da função Distribute(a árvore DOM está sendo alterada dentro desta função, e a clonagem de certos nós acontece). For-eacha estrutura do loop também não ajuda.

Os slides variáveis ​​agem de forma realmente desdeterminista, através de cada iteração, ele muda o comprimento e a ordem dos elementos descontroladamente.

Qual é a maneira correta de iterar por meio de NodeList no meu caso? Eu estava pensando em preencher algum array temporário, mas não tenho certeza de como fazer isso ...

EDITAR:

fato importante que esqueci de mencionar é que pode haver um slide dentro do outro, isso é realmente o que muda a slidesvariável como acabei de descobrir graças ao usuário Alohci .

A solução para mim foi clonar cada elemento em uma matriz primeiro e passar a matriz ono-por-um para Distribute()depois.

Kupto
fonte
3
Esta é realmente a maneira de fazer isso, então você deve estar bagunçando alguma coisa!
adeneo
a Distribute()função é muito longa e complexa para ser copiada aqui, mas tenho certeza de que estou alterando a estrutura do DOM dentro, também estou duplicando (clonando) elementos lá. Quando eu depuro, posso ver as slidesmudanças de variáveis sempre que é passado para dentro.
Kupto,
Não muda a menos que você realmente mude em algum lugar.
adeneo,
5
Eu acredito que getElementsByClassName()retorna um live nodeList, então como os elementos com essa classe são adicionados, o comprimento do nodeListover no qual você está iterando as alterações.
David diz para restabelecer Monica em
2
O loop @ Kupto ao contrário geralmente resolve esse tipo de problema, em que a função Distribuir remove ou move o elemento de forma que ele não corresponda mais à chamada getElementsByClassName, pelo motivo que David Thomas fornece.
Alohci,

Respostas:

130

De acordo com o MDN, a maneira de recuperar um item de um NodeListé:

nodeItem = nodeList.item(index)

Portanto:

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

Eu não tentei fazer isso sozinho (o forloop normal sempre funcionou para mim), mas tente .

Albert Xing
fonte
Esta é a solução certa, a menos que você tente pesquisar e alterar os elementos que têm a mesma classe e estão uns dentro dos outros. Expliquei minha solução alternativa na edição para minha pergunta.
Kupto
Claro, não levei isso em consideração.
Albert Xing
Por que é assim, se posso perguntar? Por que ele não é implementado para que você possa iterar nos nós dessa forma for(var el in document.getElementsByClassName("foo")){}?
Nearoo
3
for ... ofpermite que você itere em NodeList agora como em for (slide of slides) Distribute(slide). O suporte do navegador é irregular, mas se você estiver transpilando, for ... ofserá convertido, mas NodeList.forEachnão será.
Mr5o1,
67

Se você usar o novo querySelectorAll, poderá chamar forEach diretamente.

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

Conforme o comentário abaixo. nodeLists não tem uma função forEach.

Se usar isso com o babel, você pode adicionar Array.frome converter listas de não-nós em um array forEach. Array.fromnão funciona nativamente nos navegadores abaixo e incluindo o IE 11.

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

Em nosso encontro ontem à noite, descobri outra maneira de lidar com listas de nós, sem forEach

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

Suporte do navegador para [...]

Mostrando como lista de nós

Mostrando como lista de nós

Mostrando como matriz

Mostrando como matriz

styks
fonte
4
Pegadinha nisso é que nodeLists não tem uma função forEach neles em cada navegador. Se você estiver disposto a mexer com protótipos, é simples o suficiente de fazer:if ( !NodeList.prototype.forEach ) {NodeList.prototype.forEach = Array.prototype.forEach;}
joshcanhelp
Solução elegante se eu combinar sua resposta com o comentário de @joshcanhelp. Obrigado :) Claro que isso só levará a uma vantagem de linha com vários loops.
yarwest
1
Você deve evitar isso porque pode não funcionar em todos os navegadores. Aqui está uma solução alternativa simples que eu uso e parece funcionar perfeitamente em qualquer lugar: css-tricks.com/snippets/javascript/…
tixastronauta
Eu acho que você quis dizer[...document.getElementsByClassName('.edit')].forEach(function(button) {
wp-overwatch.com
@ wp-overwatch.com o ponto não é necessário no nome da classe. A versão correta deve ser:[...document.getElementsByClassName('edit')].forEach(function(button) {
MXT
11

Você sempre pode usar métodos de array:

var slides = getElementsByClassName("slide");
Array.prototype.forEach.call(slides, function(slide, index) {
    Distribute(slides.item(index));
});
Andrew
fonte
muito boa e linda resposta, muito obrigado!
Olga Farber
O que é Distribuir?
lesolorzanov
7

Segui a recomendação de Alohci de fazer um loop reverso porque é um show nodeList. Aqui está o que fiz por quem está curioso ...

  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
fonte
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
fonte
0

Tive um problema semelhante com a iteração e caí aqui. Talvez outra pessoa também esteja cometendo o mesmo erro que eu.

No meu caso, o seletor não era o problema. O problema é que eu baguncei o código javascript: eu tinha um loop e um subloop. O subloop também estava usando icomo um contador, em vez de j, então, como o subloop estava substituindo o valor de ido loop principal, este nunca chegou à segunda iteração.

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
fonte