Solucionado (ver solução)
Solucionado
(ver solução)
4
respostas

Erro ao adicionar classe

Quero o seguinte: Tenho uma lista de ícones, todas possuem a mesma classe que é "icon-visual", e classes específicas para cada item, como o "icon-financeiro" e o "icon-mensalidade". Quando clicar em um ícone, este vai receber uma classe, onde vai ser alterado a cor do background dele. Por exemplo, cliquei no "icon-financeiro", quero que ele ganhe a classe "icone-selecionado" ficando com o background roxo. Se em seguida eu clicar no outro, "icon-mensalidade", quero que ele receba a classe, e que a classe seja removida do ícone anterior.

Porém está me dando erro ao adicionar a classe: Uncaught TypeError: Cannot read property 'add' of undefined at HTMLDivElement.OutroRecurso (outros-recursos.js:29)

Tenho o seguinte JS:

window.onload = function() {
    var Icones = document.querySelectorAll(".icon-visual");
    for (var i=0; i<Icones.length; i++) {
        var Icon = Icones[i];

        Icon.addEventListener("click", OutroRecurso);
    }
}

function OutroRecurso(Icon) {
    Icon.classList.add("icone-selecionado"); 
}

E o HTML é este:

                <div class="plan-card-basic cards-flex">
                    <div class="plan-card-icons">
                        <div class="icon-financeiro icon-visual"> 
                            <i class="icofont-exchange"></i>
                        </div>
                        <div class="icon-mensalidade icon-visual">
                            <i class="icofont-money-bag"></i>
                        </div>
                        <div class="icon-usuarios icon-visual">
                            <i class="icofont-user-alt-7"></i>
                        </div>
                        <div class="icon-ind-movimento icon-visual">
                            <i class="icofont-chart-growth"></i>
                        </div>
                        <div class="icon-ind-luta icon-visual teste-icone">
                            <i class="icofont-chart-bar-graph"></i>
                        </div>
                    </div>
  • Se eu fizer o processo, selecionando apenas 1 classe de 1 ícone específico e adicionar a outra classe nele, o processo ocorre bem certinho.
  • Se eu fizer o processo como no código acima, e invés de adicionar classe, dar um console.log, também dá certo, sem erros.

Mas desta forma não está dando certo.

Alguém pode me ajudar?

4 respostas

Fala aí Graziela, tudo bem? O problema é que você não está mandando o ícone como parâmetro para sua segunda função: OutroRecurso.

Icon.addEventListener("click", function() {
    OutroRecurso(Icon);
});

Ou, você pode usar a referência this dentro da sua função:

function OutroRecurso() {
    this.classList.add("icone-selecionado"); 
}

As duas mudanças devem atender sua necessidade.

Espero ter ajudado.

Boa noite, Graziela! Como vai?

Seu código tem alguns problemas, mas o principal deles é que vc supôs que o ícone clicado seria passado como parâmetro para a função OutroRecurso() quando na realidade o que o JavaScript passa para essa função quando a invoca no momento do click é um objeto contendo as informações relativas ao evento que aconteceu!

Além disso, criar um addEventListener() para cada um dos ícones como foi feito no código não é boa prática! Há um padrão de projeto chamado delegate que resolve essa questão de uma forma mais simples com apenas um único addEventListener e sem a necessidade de fazer esse for! Ou seja, implementando esse padrão de projeto, de quebra o seu código fica mais simples! Dê uma olhada no código abaixo e diga o que vc acha!

Obs.: O código está todo comentado com as explicações necessárias!

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Delegate e troca de classes CSS</title>
    <style>
        .plan-card-basic {
            height: 200px;
            border: 2px solid #000;
        }
        .plan-card-icons {
            height: 170px;
            border: 2px solid #AA0;
        }
        .icon-visual {
            width: 25px;
            height: 25px;
            margin: 5px;
            background: #0AA;
        }
        .icon-selected {
            background: #F00;
        }
    </style>
</head>
<body>
    <div class="plan-card-basic cards-flex">
        <div class="plan-card-icons">
            <div class="icon-financeiro icon-visual"> 
                <i class="icofont-exchange"></i>
            </div>
            <div class="icon-mensalidade icon-visual">
                <i class="icofont-money-bag"></i>
            </div>
            <div class="icon-usuarios icon-visual">
                <i class="icofont-user-alt-7"></i>
            </div>
            <div class="icon-ind-movimento icon-visual">
                <i class="icofont-chart-growth"></i>
            </div>
            <div class="icon-ind-luta icon-visual teste-icone">
                <i class="icofont-chart-bar-graph"></i>
            </div>
        </div>
    </div>
    <script>
        // Recupero a div que envolve os ícones.
        const painelIcones = document.querySelector('.plan-card-icons');

        // Recupero um ícone qualquer, nesse caso, o primeiro a aparecer no HTML
        // que seja filho da div selecionada na linha anterior.
        let iconeSelecionado = painelIcones.querySelector('.icon-visual');

        // Delego à div selecionada, o evento de click.
        painelIcones.addEventListener('click', ativaClasse);

        function ativaClasse(evento) {
            console.log(evento); // Imprimo no console as informações do evento.

            // Pego o elemento alvo do click.
            const elementoClicado = evento.target;

            // Verifico se o elemento clicado tem a classe icon-visual,
            // ou seja, estou verificando se o elemento clicado é um ícone.
            if (elementoClicado.classList.contains('icon-visual')) {

                // Removo a classe especial icon-selected do icone selecionado previamente.
                iconeSelecionado.classList.remove('icon-selected');

                // Adiciono a classe especial icon-selected no novo icone selecionado.
                elementoClicado.classList.add('icon-selected');

                // Atualizo o valor da variável iconeSelecionado.
                iconeSelecionado = elementoClicado;
            } 
        }
    </script>
</body>
</html>

Qualquer coisa é só falar!

Grande abraço e bons estudos, minha aluna!

Bom dia Gabriel.

Não entendi o porque de fazer um IF para verificar se o que foi clicado é um ícone. Isso não acaba ocupando mais memória e sendo algo ruim para esta situação?

solução!

Boa tarde, Graziela! Como vai?

O if é necessário pq o addEventListener() foi adicionado ( delegado, e daí vem o nome do padrão delegate ) na div que tem a classe .plan-card-icons a qual envolve os ícones. Ou seja, quando eu clicar em qualquer parte dessa div o addEventListerner() será acionado! Dessa forma, eu preciso verificar se o clique está sendo exatamente nos ícones!

Sobre performance e impacto na memória, esse padrão que te mostrei é uma boa prática justamente pq ele otimiza todas essas questões! Veja que no seu código, se vc tiver 10 ícones terá consequentemente 10 addEventListener() em memória. Já o código que te mostrei terá sempre apenas 1 addEventListener() independente da quantidade de ícones!

Pegou a ideia? Qualquer coisa é só falar!

Grande abraço e bons estudos, minha aluna!

Quer mergulhar em tecnologia e aprendizagem?

Receba a newsletter que o nosso CEO escreve pessoalmente, com insights do mercado de trabalho, ciência e desenvolvimento de software