我正在尝试使用
tic-tac-toe game
和 Webpack
创建一个 Web Component
。
我需要在
div
组件的 cell
中选择具有 shadowRoot
类的 place-mark
标签,然后根据另一个函数计算的 Turn 将 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.addEventListener("click", handleClick, { once: true });});
}
function handleClick(e) {
const cell = e.target;
const currentClass = circleTurn
? CIRCLE_CLASS
: X_CLASS;
placeMark(cell, currentClass);
if (checkWin(currentClass)) {
endGame(false);
} else if (isDraw()) {
endGame(true);
} else {
swapTurns();
setBoardHoverClass();
}
}
function placeMark(cell, currentClass) {
cell.classList.add(currentClass);
}
如果您需要上述代码的完整版本,这是我的项目:
您只需从代码中删除
shadowRoot
即可
它将让您有机会访问所有元素和
querySelect
您想要的东西。
export default class PlaceMark extends HTMLElement {
constructor() {
super();
this.appendChild(template.content.cloneNode(true));}}
window.customElements.define("place-mark", PlaceMark);
全局
querySelector
代码可以不能访问阴影根中内的DOM内容。
如果您有 open
ShadowRoot,您可以执行以下操作:
document.querySelector("my-component").shadowRoot("[data-cell]")
当你有nestedshadowRoots时,这当然会变得很麻烦。
您可能想将逻辑更改为:
tileclicked
事件
<my-board>
监听此事件
colortile
colortile
事件
<my-board></my-board>
<hr>
<my-board></my-board>
<script>
class GameComponent extends HTMLElement {
constructor(html){
super().attachShadow({mode: "open"}).innerHTML = html;
}
dispatch({ from = this,eventname,detail = {} }) {
from.dispatchEvent(new CustomEvent(eventname, {
composed: true, bubbles: true, detail
}));
}
listen({at = this,eventname,func }) {
at.addEventListener(eventname, func);
}
}
customElements.define("my-board", class extends GameComponent {
constructor() {
super(`<style>:host{display:grid;grid:20px/repeat(3,60px);gap:1px}</style>` +
"green,red,blue,orange,olive,tan,teal,yellow,lime"
.split(",").map(color => `<my-tile>${color}</my-tile>`).join(""));
}
connectedCallback() {
this.listen({ at:this, eventname:"tileclicked",
func: (evt) => {
//console.log(evt.target,evt.composedPath());
this.dispatch({ from: this, eventname: "colortile",
detail: { color: evt.detail.color }})}})
}
});
customElements.define("my-tile", class extends GameComponent {
constructor() {
super(`<style>:host{text-align:center;cursor:pointer}</style><slot></slot>`);
}
connectedCallback() {
this.onclick = () => this.dispatch({ from: this, eventname: "tileclicked",
detail: { color: this.innerText }});
this.listen({
at: this.getRootNode().host.closest("my-board"),
eventname: "colortile",
func: (evt) => this.style.background = evt.detail.color });
}
});
</script>
游乐场:https://jsfiddle.net/WebComponents/Lk9r4gx2/
备注:
<my-tile>
上设置shadowDOM;那么
<my-board>
STYLE 可以设置所有图块的样式。你可以使用
MY-BOARD.shadowRoot.querySelectorAll
getRootNode()
然后去escapeshadowRoot,并找到
closest("my-board")
console.log
以了解事件如何重新定位,然后
composedPath
然后保存您的...
detail:{}
属性包中传递函数 reference
...这样 Tile 就可以访问板上的methods,反之亦然。