添加DOM事件监听器后图片不显示,JS拖拽拼图游戏开发问题
解决JS拖拽拼图游戏中populateBoard失效问题
问题根源
你直接将类的dragStart、dragDrop等方法作为事件监听函数绑定给tile时,函数内部的this会指向触发事件的<img>元素,而非PuzzleGame实例。当这些方法尝试访问实例的currTile、otherTile属性时,会因上下文错误抛出异常,导致populateBoard的循环中断,最终棋盘无法填充。
修复方案一:绑定正确的this上下文
在添加事件监听器时,用bind(this)确保方法内部的this指向当前游戏实例:
populateBoard() { for (let r = 0; r < this.rows; r++) { for (let c = 0; c < this.columns; c++) { let tile = document.createElement("img"); tile.id = r.toString() + "-" + c.toString(); tile.src = "./images/" + this.randomOrder.shift() + ".png"; // 绑定正确的this上下文 tile.addEventListener("dragstart", this.dragStart.bind(this)); tile.addEventListener("dragover", this.dragOver.bind(this)); tile.addEventListener("dragenter", this.dragEnter.bind(this)); tile.addEventListener("dragleave", this.dragLeave.bind(this)); tile.addEventListener("drop", this.dragDrop.bind(this)); tile.addEventListener("dragend", this.dragEnd.bind(this)); document.getElementById("board").append(tile); } } }
同时调整事件方法逻辑,用e.target获取事件对应的tile元素:
dragStart(e) { this.currTile = e.target; // 获取被拖拽的图片元素 } dragDrop(e) { this.otherTile = e.target; // 获取被放置的图片元素 } dragEnd() { let currImg = this.currTile.src; let otherImg = this.otherTile.src; this.currTile.src = otherImg; this.otherTile.src = currImg; }
修复方案二:单独编写assignEvents函数(符合你的推测)
先完成棋盘填充,再遍历所有tile绑定事件,避免填充过程中因事件绑定错误中断:
- 修改
populateBoard,移除事件绑定代码:
populateBoard() { for (let r = 0; r < this.rows; r++) { for (let c = 0; c < this.columns; c++) { let tile = document.createElement("img"); tile.id = r.toString() + "-" + c.toString(); tile.src = "./images/" + this.randomOrder.shift() + ".png"; document.getElementById("board").append(tile); } } }
- 添加
assignEvents方法:
assignEvents() { const tiles = document.querySelectorAll("#board img"); tiles.forEach(tile => { tile.addEventListener("dragstart", this.dragStart.bind(this)); tile.addEventListener("dragover", this.dragOver.bind(this)); tile.addEventListener("dragenter", this.dragEnter.bind(this)); tile.addEventListener("dragleave", this.dragLeave.bind(this)); tile.addEventListener("drop", this.dragDrop.bind(this)); tile.addEventListener("dragend", this.dragEnd.bind(this)); }); }
- 修改
puzzleStart,先填充棋盘再绑定事件:
puzzleStart() { this._randomizeOrder(); this.populateBoard(); this.assignEvents(); // 填充完成后绑定事件 }
事件方法的调整同方案一,用e.target获取对应tile元素。
额外优化点
_randomizeOrder中直接用sort修改原数组this.imageOrder,会导致后续调用时原数组已乱序,建议复制原数组再排序:
_randomizeOrder() { if (!this.imageOrder) return undefined; this.randomOrder = [...this.imageOrder].sort(() => Math.random() - 0.5); return this.randomOrder; }
内容的提问来源于stack exchange,提问作者Jorge Mendez
相关产品推荐
相关产品推荐

