Advertisement
  1. Web Design
  2. JavaScript

Come realizzare un effetto hover sottolineato in movimento con CSS e Javascript

Scroll to top
Read Time: 6 min

() translation by (you can also view the original English article)

Nel tutorial di oggi, andremo a usare un po' di CSS e Javascript per creare un menu con effetto hover fantasioso. Non è complicato come risultato finale, eppure realizzarlo sarà una grande opportunità di praticare le nostre abilità come front-end.

Senza ulteriori indugi, diamo un'occhiata a cosa realizzeremo:

Il markup

Iniziamo con del markup molto di base; un elemento nav che contiene il menu e un elemento span vuoto:

1
<nav class="mynav">
2
  <ul>
3
    <li>
4
      <a href="">Home</a>
5
    </li>
6
    <li>
7
      <a href="">About</a>
8
    </li>
9
    <li>
10
      <a href="">Company</a>
11
    </li>
12
    <li>
13
      <a href="">Work</a>
14
    </li>
15
    <li>
16
      <a href="">Clients</a>
17
    </li>
18
    <li>
19
      <a href="">Contact</a>
20
    </li>
21
  </ul>
22
</nav>
23
24
<span class="target"></span>

Il CSS

Con il markup pronto, dopo specifichiamo alcuni stili di base per gli elementi collegati:

1
.mynav ul {
2
  display: flex;
3
  justify-content: center;
4
  flex-wrap: wrap;
5
  list-style-type: none;
6
  padding: 0;
7
}
8
9
.mynav li:not(:last-child) {
10
  margin-right: 20px;
11
}
12
13
.mynav a {
14
  display: block;
15
  font-size: 20px;
16
  color: black;
17
  text-decoration: none;
18
  padding: 7px 15px;
19
}
20
21
.target {
22
  position: absolute;
23
  border-bottom: 4px solid transparent;
24
  z-index: -1;
25
  transform: translateX(-60px);
26
}
27
28
.mynav a,
29
.target {
30
  transition: all .35s ease-in-out;
31
}

Notiamo che l'elemento span (.target) è posizionato in modo absolute. Come vedremo in un istante, useremo Javascript per determinare la sua esatta posizione. In aggiunta, dovrebbe apparire dietro i link del menu, così gli diamo uno z-index negativo.

Javascript

A questo punto, focalizziamo la nostra attenzione sul Javascript necessario. Per iniziare, ci dirigiamo sugli elementi desiderati. Definiamo anche un array di colori che useremo più tardi.

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

Eventi

Dopo restiamo in ascolto degli eventi click e mouseenter dei link del menu.

Quando l'evento click si verifica, impediamo alla pagina di ricaricarsi. naturalmente, nel nostro caso funziona perché tutti i link hanno un attributo href vuoto. In un progetto vero tuttavia, ognuno dei link del menu probabilmente aprirà una pagina diversa.

Più importante, non appena l'evento mouseenter parte, la funzione di callback mouseenterFunc è eseguita:

1
for (let i = 0; i < links.length; i++) {
2
  links[i].addEventListener("click", (e) => e.preventDefault());
3
  links[i].addEventListener("mouseenter", mouseenterFunc);
4
}

mouseenterFunc

Il corpo della funzione mouseenterFunc somiglia a questo:

1
function mouseenterFunc() {
2
  for (let i = 0; i < links.length; i++) {
3
    if (links[i].parentNode.classList.contains("active")) {
4
      links[i].parentNode.classList.remove("active");
5
    }
6
    links[i].style.opacity = "0.25";
7
  }
8
  
9
  this.parentNode.classList.add("active");
10
  this.style.opacity = "1";
11
  
12
  const width = this.getBoundingClientRect().width;
13
  const height = this.getBoundingClientRect().height;
14
  const left = this.getBoundingClientRect().left;
15
  const top = this.getBoundingClientRect().top;
16
  const color = colors[Math.floor(Math.random() * colors.length)];
17
18
  target.style.width = `${width}px`;
19
  target.style.height = `${height}px`;
20
  target.style.left = `${left}px`;
21
  target.style.top = `${top}px`;
22
  target.style.borderColor = color;
23
  target.style.transform = "none";
24
}

