雙層迴圈為多個 `` 綁定 `onmouseover`/`onmouseout`,但滑鼠移入時所有圖片觸發同一組 `show()`/`unshow()`,且操作的是迴圈結束後的最終索引值。
for (var i = 0; i < boxs.length; i++) {
var imgs = boxs[i].getElementsByClassName("img");
for (var p = 0; p < imgs.length; p++) {
imgs[p].onmouseover = function() { show(); }; // 閉包捕獲「變數 p」,非「當時的值」
}
}
// 迴圈結束後 p = imgs.length,所有處理函數共享同一個 p
| 方案 | 代碼 | 原理 | 適用場景 |
|---|---|---|---|
| IIFE 立即執行函數 (ES5) | for(var p=0; p<len; p++) { (function(idx){ imgs[idx].onmouseover=function(){show(idx);} })(p); } | 建立新作用域鎖定當前值 | 舊環境、不支援 let |
| let 區塊作用域 (ES6+) | for(let p=0; p<len; p++) { imgs[p].onmouseover=function(){show(p);} } | 每次迴圈建立新綁定 | 現代瀏覽器、標準寫法 |
| bind 傳參 | imgs[p].onmouseover = show.bind(null, p); | 綁定參數建立新函數 | 需傳遞參數給處理函數 |
| 事件委派 (最佳) | parent.addEventListener('mouseover', function(e){ if(e.target.matches('.img')) show(e.target); }) | 單一監聽器利用冒泡 | 大量元素、動態新增、效能最佳 |
// 父容器單一綁定
document.getElementById('aa').addEventListener('mouseover', function(e) {
var img = e.target.closest('.img');
if (img) show(img);
});
document.getElementById('aa').addEventListener('mouseout', function(e) {
var img = e.target.closest('.img');
if (img) unshow(img);
});
// show/unshow 接收具體元素,不再依賴全域查詢
function show(img) {
var txt = img.nextElementSibling; // 假設結構關聯
if (txt && txt.classList.contains('txt')) {
txt.style.display = 'block';
// 定位邏輯...
}
}
function unshow(img) {
var txt = img.nextElementSibling;
if (txt && txt.classList.contains('txt')) {
txt.style.display = 'none';
}
}
迴圈中綁定事件「只生效最後一個」是 var 函數作用域與閉包捕獲變數參考(非值)導致。現代解法首選 事件委派(效能佳、邏輯清、自動支援動態元素),次選 let(ES6+ 簡單修正)。重構時應從「索引驅動」轉為「元素驅動」,利用 DOM 結構關聯替代脆弱的索引對應。