为多个元素添加addEventListener,避免元素不存在时触发TypeError
Great question! Dealing with multiple elements and event listeners while preventing crashes from missing elements is a common gotcha—let’s break down some straightforward, reliable solutions:
1. Check for Element Existence Before Binding Events
First, using a shared class name for all your animation divs (instead of relying solely on IDs) makes it easy to grab all targets at once. Then, you can loop through them and only bind events if the element actually exists.
You’ll also need a way to link each element to its Bodymovin animation instance—here’s how to do that with a Map:
// Step 1: Initialize animations and map them to their elements const animationsMap = new Map(); const animationElements = document.querySelectorAll('.animate-on-hover'); animationElements.forEach(element => { // Skip if the element somehow doesn't exist (extra safety) if (!element) return; // Initialize your Bodymovin animation const animation = bodymovin.loadAnimation({ container: element, renderer: 'svg', loop: true, autoplay: false, path: `./animations/${element.dataset.animationFile}.json` // Use data attribute to store animation path }); // Map the element to its animation instance animationsMap.set(element, animation); // Step 2: Add hover event listeners safely element.addEventListener('mouseenter', () => { // Grab the animation and play it only if it exists const targetAnimation = animationsMap.get(element); if (targetAnimation) targetAnimation.play(); }); element.addEventListener('mouseleave', () => { const targetAnimation = animationsMap.get(element); if (targetAnimation) targetAnimation.stop(); }); });
This approach ensures you never try to bind events to a non-existent element, and you avoid trying to call play()/stop() on an undefined animation instance.
2. Use Optional Chaining for Cleaner Modern Code
If you’re targeting modern browsers (most are now), the optional chaining operator (?.) lets you skip verbose existence checks. It short-circuits the code if the left-hand value is null or undefined, so no TypeError is thrown.
Here’s how to apply it to both element selection and animation calls:
// Single element example (no crash if element doesn't exist) document.getElementById('animationDiv')?.addEventListener('mouseenter', () => { animation?.play(); }); // Multiple elements example document.querySelectorAll('.animate-on-hover').forEach(element => { element.addEventListener('mouseenter', () => { animationsMap.get(element)?.play(); }); element.addEventListener('mouseleave', () => { animationsMap.get(element)?.stop(); }); });
This keeps your code concise while maintaining safety—just make sure your target browsers support optional chaining (all major browsers released after 2020 do).
3. Create a Reusable Safe Event Binding Function
If you need to support older browsers or want a reusable utility, wrap the existence check in a function. This way you don’t repeat the same check everywhere:
function safeBindEvent(element, eventType, handler) { // Only bind if the element exists and has the addEventListener method if (element && typeof element.addEventListener === 'function') { element.addEventListener(eventType, handler); } } // Use it for single elements const singleAnimDiv = document.getElementById('animationDiv'); safeBindEvent(singleAnimDiv, 'mouseenter', () => animation?.play()); safeBindEvent(singleAnimDiv, 'mouseleave', () => animation?.stop()); // Use it for multiple elements document.querySelectorAll('.animate-on-hover').forEach(element => { safeBindEvent(element, 'mouseenter', () => animationsMap.get(element)?.play()); safeBindEvent(element, 'mouseleave', () => animationsMap.get(element)?.stop()); });
This utility adds an extra layer of compatibility and keeps your main code clean.
Key Takeaway
The core idea is twofold:
- Always verify that elements and animation instances exist before interacting with them
- Use batch selection (like
querySelectorAll) and mapping to manage multiple animations efficiently
内容的提问来源于stack exchange,提问作者CK MacLeod

