É o seu primeiro dia de trabalho como pessoa desenvolvedora Javascript.
E a sua primeira tarefa é fazer uma alteração em todos os cards de produto de uma página. Esses cards estão em formato de grid:
Durante seus estudos, você descobriu que o forEach executa uma função callback para cada item de uma array:
for
: paraeach
: cada
Essa explicação pode parecer muito ampla, mas é isso mesmo que o forEach é, amplo. Você pode usar ele para fazer muitas coisas, não existe uma só intenção.
Um erro muito comum é achar que ele serve apenas para alterar cada item da array. O forEach também pode usar cada um deles para fazer outro tipo de alteração.
const names = [ 'andre', 'marcus', 'mario' ];
names.forEach(function(name) {
console.log(name);
});
No exemplo acima, o forEach escreve cada nome na lista no console.
Depois de revisar o forEach, você pega os cards com document.getElementsByClassName('product-card')
, coloca em uma variável e usa o forEach. Malandramente, você usa um console para saber se tudo ocorreu bem:
const productCards = document.getElementsByClassName('product-card');
productCards.forEach(function(card) {
console.log(card);
});
Até que BOOM!!
Uncaught TypeError: document.getElementsByClassName(...).forEach is not a function
"O que há de errado com essa array?!", você pensa.
Antes de mais nada, isso não é uma array, é um HTMLCollection. Existem alguns tipos de listas iteráveis no Javascript além da array. E de todos eles, apenas array e nodelist possuem o método forEach.
Eu escrevi um post no blog sobre array, nodelist, HTMLCollection e outros iteráveis em Javascript. É importante ler para entender essa diferença.
Antes de prosseguir, saiba que esse post faz parte de uma série de 7 artigos sobre os principais métodos de array em Javascript. Vou postar os próximos na sequência aqui no blog.
Veja agora a solução desse caso.
O método forEach
O método forEach existe tanto em array, quanto em nodelist.
Se você leu o artigo sobre os iteráveis em Javascript, percebeu que a solução não é tão complicada:
O método
getElementsByClassName
retorna um HTMLCollectionJá o
querySelectorAll
retorna um nodelist
Então você precisa mudar a forma de pegar elementos da DOM:
const productCards = document.querySelectorAll('.product-card');
productCards.forEach(function(card) {
console.log(card);
});
Agora sim, nada de erros. Então você remove o console e começa a alteração de verdade. Não vou me aprofundar no tipo de alteração:
const productCards = document.querySelectorAll('.product-card');
productCards.forEach(function(card) {
fazerAlteracao(card);
});
Show de bola. Tudo perfeito para uma primeira manhã de trabalho.
Você vai para o almoço tranquilo, já que finalizou a sua primeira tarefa. Mas na volta, outra pauta surge no Jira.
O parâmetro index da função callback
O desafio agora é outro, mas ainda no grid de produtos.
Esse grid tem 5 cards de produto por linha. E agora a pessoa PO decidiu que o último produto de cada linha terá um estilo diferente.
Como aplicar esse estilo apenas no último produto de cada linha?
Para corrigir, você precisa lembrar que o forEach recebe uma função como parâmetro . Essa função se chama callback, um nome bonito para descrever uma função que você informa como argumento para outra função.
Essa callback pode receber um segundo parâmetro, ele geralmente se chama index
:
const productCards = document.querySelectorAll('.product-card');
productCards.forEach(function(card, index) {
// ...
});
Index é o índice daquele item dentro da array, e esse valor inicia em zero.
Por exemplo, se você executar o código abaixo:
const items = [ 'teclado', 'mouse', 'monitor', 'mesa' ];
items.forEach(function(item, index) {
console.log('Item: ' + item);
console.log('Index: ' + index);
console.log('---');
});
Receberá o seguinte resultado:
Item: teclado
Index: 0
---
Item: mouse
Index: 1
---
Item: monitor
Index: 2
---
Item: mesa
Index: 3
---
O índice será fundamental para criar um estilo diferente para alguns cards do grid.
Se cada item da lista tem um index, cada card de produto no grid também tem:
Os cards da primeira fileira têm o index 0, 5, 10...
Os cards da segunda fileira têm o index 1, 6, 11...
Os cards da terceira fileira têm o index 2, 7, 12...
Os cards da quarta fileira têm o index 3, 8, 13...
Os cards da quinta fileira têm o index 4, 9, 14...
O foco está nos índices 4, 9, 14... Agora, como alterar apenas eles? Após muito Google, Stackoverflow e lágrimas, você descobre o operador módulo (%) que retorna o resto da divisão. Exemplos:
3 % 2 = 1 (três dividido por dois dá 1 e sobra 1)
9 % 5 = 4 (nove dividido por cinco dá 1 e sobra 4)
12 % 5 = 2 (doze dividido por cinco dá 2 e sobra 2)
Após mais alguns testes, você descobre que se usar eles com módulo em 5, o resultado é sempre o mesmo, 4:
4 % 5 = 4 (quatro dividido por cinco dá 0 e sobra 4)
9 % 5 = 4 (nove dividido por cinco dá 1 e sobra 4)
14 % 5 = 4 (quatorze dividido por cinco dá 2 e sobra 4)
Não importa quanto dá, ou seja, o resultado, e sim quanto sobra no final da divisão.
O número 5 funciona aqui, porque ele é a quantidade de cards por linha.
Lembra como estava a sua função antes? Veja como ela fica agora:
const productCards = document.querySelectorAll('.product-card');
productCards.forEach(function(card, index) {
if (index % 5 === 4) {
// alterar o estilo ao adicionar uma classe
card.classList.add('alternate-style');
} else {
// não alterar o estilo
}
});
Tudo está perfeito... até que o requisito muda...
O parâmetro array da função callback
Agora tudo mudou.
Eles não querem mais alterar o estilo do último card de cada linha. E sim alterar o estilo da segunda metade dos cards. What?!
Sim, se o grid tiver 16 produtos, você deve alterar o estilo dos últimos 8. Ignore por enquanto casos de grids com número ímpar de cards.
Legal, e agora?
Além dos parâmetros item e index, a função callback também pode receber o terceiro e último parâmetro. Ele geralmente se chama array e retorna a própria lista que você está iterando.
No exemplo abaixo:
const items = [ 'teclado', 'mouse', 'monitor', 'mesa' ];
items.forEach(function(item, index, array) {
console.log('---');
console.log('item:', item);
console.log('index:', index);
console.log('array:', array);
});
O resultado é:
---
item: teclado
index: 0
array: (4) ['teclado', 'mouse', 'monitor', 'mesa']
---
item: mouse
index: 1
array: (4) ['teclado', 'mouse', 'monitor', 'mesa']
---
item: monitor
index: 2
array: (4) ['teclado', 'mouse', 'monitor', 'mesa']
---
item: mesa
index: 3
array: (4) ['teclado', 'mouse', 'monitor', 'mesa']
Se você voltar agora para o exemplo do grid, pode usar esse parâmetro a seu favor.
Se você acessar array.length
, vai receber a quantidade de itens da array. Se dividir por 2, estará mais perto ainda do resultado. Veja como fica:
const productCards = document.querySelectorAll('.product-card');
productCards.forEach(function(card, index, array) {
const half = (array.length - 1) / 2;
if (index > half) {
// alterar o estilo ao adicionar uma classe
card.classList.add('alternate-style');
} else {
// não alterar o estilo
}
});
Aqui você descobre qual é o número que representa a metade da quantidade de itens da array (half
). Depois verifica se o index atual é maior do que isso. Se sim, altera o estilo do card.
Apesar de a função callback poder receber 3 parâmetros, dificilmente você usará todos. Arrisco dizer que 90% das tarefas você faz apenas com o primeiro, o item
.
Após executar esse último forEach, você deseja pegar o valor dele e fazer alguma operação, porém...
O que o forEach retorna?
O forEach não retorna nada.
Ou melhor, toda função retorna alguma coisa. Mas quando você ouvir que a função não retorna nada, significa que ela retorna undefined
. E esse é o caso do forEach :
const list = [ 'qualquer', 'coisa', 'na', 'lista' ];
const result = list.forEach(function(item) {
// executa alguma coisa com o item
});
console.log(result); // undefined
Logo, você não precisa criar uma variável para receber o retorno dele, pois o retorno não varia.
Como falei no começo, o forEach executa uma função para cada item de uma array, e depois disso ele encerra aquela expressão. Não é possível ligar outros métodos no forEach.
Mas se você realmente precisa de um retorno, não deve usar o forEach, e sim o map. Map é um outro método famoso no Javascript, e será tema do próximo artigo da série sobre os principais métodos Javascript 😬
Callback
Não estou falando da callback do forEach haha
Caso você não saiba, Callback é como eu chamo o capítulo final dos posts. Porque agora é o momento de retomar o que você aprendeu. Então vamos lá.
O forEach é um método Javascript que executa uma função para cada item de uma array ou nodelist.
Ele não serve apenas para alterar cada item de uma lista.
Se você tentar usar esse método com outros tipos de lista, como um HTMLCollection, receberá um erro.
A função callback que você informa para o forEach pode receber três parâmetros. O primeiro é cada um dos itens da lista. O segundo é o índice do item. Ele inicia em zero e avança até o tamanho total da lista menos 1. E o terceiro é a própria array que você está iterando.
O forEach é um método que não retorna nada. Ou melhor dizendo, sempre retorna undefined
.
Os problemas que apresentei nesse post servem apenas para fins didáticos. Isso significa que o forEach não é a melhor solução para eles. Se você ainda está no início dos estudos, ignore isso e foque apenas em entender como o forEach funciona.
Esse e os próximos artigos sobre os principais métodos Javascript são muito especiais. Não só porque os projetos exigem o uso deles, mas porque tive uma certa dificuldade para aprender.
Aqui no blog busco explicar de uma forma que você não tenha as mesmas dificuldades que eu tive. Se ficou com alguma dúvida, não deixe de comentar para que eu possa melhorar as explicações cada vez mais.
Obrigado pelo seu tempo e sua leitura 😬.
Continue estudando: