Come costruire un effetto Hover Underline Shifting con CSS e JavaScript

Nel tutorial di oggi, useremo un po 'di CSS e JavaScript per creare un effetto hover del menu di fantasia. Non è un risultato finale complicato, tuttavia costruirlo sarà una grande opportunità per mettere in pratica le nostre competenze front-end.

Senza ulteriori intro, diamo un'occhiata a cosa costruiremo:

Il markup

Iniziamo con un markup molto semplice; un nav elemento che contiene il menu e un vuoto campata elemento:

 

Il CSS

Con la marcatura pronta, successivamente specifichiamo alcuni stili di base per gli elementi correlati:

.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; colore nero; decorazione del testo: nessuna; imbottitura: 7px 15px;  .target position: absolute; border-bottom: 4px solid transparent; z-index: -1; transform: translateX (-60px);  .mynav a, .target transition: all .35s easy-in-out; 

Si noti che il campata elemento (.bersaglio) È assolutamente posizionato. Come vedremo tra un momento, utilizzeremo JavaScript per determinare la sua posizione esatta. Inoltre, dovrebbe apparire dietro a i collegamenti del menu, quindi diamo un negativo z-index.

Il JavaScript

A questo punto, concentriamo la nostra attenzione sul JavaScript richiesto. Per cominciare, miriamo agli elementi desiderati. Definiamo anche una serie di colori che useremo in seguito.

const target = document.querySelector (". target"); const links = document.querySelectorAll (". mynav a"); const colors = ["deepskyblue", "orange", "firebrick", "gold", "magenta", "black", "darkblue"];

eventi

Quindi ascoltiamo per il clic e MouseEnter eventi dei collegamenti del menu. 

Quando il clic evento accade, impediamo alla pagina di ricaricarsi. Naturalmente, questo funziona nel nostro caso perché tutti i link hanno un vuoto href attributo. In un progetto reale, tuttavia, ognuno dei collegamenti del menu aprirebbe probabilmente una pagina diversa.  

Soprattutto, non appena il MouseEnter fuochi di eventi, il mouseenterFunc la funzione di callback è eseguita:

for (let i = 0; i < links.length; i++)  links[i].addEventListener("click", (e) => e.preventDefault ()); link [i] .addEventListener ("mouseenter", mouseenterFunc); 

mouseenterFunc

Il corpo del mouseenterFunc la funzione si presenta così:

function mouseenterFunc () for (lascia che 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 = '$widthpx'; target.style.height = '$heightpx'; target.style.left = '$leftpx'; target.style.top = '$toppx'; target.style.borderColor = color; target.style.transform = "none"; 

All'interno di questa funzione facciamo quanto segue:

  1. Aggiungi il attivo classe al genitore immediato (Li) del link di destinazione.
  2. Diminuire il opacità da tutti i collegamenti dei menu, a parte quello "attivo".
  3. Utilizzare il getBoundingClientRect metodo per recuperare la dimensione del collegamento associato e la sua posizione rispetto al viewport. 
  4. Ottieni un colore casuale dall'array sopracitato e passalo come valore al colore del bordo proprietà del campata elemento. Ricorda, il suo valore di proprietà iniziale è impostato su trasparente.
  5. Assegna i valori estratti dal getBoundingClientRect metodo alle proprietà corrispondenti del campata elemento. In altre parole, il campata il tag eredita la dimensione e la posizione del link su cui si trova il passaggio del mouse.
  6. Reimposta la trasformazione predefinita applicata a campata elemento. Questo comportamento è importante solo la prima volta che passiamo il mouse su un link. In questo caso, la trasformazione dell'elemento va da trasformare: translateX (-60px) a trasformare: nessuno. Questo ci dà un buon effetto slide-in.

Se attivo

È importante notare che il codice sopra riportato viene eseguito ogni volta che si passa il mouse su un collegamento. Funziona quindi quando si passa con il mouse su un collegamento "attivo". Per evitare questo comportamento, avvolgiamo il codice sopra all'interno di Se dichiarazione:

function mouseenterFunc () if (! this.parentNode.classList.contains ("active")) // code here

Finora, la nostra demo ha il seguente aspetto:

Quasi, ma non abbastanza

Quindi, tutto sembra funzionare come previsto, giusto? Bene, non è vero perché se scorriamo la pagina, o ridimensioniamo il viewport, e poi proviamo a selezionare un collegamento, le cose si complicano. In particolare, la posizione del campata l'elemento diventa errato.

Gioca con la demo della pagina completa (assicurati di aver aggiunto abbastanza contenuti fittizi) per vedere cosa intendo.

Per risolverlo, dobbiamo calcolare fino a che punto siamo scorsi dalla parte superiore della finestra e aggiungere questo valore alla corrente superiore valore dell'elemento obiettivo. Allo stesso modo dovremmo calcolare fino a che punto il documento è stato scostato orizzontalmente (per ogni evenienza). Il valore risultante viene aggiunto alla corrente sinistra valore dell'elemento obiettivo.

Ecco le due righe di codice che aggiorniamo:

const left = this.getBoundingClientRect (). left + window.pageXOffset; const top = this.getBoundingClientRect (). top + window.pageYOffset;

Tieni presente che tutto il codice sopra riportato viene eseguito non appena il browser elabora il DOM e trova lo script pertinente. Ancora una volta, per le tue implementazioni e i tuoi progetti potresti voler eseguire questo codice quando la pagina viene caricata, o qualcosa del genere. In uno scenario del genere, dovrai incorporarlo all'interno di un gestore di eventi (ad es. caricare gestore di eventi).

viewport

L'ultima cosa che dobbiamo fare è assicurarci che l'effetto funzioni ancora mentre ridimensioniamo la finestra del browser. Per realizzare ciò, ascoltiamo il ridimensionare evento e registrare il resizeFunc gestore di eventi.

window.addEventListener ("ridimensiona", resizeFunc);

Ecco il corpo di questo gestore:

function resizeFunc () const active = document.querySelector (". mynav li.active"); if (attivo) const left = active.getBoundingClientRect (). left + window.pageXOffset; const top = active.getBoundingClientRect (). top + window.pageYOffset; target.style.left = '$ left px'; target.style.top = '$ top px'; 

All'interno della funzione sopra, facciamo quanto segue:

  1. Controlla se c'è una voce di elenco di menu con la classe di attivo. Se ci è un tale elemento, che afferma che abbiamo già passato il tempo sopra un link.
  2.  Prendi il nuovo sinistra e superiore proprietà dell'elemento "attivo" insieme alle relative proprietà della finestra e assegnarle a campata elemento. Si noti che recuperiamo i valori solo per le proprietà che cambiano durante il ridimensionare evento. Ciò significa che non è necessario ricalcolare la larghezza e l'altezza dei collegamenti del menu.

Supporto del browser

La demo funziona bene in tutti i browser recenti. Se riscontri dei problemi, fammelo sapere nei commenti qui sotto. Inoltre, come forse avrete notato, usiamo Babel per compilare il nostro codice ES6 fino a ES5.

Conclusione

In questo tutorial abbiamo esaminato il processo di creazione di un effetto hover di menu semplice ma interessante.

Spero vi sia piaciuto ciò che abbiamo costruito qui e ci siamo ispirati per sviluppare effetti di menu ancora più potenti come quello che appare (al momento della scrittura) nel sito Stripe.

Hai mai creato qualcosa di simile? Se è così, assicurati di condividere con noi le sfide che hai affrontato.