No tutorial de hoje, vamos usar um pouco de CSS e JavaScript para criar um efeito de menu flutuante sofisticado. Não é um resultado final complicado, mas construí-lo será uma ótima oportunidade para praticar nossas habilidades de front-end.
Sem mais introduções, vamos conferir o que vamos construir:
A marcação
Começamos com algumas marcações bem básicas; uma nav elemento que contém o menu e um vazio span elemento:
O CSS
Com a marcação pronta, a seguir especificamos alguns estilos básicos para os elementos relacionados:
.mynav ul {
display: flex;
justify-content: center;
flex-wrap: wrap;
list-style-type: none;
padding: 0;
}
.mynav li:not(:last-child) {
margin-right: 20px;
}
.mynav a {
display: block;
font-size: 20px;
color: black;
text-decoration: none;
padding: 7px 15px;
}
.target {
position: absolute;
border-bottom: 4px solid transparent;
z-index: -1;
transform: translateX(-60px);
}
.mynav a,
.target {
transition: all .35s ease-in-out;
}
Observe que o span elemento (.target) está absolutamente posicionado. Como veremos em breve, usaremos JavaScript para determinar sua posição exata. Além disso, deve aparecer atras do os links do menu, por isso damos uma nota negativa z-index.
-



A propriedade z-index em CSS parece bastante simples, mas há muito o que descobrir sob a superfície se você realmente quiser entender como ela funciona. Nisso…
O JavaScript
Neste ponto, vamos focar nossa atenção no JavaScript necessário. Para começar, temos como alvo os elementos desejados. Também definimos um array de cores que usaremos mais tarde.
const target = document.querySelector(".target");
const links = document.querySelectorAll(".mynav a");
const colors = ["deepskyblue", "orange", "firebrick", "gold", "magenta", "black", "darkblue"];
Eventos
A seguir, ouvimos o click e mouseenter eventos dos links do menu.
Quando o click evento acontecer, impedimos que a página seja recarregada. Claro, isso funciona no nosso caso porque todos os links têm um vazio href atributo. Em um projeto real, no entanto, cada um dos links do menu provavelmente abriria uma página diferente.
O mais importante é que assim que o mouseenter incêndios de eventos, o mouseenterFunc função de retorno de chamada é executada:
for (let i = 0; i < links.length; i++) {
links[i].addEventListener("click", (e) => e.preventDefault());
links[i].addEventListener("mouseenter", mouseenterFunc);
}
mouseenterFunc
O corpo do mouseenterFunc função fica assim:
function mouseenterFunc() {
for (let i = 0; i < links.length; i++) {
if (links[i].parentNode.classList.contains("active")) {
links[i].parentNode.classList.remove("active");
}
links[i].style.opacity = "0.25";
}
this.parentNode.classList.add("active");
this.style.opacity = "1";
const width = this.getBoundingClientRect().width;
const height = this.getBoundingClientRect().height;
const left = this.getBoundingClientRect().left;
const top = this.getBoundingClientRect().top;
const color = colors[Math.floor(Math.random() * colors.length)];
target.style.width = `${width}px`;
target.style.height = `${height}px`;
target.style.left = `${left}px`;
target.style.top = `${top}px`;
target.style.borderColor = color;
target.style.transform = "none";
}
Dentro desta função fazemos o seguinte:
- Adicione o
activeclass para o pai imediato (li) do link de destino. - Diminua o
opacityde todos os links do menu, exceto o "ativo". - Use o
getBoundingClientRectmétodo para recuperar o tamanho do link associado e sua posição em relação à viewport. - Obtenha uma cor aleatória da matriz mencionada e passe-a como valor para o
border-colorpropriedade dospanelemento. Lembre-se, seu valor de propriedade inicial é definido comotransparent. - Atribua os valores extraídos do
getBoundingClientRectmétodo para as propriedades correspondentes dospanelemento. Em outras palavras, ospantag herda o tamanho e a posição do link sobre o qual o cursor está passando. - Redefina a transformação padrão aplicada ao
spanelemento. Esse comportamento só é importante na primeira vez que passamos o mouse sobre um link. Neste caso, a transformação do elemento vai detransform: translateX(-60px)paratransform: none. Isso nos dá um bom efeito de deslizamento.
Se ativo
É importante observar que o código acima é executado toda vez que passamos o mouse sobre um link. Portanto, ele também é executado quando passamos o mouse sobre um link “ativo”. Para evitar esse comportamento, envolvemos o código acima dentro de um if declaração:
function mouseenterFunc() {
if (!this.parentNode.classList.contains("active")) {
// code here
}
}
Até agora, nossa demonstração é a seguinte:
Quase, mas não completamente
Então, tudo parece funcionar como esperado, certo? Bem, isso não é verdade porque se rolarmos pela página ou redimensionarmos a janela de visualização e tentarmos selecionar um link, as coisas ficarão confusas. Especificamente, a posição do span elemento fica incorreto.
Brinque com a demonstração de página inteira (certifique-se de ter adicionado conteúdo fictício suficiente) para ver o que quero dizer.
Para resolvê-lo, temos que calcular o quanto rolamos do topo da janela e adicionar esse valor ao valor atual top valor do elemento de destino. Da mesma forma, devemos calcular o quanto o documento foi rolado horizontalmente (apenas no caso). O valor resultante é adicionado ao valor atual left valor do elemento de destino.
Aqui estão as duas linhas de código que atualizamos:
const left = this.getBoundingClientRect().left + window.pageXOffset; const top = this.getBoundingClientRect().top + window.pageYOffset;
Tenha em mente que todo o código acima é executado assim que o navegador processa o DOM e encontra o script relevante. Novamente, para suas próprias implementações e designs, você pode querer executar este código quando a página for carregada, ou algo assim. Nesse cenário, você terá que incorporá-lo em um manipulador de eventos (por exemplo, load manipulador de eventos).
Janela de exibição
A última coisa que precisamos fazer é garantir que o efeito ainda funcione conforme redimensionamos a janela do navegador. Para isso, ouvimos o resize evento e registre o resizeFunc manipulador de eventos.
window.addEventListener("resize", resizeFunc);
Aqui está o corpo deste manipulador:
function resizeFunc() {
const active = document.querySelector(".mynav li.active");
if (active) {
const left = active.getBoundingClientRect().left + window.pageXOffset;
const top = active.getBoundingClientRect().top + window.pageYOffset;
target.style.left = `${left}px`;
target.style.top = `${top}px`;
}
}
Dentro da função acima, fazemos o seguinte:
- Verifique se há um item de lista de menu com a classe de
active. Se lá é tal elemento, que indica que já passamos o mouse sobre um link. - Obtenha o novo
leftetoppropriedades do item “ativo” junto com as propriedades de janela relacionadas e atribua-as aospanelemento. Observe que recuperamos os valores apenas para as propriedades que mudam durante oresizeevento. Isso significa que não há necessidade de recalcular a largura e a altura dos links do menu.
Suporte ao navegador
A demonstração funciona bem em todos os navegadores recentes. Se você encontrar algum problema, deixe-me saber nos comentários abaixo. Além disso, como você deve ter notado, usamos Babel para compilar nosso código ES6 para ES5.
Conclusão
Neste tutorial, passamos pelo processo de criação de um efeito de foco de menu simples, mas interessante.
Espero que você tenha gostado do que construímos aqui e se inspirado para desenvolver efeitos de menu ainda mais poderosos, como o que aparece (no momento da redação) no site do Stripe.
Você já criou algo parecido? Se sim, não deixe de compartilhar conosco os desafios que você enfrentou.
[ad_2]