循环中<li>的addEventListener不触发及相关技术疑问
<li>添加类的解决方案 Hey there! I see you've been stuck on this for two days—let's fix this together. First, let's go over why your original code wasn't working, then share a polished solution, and finally answer all your specific questions clearly.
First: Why Your Original Code Failed
Let's break down the key issues:
- HTML inline
onclickconflict: You addedonclick="showMe()"to your<li>, which calls the function in the global scope (wherethispoints towindow, not the<li>) and overrides part of youraddEventListenerlogic. - Loop variable hoisting bug: In your second try,
pullTextOnClickusesliitems[i], but by the time the click event fires, the loop has finished andiequalsliitems.length—soliitems[i]isundefined. - Incorrect
bindusage: Your first try'sbind(this, lit)setsthistowindow(since you're insidewindow.onload) instead of the target<li>. - Pseudo-element note: Remember,
::afterisn't a real DOM element, so you can't bind events directly to it—but clicks on it bubble up to its parent<li>, so binding the<li>is the right approach.
Fixed Working Code
Here's the corrected version that works as expected:
HTML
<div class="container"> <section class="timeline" id="timelineexpand"> <ul> <li class="liitems" id="li1"> <div> <time>1992</time> Sth happend </div> </li> <li class="liitems" id="li2"> <div> <time>1997</time> Sth else </div> </li> </ul> </section> </div>
CSS (added a test style for in-view)
.timeline ul li { list-style-type:none; position:relative; width:6px; margin:0 auto; padding-top:50px; background:#fff; } .timeline ul li::after{ content:''; position:absolute; left:50%; bottom:0; transform: translateX(-50%); width:50px; height:50px; border-radius:50%; background: inherit; cursor:pointer; } /* Visual feedback for the active class */ .timeline ul li.in-view { background: #ff4444; }
JavaScript
/* jslint browser: true */ /*global window */ window.onload = function() { // Use querySelectorAll for cleaner selection const liItems = document.querySelectorAll(".liitems"); // Event handler: `this` points to the clicked <li> by default function handleLiClick() { this.classList.add("in-view"); console.log(this.className); } // Bind the handler to each <li> liItems.forEach(item => { item.addEventListener("click", handleLiClick); }); };
Answers to Your Specific Questions
1. What does /*global window */ do? Why do I get "window.onload is undefined" without it?
This is a JSLint directive comment. JSLint is a strict code-linting tool that checks for undeclared variables. Without this comment, JSLint doesn't recognize window as a built-in global variable, so it throws a warning saying "window.onload is undefined". If you don't use JSLint, you can safely remove this comment—its only purpose is to satisfy the linter.
2. How do I bind the target <li> element to the event handler?
You have three reliable options:
- Use
thisdirectly: When you bind a function withaddEventListener,thisautomatically refers to the DOM element that triggered the event (your<li>). This is the simplest approach, as shown in the fixed code. - Use
event.currentTarget: If you need to be explicit, access the bound element via the event object:function handleLiClick(event) { const targetLi = event.currentTarget; // Always the element you bound the event to targetLi.classList.add("in-view"); } - Avoid inline
onclickattributes: Inline attributes likeonclick="showMe()"forcethisto point towindow(global scope), so they're best avoided in favor ofaddEventListener.
3. How does bind() work? Why didn't my code use it correctly?
Function.prototype.bind() creates a new copy of your function with two key changes:
- The
thisvalue inside the new function is permanently set to the first argument you pass tobind(). - Any additional arguments you pass to
bind()are preset as parameters for the new function.
Your code failed because:
- You passed
this(which iswindowinsidewindow.onload) as the first argument tobind(), soshowMe'sthisbecamewindowinstead of the<li>. - You also had an inline
onclickcalling the originalshowMefunction, so your bound version was never actually used.
If you wanted to use bind intentionally (though it's unnecessary here), you could do this:
const lit = document.getElementById("li1"); function showMe(element) { element.classList.add("in-view"); } // Bind the <li> as the first parameter to showMe lit.addEventListener("click", showMe.bind(null, lit));
4. Why does addClass() throw an error, but classList.add() works?
addClass() is a jQuery method, not native JavaScript. If you haven't included the jQuery library in your page, calling addClass() will throw a "function not defined" error.
classList.add() is a native DOM API supported by all modern browsers—no external libraries required. It's the standard way to add classes to elements in vanilla JS.
If you really want to use addClass(), you'd need to first include jQuery in your HTML:
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
Then use jQuery's syntax:
$(".liitems").click(function() { $(this).addClass("in-view"); });
But native classList is more lightweight and doesn't require extra dependencies, so it's the better choice here.
内容的提问来源于stack exchange,提问作者Zheka

