Categories

Scroll Animation (Vanilla JS)
Jeremy Threlfall

Jeremy Threlfall

Aug 14, 2025

Scroll Animation (Vanilla JS)

All page elements, including the heading and multiple boxes, are created dynamically via JavaScript. The script calculates each box’s position relative to the viewport and applies a CSS class to trigger animations as the user scrolls. This approach allows smooth transitions and interactive effects without any pre-existing HTML, while keeping the page fully responsive and modular.

Live Demo | GitHub

HTML:

The HTML elements are all created in JavaScript. Nothing to see here.

CSS:

The CSS uses transform and transition to animate the boxes into view. Each .box is initially moved off-screen to the left or right using translateX(400%) or translateX(-400%). The .box.show class resets the transform to 0, creating a smooth sliding effect with transition: transform 0.3s ease. The :nth-of-type(even) pseudo-class alternates the initial direction so every other box slides in from the opposite side.

/* Basic CSS Reset */
...
h1 {
	margin: 10px;
}
.box-container {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
}
.box {
	background-color: #4682b4;
	color: #fff;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 400px;
	height: 200px;
	margin: 10px;
	border-radius: 10px;
	box-shadow: 2px 4px 5px rgba(0, 0, 0, 0.3);
	/* move the elements off screen, to the right */
	transform: translateX(400%);
	transition: transform 0.3s ease;
}
/* target every other/even elements of this class and move them off screen, to the left */
.box:nth-of-type(even) {
	transform: translateX(-400%);
}
.box.show {
	transform: translateX(0);
}
.box h2 {
	font-size: 45px;
}
JavaScript:

The JavaScript in this project handles both building the page and controlling the scroll animation. It starts by creating the main heading dynamically, appending an h1 directly to the document.body. A div with the class box-container is also created to hold all the boxes, keeping them grouped and allowing the layout to remain independent of the heading. The script then loops numBoxes times to create each .box, appending an h2 with text content to each box, and then placing the boxes inside the container. This approach ensures that the HTML structure is entirely generated via JavaScript, with no pre-existing elements needed in the HTML file.

The checkBoxes function controls the animation by checking each box’s position relative to the viewport. It calculates a triggerBottom value so that boxes only animate into view when they are near the bottom of the window, creating a smooth entry effect. For each .box, it compares its top position to triggerBottom and conditionally adds or removes the show class. Adding show triggers the CSS transform and transition, animating the box into view, while removing it reverses the effect. The function is run both on page load and on each scroll event, ensuring that the animation responds dynamically as the user navigates down the page.

// number of boxes to make
const numBoxes = 50;

// create the h1 heading and append to body
const heading = document.createElement('h1');
heading.textContent = 'Scroll to see the animation';
document.body.appendChild(heading);

// create a container with class to contain all the boxes and append to body
const container = document.createElement('div');
container.classList.add('box-container');
document.body.appendChild(container);

// create numBoxes boxes, applying class, appending h2 with text content, and appending each box to the container
for (let i = 1; i <= numBoxes; i++) {
	const box = document.createElement('div');
	box.classList.add('box');
	const boxText = document.createElement('h2');
	boxText.textContent = `Content ${i}`;
	box.appendChild(boxText);
	document.body.appendChild(box);
}

// make an array containing all the boxes
const boxes = document.querySelectorAll('.box');

const checkBoxes = () => {
	// set the triggerBottom to be less than innerHeight of window, so user can see the transition animation clearly
	const triggerBottom = (window.innerHeight / 5) * 4;
	boxes.forEach((box) => {
		// get the top of box rectangle to compare with triggerBottom
		const boxTop = box.getBoundingClientRect().top;
		// add show, box animates into viewport. remove show, box animates out of viewport
		boxTop < triggerBottom
			? box.classList.add('show')
			: box.classList.remove('show');
	});
};

window.addEventListener('scroll', checkBoxes);

// run checkBoxes on page load so that there is some content before scrolling starts
checkBoxes();
Final Thoughts:

This project demonstrates how to dynamically create and manage all HTML elements using vanilla JavaScript. While libraries like jQuery and frameworks such as React or Angular have largely changed how developers approach DOM manipulation, doing everything in plain JS can still be useful for understanding the fundamentals. Although building a page entirely in JS can be handy when content must be generated on the fly, it’s not always the most practical approach. Ultimately, this project served as a focused refresher on manipulating the DOM and handling layout and animations purely with scripts.

author.name

Jeremy Threlfall

Jeremy is a Fullstack Developer. Originally from the United States, he currently resides in Taiwan, and works freelance remotely.

Portfolio

Leave a Comment (will be submitted for review)

Related Posts

Categories