All'interno di questa funzione facciamo i seguenti passi:

  1. Aggiungiamo la classe active al genitore immediato (li) del link in questione.
  2. Diminuiamo l'opacity da tutti i link del menu, tranne da quello "attivo".
  3. Usiamo il metodo getBoundingClientRect per recuperare la misura del link associato e la sua posizione relativa al viewport.
  4. Prendiamo un colore a caso dall'array menzionato prima e lo passiamo come valore alla proprietà border-color dell'elemento span. Ricordate, il valore della sua proprietà iniziale è impostato su transparent.
  5. Assegniamo i valori estratti al metodo getBoundingClientRect alla corrispondente proprietà dell'elemento span. In altre parole, il tag span eredita la misura e la posizione del link su cui abbiamo portato il mouse.
  6. Resettiamo la trasformazione di predefinita applicata all'elemento span. Il comportamento è importante solo la prima volta che portiamo il mouse su un link. In questo caso, la trasformazione dell'elemento va da transform: translateX(-60px) a transform: none.  Ciò gli dà un piacevole effetto scorrimento verso l'interno.

Se attivo

È importante notare che il codice sopra è eseguito ogni volta che portiamo il mouse su un link. Di conseguenza gira anche quando portiamo il mouse su un link "attivo". Per prevenire questo comportamento, avvolgiamo il codice in una dichiarazione if:

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

4
  }
5
}

Finora, la nostra demo appare come segue:

Quasi, ma non del tutto

Quindi, ogni cosa sembra funzionare come ci aspettavamo, giusto? Beh, non è vero perché se scorriamo attraverso la pagina o ridimensioniamo il viewport e poi cerchiamo di selezionare un link, le cose diventano caotiche. Precisamente, la posizione dell'elemento span diventa scorretta.

Giocherellate con la demo a piena pagina per vedere cosa intendo.

Per risolverlo, dobbiamo calcolare quanta distanza abbiamo scorso dall'inizio della finestra e aggiungere questo valore la valore top attuale dell'elemento di riferimento. Allo stesso modo dovremmo calcolare di quanto il documento è stato scorso orizzontalmente (per ogni eventualità). Il valore risultante viene aggiunto la valore left attuale dell'elemento di riferimento.

Ecco le due linee di codice che abbiamo aggiornato:

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

Tenete a mente che tutto il codice sopra è eseguito non appena il browser esamina il DOM e trova lo script attinente. Ancora, per le vostre proprie implementazioni e progetti potreste voler lanciare questo codice quando si carica la pagina, o qualcosa del genere. In un tale scenario, dovrete inserirlo in un gestore di eventi (es. gestore di eventi load).

Viewport

L'ultima cosa da fare è assicurarsi che l'effetto funzionerà ancora quando ridimensioneremo la finestra del browser. Per raggiungere ciò, restiamo in ascolto dell'evento resize e registriamo il gestore eventi resizeFunc.

1
window.addEventListener("resize", resizeFunc);

Ecco il corpo di questo gestore:

1
function resizeFunc() {
2
  const active = document.querySelector(".mynav li.active");
3
  
4
  if (active) {
5
    const left = active.getBoundingClientRect().left + window.pageXOffset;
6
    const top = active.getBoundingClientRect().top + window.pageYOffset;
7
8
    target.style.left = `${left}px`;
9
    target.style.top = `${top}px`;
10
  }
11
}

All'interno della funzione sopra, facciamo le seguenti cose:

  1. Controlliamo se c'è una voce nella lista del menu con la classe active. Se c'è tale elemento, ciò significa che abbiamo già portato il mouse su un link.
  2. Prendete le nuove proprietà di left e top della voce "attiva" insieme alle proprietà della finestra connessa e assegnategli l'elemento span. Notate che recuperiamo i valori solo per le proprietà che cambiano durante l'evento resize. Ciò significa che non c'è bisogno di ricalcolare la larghezza e l'altezza dei link del menu.

Supporto browser

La demo funziona bene in tutti i browser recenti. Se però vi imbattete in un qualche problema, fatemelo sapere nei commenti sotto. Inoltre, come avete probabilmente notato, abbiamo usato Babel per compilare il nostro codice ES6 fino a ES5.

Conclusioni

In questa veloce dritta siamo andati oltre il processo di creazione di un semplice, eppure interessante effetto hover su menu.

Spero che vi sia piaciuto ciò che abbiamo realizzato qui e vi abbia dato un'ispirazione per sviluppare effetti menu ancora più potenti come quello che appare (al momento in cui scrivo) sul sito di Stripe.

Avete mai creato qualcosa di simile? Se si, assicuratevi di condividere con noi le sfide che avete affrontato.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Web Design tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.