How to create a “sticky scroll animation” (scrollytelling) section in Elementor? My CSS + JS setup is not working

1 week ago 5
ARTICLE AD BOX

I’m trying to reproduce a “sticky scroll animation” (also known as scrollytelling / pinned scroll) inside a WordPress page built with Elementor.
The effect I want is the same as the one used on thelot.es right after the headline “The Lot es ese lugar donde siempre pasa algo”:
A full-viewport section stays fixed while multiple tiles fade in/out one by one as the user scrolls through a 400vh container.

I already added custom CSS and custom JS inside Elementor → Page Settings → Advanced, but the animation does not trigger.
None of the elements receive the .active class, and all tiles stay static.

Section: "Eventos y Servicios 400" ├── Inner Section: "E&S Viewport 100" │ ├── Heading: Title │ ├── Heading: Subtitle │ └── Container: "Eventos y servicios" ← contains the 4 tiles │ ├── Tile 1 (class: tile-1 magic-item) │ ├── Tile 2 (class: tile-2 magic-item) │ ├── Tile 3 (class: tile-3 magic-item) │ └── Tile 4 (class: tile-4 magic-item) └── Widget: HTML (JS not stored here, used only for testing)

Classes applied in Elementor (no dots):

Section: scroll-track

Inner Section: sticky-view

Tiles wrapper: magic-tiles

Each tile: magic-item + tile-X

CSS added in Page Settings → Advanced → Custom CSS

.scroll-track { min-height: 400vh; position: relative; } .sticky-view { position: sticky; top: 0; min-height: 100vh; height: 100vh; display: flex; align-items: center; overflow: hidden; } .magic-tiles { position: relative; width: 100%; height: 100%; } .magic-item { position: absolute; top: 50%; left: 50%; width: 60%; transform: translate(-50%, -50%) scale(0.9); opacity: 0; transition: opacity 0.5s ease-out, transform 0.5s ease-out; } .magic-item.active { opacity: 1; transform: translate(-50%, -50%) scale(1); }

JavaScript added in Page Settings → Advanced → Custom JS

(Elementor loads it automatically without <script> tags)

document.addEventListener('scroll', function () { const track = document.querySelector('.scroll-track'); if (!track) return; const tiles = document.querySelectorAll('.magic-item'); if (!tiles.length) return; const rect = track.getBoundingClientRect(); const trackHeight = track.offsetHeight - window.innerHeight; if (trackHeight <= 0) return; let progress = -rect.top / trackHeight; if (progress < 0) progress = 0; if (progress > 1) progress = 1; const sections = tiles.length; const sectionSize = 1 / sections; tiles.forEach((tile, index) => { const start = sectionSize * index; const end = sectionSize * (index + 1); const isActive = progress >= start && progress < end; tile.classList.toggle('active', isActive); tile.style.zIndex = (sections - index).toString(); }); });

Expected behavior

The .sticky-view element should remain fixed during scrolling.

As the user scrolls through the 400vh .scroll-track, only one tile should become .active at a time.

The active tile should fade/scale in, replacing the previous one.


Actual behavior

All tiles remain visible or behave statically.

No .active class is added or removed while scrolling.

The JS appears to run (no console errors), but the scroll calculations do not seem to affect the elements.


What I’ve already checked

Classes in Elementor do NOT include the leading dot (e.g., scroll-track is applied correctly).

Custom JS runs (tested with console logs).

All custom CSS loads correctly on the frontend.

No Elementor motion effects are applied to the tiles.

No JavaScript conflicts detected.


Question

What could be causing this Elementor section not to behave as a scroll-pinned scrollytelling block?
Are there known limitations with position: sticky inside Elementor containers or Flexbox sections that would prevent this JS/CSS approach from working?

I'm trying to get the effect from this webpage: https://www.thelot.es/
just in the "The Lot es ese lugar donde siempre pasa algo" section (Day, Night, Events)

Any help debugging the scroll calculations or correcting the Elementor layout would be appreciated.

Read Entire Article