Hur man bygger en Shifting Underline Hover Effect med CSS och JavaScript

I dagens handledning kommer vi att använda en liten bit av CSS och JavaScript för att skapa en fin menyhuvudseffekt. Det är inte ett komplicerat slutresultat, men byggandet är ett utmärkt tillfälle att öva våra avancerade färdigheter.

Utan ytterligare intro, låt oss kolla vad vi ska bygga:

Markup

Vi börjar med en mycket grundläggande uppställning. en nav element som innehåller menyn och en tom spänna element:

 

CSS

Med markup redo, nästa vi specificerar några grundläggande stilar för de relaterade elementen:

.mynav ul display: flex; rättfärdiga innehåll: center; flex-wrap: wrap; lista-stil-typ: none; vaddering: 0;  .mynav li: not (: last-child) margin-right: 20px;  .mynav a display: block; fontstorlek: 20px; svart färg; text-dekoration: ingen; vaddering: 7px 15px;  .target position: absolute; gränsbotten: 4px solid transparent; z-index: -1; transformera: translateX (-60px);  .mynav a, .target transition: all .35s easy-in-out; 

Observera att spänna element (.mål) Är helt placerad. Som vi ser på ett ögonblick använder vi JavaScript för att bestämma dess exakta position. Dessutom bör det visas Bakom menyn länkar, så vi ger det en negativ z-index.

JavaScript

Låt oss nu fokusera på den obligatoriska JavaScript. Till att börja med riktar vi in ​​de önskade elementen. Vi definierar också en uppsättning färger som vi använder senare.

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

evenemang

Nästa lyssnar vi på klick och mouseenter händelser i menykopplingarna. 

När klick händelse händer, förhindrar vi att sidan laddas om. Naturligtvis fungerar detta i vårt fall eftersom alla länkar har en tom href attribut. I ett verkligt projekt kommer dock alla menykopplingar sannolikt att öppna en annan sida.  

Viktigast, så snart som mouseenter händelsebränder, mouseenterFunc återuppringningsfunktionen utförs:

för (låt jag = 0; i < links.length; i++)  links[i].addEventListener("click", (e) => e.preventDefault ()); länkar [i] .addEventListener ("mouseenter", mouseenterFunc); 

mouseenterFunc

Kroppen av mouseenterFunc funktionen ser så här ut:

funktion mouseenterFunc () for (låt jag = 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"; 

Inne i den här funktionen gör vi följande:

  1. Lägg till aktiva klass till omedelbar förälder (li) av mållänken.
  2. Minska opacitet från alla meny länkar, förutom den "aktiva" en.
  3. Använd getBoundingClientRect metod för att hämta storleken på den associerade länken och dess position i förhållande till visningsporten. 
  4. Få en slumpmässig färg från ovannämnda array och överför den som värde till gräns färg egenskapen hos spänna element. Kom ihåg att dess ursprungliga egenskapsvärde är inställt på transparent.
  5. Tilldela de värden som extraheras från getBoundingClientRect metod till motsvarande egenskaper hos spänna element. Med andra ord, spänna taggen ärverger storleken och läget för länken som svävar över.
  6. Återställ standardtransformationen som tillämpas på spänna element. Detta beteende är bara viktigt första gången vi svävar över en länk. I detta fall går omvandlingen av elementet från transformera: translateX (-60px) till transformera: ingen. Det ger oss en snygg inskjutning.

Om Aktiv

Det är viktigt att notera att koden ovan är exekverad varje gång vi svävar över en länk. Det körs därför när vi svävar över en "aktiv" länk också. För att förhindra detta beteende sätter vi in ​​koden ovan i en om påstående:

funktion mouseenterFunc () if (! this.parentNode.classList.contains ("active")) // kod här

Hittills ser vår demo ut enligt följande:

Nästan, men inte riktigt

Så verkar allt som förväntat, eller hur? Jo det är inte sant, för om vi bläddra igenom sidan, eller ändra storlek på visningsporten och försök att välja en länk, blir det rörigt. Specifikt läget för spänna elementet blir felaktigt.

Spela runt med demonstrationen för hela sidan (se till att du har lagt till tillräckligt med dummyinnehåll) för att se vad jag menar.

För att lösa det måste vi beräkna hur långt vi har bläddrat från toppen av fönstret och lägg till det här värdet till det aktuella topp värdet av målelementet. På samma sätt borde vi beräkna hur långt dokumentet blivit rullat horisontellt (bara i fall). Det resulterande värdet läggs till strömmen vänster värdet av målelementet.

Här är de två raderna av kod som vi uppdaterar:

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

Tänk på att hela koden ovan är körd så snart webbläsaren behandlar DOM och hittar det aktuella skriptet. Återigen, för dina egna implementeringar och mönster kanske du vill köra den här koden när sidan laddas, eller något liknande. I ett sådant scenario måste du bädda in det inom en händelsehanterare (t.ex.. ladda händelsehanterare).

View

Det sista vi måste göra är att se till att effekten fortfarande fungerar när vi ändrar storleken på webbläsarfönstret. För att uppnå detta lyssnar vi på ändra storlek händelse och registrera resizeFunc händelsehanterare.

window.addEventListener ("resize", resizeFunc);

Här är den här handlarens kropp:

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

Inne i funktionen ovan gör vi följande:

  1. Kontrollera om det finns ett menyobjekt med klassen aktiva. Om det är ett sådant element, som säger att vi redan har svängt över en länk.
  2.  Hämta det nya vänster och topp egenskaper för det "aktiva" objektet tillsammans med de relaterade fönsteregenskaperna och tilldela dem till spänna element. Observera att vi bara hämtar värdena för de egenskaper som ändras under ändra storlek händelse. Det betyder att man inte behöver räkna om bredden och höjden på menylänkarna.

Browser Support

Demon fungerar bra i alla nya webbläsare. Om du stöter på några problem, låt mig veta i kommentarerna nedan. Också, som du kanske har märkt, använder vi Babel för att kompilera vår ES6-kod ner till ES5.

Slutsats

I denna handledning gick vi igenom processen för att skapa en enkel men ändå intressant menyhuver effekt.

Jag hoppas att du njöt av vad vi byggde här och tog inspiration för att utveckla ännu mer kraftfulla menyeffekter som den som visas (vid skrivandet) i Stripes webbplats.

Har du någonsin skapat något liknande? Om så är fallet, var noga med att dela med oss ​​de utmaningar du ställt inför.