如何用JavaScript实现列表项逐个淡入动画?修复代码报错
Let's break down the issues and fix them one by one to get that staggered fade-in effect working perfectly.
Why the "Cannot read property 'classList' of undefined" Error?
The problem lies in how setTimeout interacts with your loop variable i. Since setTimeout runs asynchronously, by the time its callback fires, the loop has already finished executing. At that point, i equals items.length—which is beyond the last index of your list items—so items[i] returns undefined, hence the error.
Fixing the Error & Building the Sequential Fade-In
We'll address the loop issue first, then correct a CSS selector mistake, and tweak timing to create the staggered effect you want.
1. Fixed JavaScript Code
We can use let instead of var for the loop variable (since let has block scope, each iteration gets its own unique copy of i). This ensures the setTimeout callback references the correct item index when it runs:
function fadeItems() { const items = document.getElementsByClassName("fade-item"); for (let i = 0; i < items.length; i++) { // Stagger each item's fade-in by 300ms (adjust this value for faster/slower gaps) setTimeout(() => { items[i].classList.add("fadein"); }, 300 * i); } }
I changed the fixed 3000ms delay to 300 * i—this makes each item fade in 300ms after the previous one, creating the smooth staggered effect you're aiming for.
2. Corrected CSS
Your original CSS used a descendant selector (.fade-item .fadein), which looks for an element with class fadein inside a .fade-item. Instead, we need a combined class selector to target .fade-item elements that also have the fadein class:
.fade-item { opacity: 0; /* Use transition for a smooth, simple opacity change */ transition: opacity 0.9s ease-in-out; } .fade-item.fadein { opacity: 1; } /* Optional: If you prefer using keyframes instead of transition, uncomment below */ /* @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } .fade-item.fadein { animation: fadeIn 0.9s forwards; } */
I simplified the CSS using a transition since it's more straightforward for this use case, but kept the keyframe version commented out if you prefer that approach.
3. Full Working Code
Here's the complete, tested code put together:
<ul> <li class="fade-item">Item 01</li> <li class="fade-item">Item 02</li> <li class="fade-item">Item 03</li> <li class="fade-item">Item 04</li> <li class="fade-item">Item 05</li> </ul> <style> .fade-item { opacity: 0; transition: opacity 0.9s ease-in-out; } .fade-item.fadein { opacity: 1; } </style> <script> function fadeItems() { const items = document.getElementsByClassName("fade-item"); for (let i = 0; i < items.length; i++) { setTimeout(() => { items[i].classList.add("fadein"); }, 300 * i); } } // Trigger the fade-in when the page finishes loading window.addEventListener('load', fadeItems); </script>
How It All Comes Together
- The
letkeyword ensures each loop iteration retains its ownivalue, so thesetTimeoutcallback always references the correct list item. - The staggered delay (
300 * i) creates the sequential fade-in effect, with each item appearing shortly after the last. - The corrected CSS selector applies the opacity change immediately when the
fadeinclass is added to a list item.
内容的提问来源于stack exchange,提问作者Suresh.W

