You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Web Component开发井字棋:无法选中shadowRoot内的cell元素

问题描述

我正尝试使用Webpack和Web Component开发一款井字棋游戏。需要选中place-mark组件shadowRoot内带有cell类的div标签,并根据回合计算结果,将其innerText更新为O或X。目前逻辑运行正常,但无法选中正确的组件来更新HTML视图,导致用户无法看到选中区域的当前状态。

简化代码

HTML代码

<div class="board" id="board">
    <place-mark data-cell></place-mark>
</div>

组件代码

const template = document.createElement("template");

template.innerHTML = `<div class="cell" />`;

export default class PlaceMark extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: "open" });
        this.shadowRoot.appendChild(
            template.content.cloneNode(true)
        );
    }
}

window.customElements.define(
    "place-mark",
    PlaceMark
);

JS逻辑代码

const X_CLASS = "x";
const CIRCLE_CLASS = "circle";
const cellElements = document.querySelectorAll('[data-cell]');
const board = document.getElementById("board");
let circleTurn;

function startGame() {
    // 修正原语法错误,遍历所有cell元素绑定点击事件
    cellElements.forEach(cell => {
        cell.addEventListener("click", handleClick, { once: true });
    });
}

function handleClick(e) {
    // 确保获取到place-mark组件本身(点击可能触发在内部cell上)
    const placeMarkElement = e.target.closest('[data-cell]') || e.target;
    const currentClass = circleTurn ? CIRCLE_CLASS : X_CLASS;
    placeMark(placeMarkElement, currentClass);
    
    if (checkWin(currentClass)) {
        endGame(false);
    } else if (isDraw()) {
        endGame(true);
    } else {
        swapTurns();
        setBoardHoverClass();
    }
}

function placeMark(cell, currentClass) {
    // 访问组件Shadow DOM内的cell元素
    const innerCell = cell.shadowRoot.querySelector('.cell');
    innerCell.classList.add(currentClass);
    // 根据回合设置对应文本
    innerCell.innerText = currentClass === CIRCLE_CLASS ? 'O' : 'X';
}

关键修正说明

  1. 修复事件绑定错误:原startGame函数存在语法问题,改为遍历所有data-cell元素批量绑定点击事件。
  2. 定位正确组件:点击事件的target可能是组件内部的cell div,通过closest('[data-cell]')确保拿到place-mark组件本身。
  3. 访问Shadow DOM元素:利用open模式的shadowRoot,通过cell.shadowRoot.querySelector('.cell')获取组件内部的cell元素,实现视图更新。
  4. 补充文本更新逻辑:原placeMark函数仅添加样式类,补充设置innerText为O或X,让用户能直观看到选中状态。

如果想更贴合Web Component的封装思想,可以在组件内部添加方法管理标记逻辑:

// 在PlaceMark类中新增方法
setMark(mark) {
    const cell = this.shadowRoot.querySelector('.cell');
    cell.innerText = mark;
    cell.classList.add(mark === 'O' ? CIRCLE_CLASS : X_CLASS);
}

// 修改placeMark函数调用组件方法
function placeMark(cell, currentClass) {
    const mark = currentClass === CIRCLE_CLASS ? 'O' : 'X';
    cell.setMark(mark);
}

内容的提问来源于stack exchange,提问作者Nazanin Naghavi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 21:06:19