Crie um menu de navegação com um indicador ativo animado (JavaScript)

Em um tutorial anterior, discutimos como construir um deslocando o efeito de foco do sublinhado. Hoje, aprenderemos como criar outro efeito sofisticado de menu: cada vez que clicarmos ou passarmos o mouse sobre um item (a escolha é sua!) haverá um elemento mágico em movimento que acompanhará o item ativo.

Durante esta jornada, também abordaremos uma técnica útil para atualizar os estilos de pseudoelementos CSS por meio do JavaScript com a ajuda das variáveis ​​CSS.

1. Comece com a marcação HTML

Para desenvolver este componente, vamos definir um nav elemento que conterá o menu agrupado dentro de um container. Por padrão, daremos a active class ao primeiro item da lista, mas podemos igualmente atribuir esta classe a qualquer um deles.

Aqui está a estrutura necessária:

1
 class="container">
2
  
3
     class="menu">
4
       class="active">
5
         href="">Home
6
      
7
      
  • 8
             href="">Information About Us
    
    9
          
    
    10
          
  • 11
             href="">Clients
    
    12
          
    
    13
          
  • 14
             href="">Contact
    
    15
          
    
    16
        
    
    17
      
    
    18
    
    

    “Informações sobre nós” é um rótulo estranho para um item de menu, mas demonstra a mudança de largura do efeito.

    2. Adicione o CSS

    Felizmente, só precisaremos de alguns estilos.

    Como dissemos, o menu ficará dentro de um container com largura máxima de 1000px.

    Para criar o elemento com o fundo em movimento que atuará como um indicador de menu ativo, não usaremos nenhum elemento HTML extra. Em vez disso, definiremos o ::before pseudo-elemento do menu e, em seguida, atualize seu transform e width valores dinamicamente por meio de JavaScript.

    O elemento móvelO elemento móvelO elemento móvel
    Em telas menores que 801px, ocultaremos o marcador em movimento e usaremos apenas alguns estilos semelhantes para indicar o item de menu ativo.

    Aqui estão os estilos de menu que mais nos interessam:

    1
    /*CUSTOM VATIABLES HERE*/
    
    2
    
    
    3
    .menu {
    
    4
      list-style: none;
    
    5
      position: relative;
    
    6
      display: inline-flex;
    
    7
      background: var(--pink);
    
    8
      padding: 10px;
    
    9
      border-radius: 15px;
    
    10
      box-shadow: rgba(17, 12, 46, 0.15) 0px 48px 100px 0px;
    
    11
    }
    
    12
    
    
    13
    .menu::before {
    
    14
      content: "";
    
    15
      position: absolute;
    
    16
      top: 10px;
    
    17
      left: 0;
    
    18
      transform: translateX(var(--transformJS));
    
    19
      width: var(--widthJS);
    
    20
      height: calc(100% - 20px);
    
    21
      border-radius: var(--active-link-border-radius);
    
    22
      background: var(--light-pink);
    
    23
      box-shadow: var(--active-link-box-shadow);
    
    24
      transition: all 0.3s linear;
    
    25
    }
    
    26
    
    
    27
    .menu li a {
    
    28
      display: inline-block;
    
    29
      position: relative;
    
    30
      padding: 10px 20px;
    
    31
      font-size: 20px;
    
    32
      font-weight: 500;
    
    33
      z-index: 1;
    
    34
    }
    
    35
    
    
    36
    .menu li:not(:last-child) {
    
    37
      margin-right: 20px;
    
    38
    }
    
    39
    
    
    40
    @media (max-width: 800px) {
    
    41
      .menu,
    
    42
      .menu li {
    
    43
        display: inline-block;
    
    44
      }
    
    45
    
    
    46
      .menu li.active a {
    
    47
        background: var(--light-pink);
    
    48
        border-radius: var(--active-link-border-radius);
    
    49
        box-shadow: var(--active-link-box-shadow);
    
    50
      }
    
    51
    
    
    52
      .menu::before {
    
    53
        display: none;
    
    54
      }
    
    55
    }
    

    3. Aplique o JavaScript

    Agora a parte interessante.

    vamos especificar o doCalculations() função que receberá como parâmetro o item ativo e fará estas coisas:

    • Calcule sua largura e deslocamento à esquerda em relação à lista pai.
    • Atualize de acordo com o transformJS e widthJS Variáveis ​​CSS que, por sua vez, definirão o transform e width valores do cardápio ::before pseudo-elemento.

    Esta função será executada nos seguintes casos:

    • Quando o DOM estiver pronto. Nesse caso, o item ativo será aquele com o active aula. Por padrão, este será o primeiro.
    • Cada vez que clicamos ou passamos o mouse sobre um link de menu.
    • À medida que redimensionamos a janela do navegador. Isso é importante porque lembre-se de que em telas menores seguimos uma abordagem diferente.

    Aqui está o código JavaScript necessário para a animação ao clicar:

    1
    const menu = document.querySelector(".menu");
    
    2
    const menuLinks = menu.querySelectorAll("a");
    
    3
    const menuLinkActive = menu.querySelector("li.active");
    
    4
    const activeClass = "active";
    
    5
    
    
    6
    doCalculations(menuLinkActive);
    
    7
    
    
    8
    for (const menuLink of menuLinks) {
    
    9
      menuLink.addEventListener("click", function (e) {
    
    10
        e.preventDefault();
    
    11
        menu.querySelector("li.active").classList.remove(activeClass);
    
    12
        menuLink.parentElement.classList.add(activeClass);
    
    13
        doCalculations(menuLink);
    
    14
      });
    
    15
    }
    
    16
    
    
    17
    function doCalculations(link) {
    
    18
      menu.style.setProperty("--transformJS", `${link.offsetLeft}px`);
    
    19
      menu.style.setProperty("--widthJS", `${link.offsetWidth}px`);
    
    20
    }
    
    21
    
    
    22
    window.addEventListener("resize", function() {
    
    23
      const menuLinkActive = menu.querySelector("li.active");
    
    24
      doCalculations(menuLinkActive);
    
    25
    });
    

    Para a animação ao passar o mouse, substituiremos o click evento com o mouseenter um assim:

    1
    ...
    
    2
    
    
    3
    menuLink.addEventListener("mouseenter", function () {
    
    4
      document.querySelector(".menu li.active").classList.remove(activeClass);
    
    5
      menuLink.parentElement.classList.add(activeClass);
    
    6
      doCalculations(menuLink);
    
    7
    });
    

    Conclusão

    Feito! De forma rápida, conseguimos desenvolver uma animação de menu de navegação prática que pode ocorrer tanto ao clicar quanto ao passar o mouse. Uma implementação mais avançada dele coloca menus suspensos em jogo e cria uma navegação de acompanhamento como no site do Stripe.

    Além da animação em si, outra coisa a ter em mente é a forma como usamos variáveis ​​CSS para atualizar os estilos de pseudo-elementos. Esta é uma ótima fonte de conhecimento nos casos em que você tem dores de cabeça ao manipular pseudo-elementos por meio do JavaScript.

    Mais uma vez, vamos relembrar nossas demos.

    A primeira demonstração:

    E o segundo:

    Como sempre, muito obrigado pela leitura!

    Deixe uma resposta