View on GitHub


a bunch of vanilla JS practice projects based on JavaScript 30 by WesBos.


demo gif

Back to home page

User stories


Show me the content

The dropdown effect has two parts: the content and the background. For showing the content, we want to animate each list item in triggers from opacity:0 and display:none to display:block when hover and opacity:1 after X milliseconds.


.trigger-enter .dropdown {
 display: block;

.trigger-enter-active .dropdown {
 opacity: 1;

In JavaScript:

const triggers = document.querySelectorAll(".ul_nav > li");

function handleEnter() {
 this.classList.add("trigger-enter"); //showing links
 //if using function, this will be window, arrow function inherits parent's this
 //make sure cursor is still at the link
 setTimeout(() => this.classList.contains("trigger-enter") && this.classList.add("trigger-enter-active", 150));

function handleLeave() {
 this.classList.remove("trigger-enter", "trigger-enter-active");

triggers.forEach(trigger => trigger.addEventListener("mouseenter", handleEnter));
triggers.forEach(trigger => trigger.addEventListener("mouseleave", handleLeave));

Puppy background that follows along

Just like the content, background will be animated from opacity:0 to opacity:1 by adding a class open. {
 opacity: 1;

To locate the background in the right position, we need to figure out where the nav and the list item are so that when we scroll or added other elements before nav, dropdown will still locates at the right position.

const background = document.querySelector(".div_dropdownBackground");
const nav = document.querySelector(".nav_top");

function handleEnter() {

 const dropdown = this.querySelector(".dropdown"); //adding background
 const dropdownCoords = dropdown.getBoundingClientRect(); //li position
 const navCoords = nav.getBoundingClientRect(); //nav position

 //locate the background
 const coords = {
  height: dropdownCoords.height,
  width: dropdownCoords.width,
  top: -,
  left: dropdownCoords.left - navCoords.left
 };"width", `${coords.width}px`);"height", `${coords.height}px`);"transform", `translate(${coords.left}px,${}px)`);

function handleLeave() {
 this.classList.remove("trigger-enter", "trigger-enter-active");

A little new knowledge from this tutorial is CSS attribute “will-change”.