检查周围的表格单元格

9
我正在制作一个名为“点与方块”的游戏。
在网格上有一堆点: enter image description here
<table>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
</table>

当你点击盒子的一侧时,它会变成黑色:

enter image description here enter image description here

function addLine(obj) {
            console.log("Called")

            if (obj.style.backgroundColor != "black") {
                obj.style.backgroundColor = "black";
                changeTurn()
            }

一旦您点击第四个边,关闭盒子,盒子会变成点击第四个边的玩家的颜色: enter image description here 目前,玩家必须手动点击方框以更改其颜色。但是,当盒子周围的四个边都为黑色时,我希望它可以自动填充盒子的颜色。
如何使用Javascript函数检查方框上方、下方、左侧和右侧的线是否填充为黑色?

var playerTurn = "Blue";
            changeTurn();
            var number = 0;

            function addLine(obj) {
                console.log("Called")

                if (obj.style.backgroundColor != "black") {
                    obj.style.backgroundColor = "black";
                    changeTurn()
                }
            }
            
            function fillBox(obj) {
                if (playerTurn == "Blue") {
                    obj.style.backgroundColor = "red";
                }
                else if ( playerTurn == "Red") {
                    obj.style.backgroundColor = "blue";
                }
            }

            function changeTurn() {
                if (playerTurn == "Red") {
                    playerTurn = "Blue";
                    document.getElementById('turn').style.color = "blue";

                }
                else if (playerTurn == "Blue") {
                    playerTurn = "Red";
                    document.getElementById('turn').style.color = "red";
                };
                console.log(playerTurn);
                document.getElementById('turn').innerHTML = playerTurn + "'s Turn";
            }
h3 {
    font-family: Arial;
}
table {
    border-collapse: collapse;
}
.vLine {
    width: 10px;
    height: 60px;
}
.box {
    width: 60px;
    height: 60px;
}
.hLine {
    width: 60px;
    height: 10px;
}
.gap {
    width: 10px;
    height: 12px;
    background-color: black;
}
.vLine:hover, .hLine:hover {
    background-color: black;
}
<h3 id="turn"></h3>

<table>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
</table>


3
目前还没有回答,但是你应该将逻辑与标记分开 - 内联事件处理程序不好,太多了会让你噩梦连连。你应该将行为与类关联,并在处理程序内引用元素。请参考此链接 - Oka
4个回答

3
这很简单,但需要良好的DOM遍历技巧。
当你点击一条线时,最多只有两个可能要填充的方框。对于水平线来说,这些是上下的方框,需要跳行。对于垂直线来说,是左右的方框。
对于每个这样的方框,我们需要确认四条线。同样地,我们必须跳行以处理上下的线。
我实际上已经重新创建了这个游戏,因为它很有趣,而且这里有很多可以学习的东西。所以让我们来看看重要的部分。在这段代码中,我们将避免使用不良形式,例如内联处理程序和直接在元素上设置样式,并让CSS类作为控件,尽管更完整的选项是使用data属性。
让我们从两个基本的辅助函数开始。
第一个用于将类似数组的对象转换为实际的数组,以便我们可以在它们上面使用Array.prototype方法。我们主要在nodeList对象上使用它。
function toArray (o) {
  return Array.prototype.slice.call(o);
}

第二种方法利用第一种方法,可以给出在其parentNode中的元素位置。
function nodeIndex (node) {
  return toArray(node.parentNode.children).indexOf(node);
}

接下来,我们来看一下事件处理程序。它们非常相似。当我们点击一条线时,我们使用另一个辅助函数 fill 填充该行,这会将一个名为 .filled 的新类添加到该行中。然后,我们检查可以填充的框。对于水平线,这是上方和下方的垂直框;对于垂直线,这是左侧和右侧的水平框。我们删除事件侦听器,以便每条线只能被点击一次。

function addHLine (e) {
  fill(this);
  this.removeEventListener(e.type, addHLine);
  checkVerticalBoxes(this);
  updateGame();
}

function addVLine (e) {
  fill(this);
  this.removeEventListener(e.type, addVLine);
  checkHorizontalBoxes(this);
  updateGame();
}

现在我们来看一下checkHorizontalBoxescheckVerticalBoxes。水平的框位于同一行,从我们的起始行索引为+/-1。垂直框位于我们的起始行索引为+/-1的行中,并且与该行共享相同的索引。由于有时可能会超出范围,我们需要一些if语句来判断可能的行或框是否越界。
function checkHorizontalBoxes (line) {
  var left = line.previousElementSibling,
      right = line.nextElementSibling;

  if (left) checkSurroundingLines(left);
  if (right) checkSurroundingLines(right);
}

function checkVerticalBoxes (line) {
  var index = nodeIndex(line),
      up = line.parentNode.previousElementSibling,
      down = line.parentNode.nextElementSibling;

  if (up) checkSurroundingLines(up.children[index]);
  if (down) checkSurroundingLines(down.children[index]);
}

这两个函数都会在潜在的盒子上调用checkSurroundingLines方法。该函数又调用了两个函数 - checkVerticalLinescheckHorizontalLines。开始看到规律了吗?isFilledfill类似,它是一个我们定义的辅助函数,用于测试一条线是否有.filled类。

function checkHorizontalLines (node, idx) {
  var left = node.previousElementSibling,
      right = node.nextElementSibling;

  return isFilled(left) && isFilled(right);
}

function checkVerticalLines (node, idx) {
  var row = node.parentNode,
      up = row.previousElementSibling.children[idx],
      down = row.nextElementSibling.children[idx];

  return isFilled(up) && isFilled(down);
}

如果这两个返回值都是true,我们就填充我们的框。 这是这种DOM遍历逻辑的基本运行方式。实际代码中还有一些额外的东西,所以请阅读所有部分。以下是工作游戏:DEMO

// Player objects

function Player (name, className, scoreBox) {
  this.name = name;
  this.className = className;
  this.scoreBox = scoreBox;
  this.score = 0;
}

// State

var players = [new Player('Blue', 'blue-mark', 'p1'),
            new Player('Red', 'red-mark', 'p2')],
    currentPlayerIdx = 0,
    currentPlayer = players[currentPlayerIdx],
    turnBox = document.getElementById('turn');

// Helpers

function toArray (o) {
  return Array.prototype.slice.call(o);
}

function nodeIndex (node) {
  return toArray(node.parentNode.children).indexOf(node);
}

function fill (el) {
  el.classList.add('filled');
}

function isFilled (el) {
  return el.classList.contains('filled');
}

// Checks

function checkHorizontalLines (node, idx) {
  var left = node.previousElementSibling,
      right = node.nextElementSibling;
  
  return isFilled(left) && isFilled(right);
}

function checkVerticalLines (node, idx) {
  var row = node.parentNode,
      up = row.previousElementSibling.children[idx],
      down = row.nextElementSibling.children[idx];
      
  return isFilled(up) && isFilled(down);
}

function checkSurroundingLines (node) {
  var idx = nodeIndex(node),
      surrounded = checkVerticalLines (node, idx) && checkHorizontalLines(node, idx);
  
  if (surrounded) {
    node.classList.add('marked');
    node.classList.add(currentPlayer.className);
    return true;
  } 
}

function checkHorizontalBoxes (line) {
  var left = line.previousElementSibling,
      right = line.nextElementSibling;
  
  if (left) checkSurroundingLines(left);
  if (right) checkSurroundingLines(right);
}

function checkVerticalBoxes (line) {
  var index = nodeIndex(line),
      up = line.parentNode.previousElementSibling,
      down = line.parentNode.nextElementSibling;
  
  if (up) checkSurroundingLines(up.children[index]);
  if (down) checkSurroundingLines(down.children[index]);
}


// State sets

function setInfo () {
  turnBox.className = currentPlayer.className;
  turnBox.innerHTML = currentPlayer.name + "'s Turn";
  
  players.forEach(function (p) {
    document.getElementById(p.scoreBox).innerHTML = p.score;
  });
}

function getScores() {
  players.forEach(function (p) {
    p.score = document.querySelectorAll('.box.marked.'+p.className).length;
  });
}

function changeTurn () {
  currentPlayerIdx = 1 - currentPlayerIdx;
  currentPlayer = players[currentPlayerIdx];
  
  setInfo();
}

function updateGame() {
  getScores();
  changeTurn();
}

// Events

function addHLine (e) {
  fill(this);
  this.removeEventListener(e.type, addHLine);
  checkVerticalBoxes(this);
  updateGame();
}

function addVLine (e) {
  fill(this);
  this.removeEventListener(e.type, addVLine);
  checkHorizontalBoxes(this);
  updateGame();
}

function assignHandler (sel, ev, fn) {
  var els = document.querySelectorAll(sel);
  toArray(els).forEach(function (el) {
    el.addEventListener(ev, fn);
  });
}

assignHandler('.hLine', 'click', addHLine);
assignHandler('.vLine', 'click', addVLine);


setInfo();
h3 {
  font-family: Arial;
}
table {
  border-collapse: collapse;
}
.vLine {
  width: 10px;
  height: 60px;
}
.box {
  width: 60px;
  height: 60px;
}
.hLine {
  width: 60px;
  height: 10px;
}
.gap {
  width: 10px;
  height: 12px;
  background-color: #333;
}
.vLine:not(.filled):hover,
.hLine:not(.filled):hover {
  background-color: #333;
}

.filled {
  background-color: grey;
}

.marked {
  background-color: green;
}

.box.blue-mark {
  background-color: #3355ff;
}

.box.red-mark {
  background-color: #ff5533;
}

#turn.blue-mark,
#p1 {
  color: #3355ff;
}

#turn.red-mark,
#p2 {
  color: #ff5533;
}
<h3 id="turn"></h3>
<p class="counts">
  <span id="p1"></span> -
  <span id="p2"></span>
</p>

<table>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
  <tr>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
  </tr>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
  <tr>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
  </tr>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
  <tr>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
  </tr>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
  <tr>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
  </tr>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
  <tr>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
  </tr>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
  <tr>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
    <td class="box"></td>
    <td class="vLine"></td>
  </tr>
  <tr>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
    <td class="hLine"></td>
    <td class="gap"></td>
  </tr>
</table>


1
您可以使用previousElementSiblingnextElementSibling来获取前一个和后一个元素,并结合此处提出的方法:在不循环的情况下获取元素在其父节点中的数字索引是否可能?,可以获取上方和下方。像这样:
    var indexPos = Array.prototype.indexOf.call(obj.parentNode.children, obj);
    above = obj.parentNode.previousElementSibling.children[indexPos];
    below = obj.parentNode.nextElementSibling.children[indexPos]
    next = obj.nextElementSibling;
    previous = obj.previousElementSibling;

你可以在这里看到,点击一个元素会使周围的元素变成绿色。

https://jsfiddle.net/bvc0ta55/

它没有处理错误,例如如果周围有缺失的元素,但由于您的正方形元素都被包围,您不应该遇到这个问题。


我尝试在JSFiddle中使用此代码以符合游戏规则,但似乎无法正常工作:https://jsfiddle.net/bvc0ta55/1/ 它显示函数未定义的错误,并且无法读取indexof。虽然我不确定是否做错了什么。 - JoshK

0
一般来说,您应该尝试将游戏逻辑与DOM分离,并在游戏对象(数组等)中处理所有内容。 然而,在您的情况下,例如,您可以使用数据属性为每个感兴趣的元素分配特定值,创建元素数组,然后稍后使用这些数据属性来检查方框是否被包围。这是一个有趣的解决方案,所以我通过添加一些东西(创建数组和分配数据属性以及水平/垂直检查)修改了您的代码。Jsfiddle: https://jsfiddle.net/pisamce/1ws3oyfe/ 我还修改了您的addLine函数以调用检查函数:
//check if box is surrounded
var row = +obj.dataset.row;
var col = +obj.dataset.col;
var type = obj.dataset.type;
if(type === 'h'){
    checkHorizontal(row, col);
}
else if(type === 'v'){
    checkVertical(row, col);
}

var bgcolor = 'black';
var hlines = document.getElementsByClassName('hLine');
for(var i=0;i<7;i++){
    for(var j=0;j<6;j++){
        hlines[j+i*6].setAttribute('data-row', i);
        hlines[j+i*6].setAttribute('data-col', j);
        hlines[j+i*6].setAttribute('data-type', 'h');
    }
}
var vlines = document.getElementsByClassName('vLine');
for(var i=0;i<6;i++){
    for(var j=0;j<7;j++){
        vlines[j+i*7].setAttribute('data-row', i);
        vlines[j+i*7].setAttribute('data-col', j);
        vlines[j+i*7].setAttribute('data-type', 'v');
    }
}
var boxes = document.getElementsByClassName('box');
for(var i=0;i<6;i++){
    for(var j=0;j<6;j++){
        boxes[j+i*6].setAttribute('data-row', i);
        boxes[j+i*6].setAttribute('data-col', j);
        boxes[j+i*6].setAttribute('data-type', 'b');
    }
}
function checkHorizontal(row, col){
    //check up
    if(row > 0
       && hlines[(row-1)*6+col].style.backgroundColor === bgcolor
       && hlines[row*6+col].style.backgroundColor === bgcolor
       && vlines[(row-1)*7+col].style.backgroundColor === bgcolor
       && vlines[(row-1)*7+col+1].style.backgroundColor === bgcolor){
           fillBox(boxes[(row-1)*6+col]);
    }
    //check down
    if(row < 6
       && hlines[(row+1)*6+col].style.backgroundColor === bgcolor
       && hlines[row*6+col].style.backgroundColor === bgcolor
       && vlines[row*7+col].style.backgroundColor === bgcolor
       && vlines[row*7+col+1].style.backgroundColor === bgcolor){
           fillBox(boxes[row*6+col]);
    }
}
function checkVertical(row, col){
    //check left
    if(col > 0
       && hlines[row*6+col-1].style.backgroundColor === bgcolor
       && hlines[(row+1)*6+col-1].style.backgroundColor === bgcolor
       && vlines[row*7+col].style.backgroundColor === bgcolor
       && vlines[row*7+col-1].style.backgroundColor === bgcolor){
           fillBox(boxes[row*6+col-1]);
    }
    //check right
    if(col < 5
       && hlines[row*6+col].style.backgroundColor === bgcolor
       && hlines[(row+1)*6+col].style.backgroundColor === bgcolor
       && vlines[row*7+col+1].style.backgroundColor === bgcolor
       && vlines[row*7+col].style.backgroundColor === bgcolor){
           fillBox(boxes[row*6+col]);
    }
}


var playerTurn = "Blue";
            changeTurn();
            var number = 0;

            function addLine(obj) {
                console.log("Called")
                
                if (obj.style.backgroundColor != "black") {
                    obj.style.backgroundColor = "black";
                    changeTurn()
                }
                
                //check if box is surrounded
                var row = +obj.dataset.row;
                var col = +obj.dataset.col;
                var type = obj.dataset.type;
                if(type === 'h'){
                    checkHorizontal(row, col);
                }
                else if(type === 'v'){
                    checkVertical(row, col);
                }
            }
            
            function fillBox(obj) {
                if (playerTurn == "Blue") {
                    obj.style.backgroundColor = "red";
                }
                else if ( playerTurn == "Red") {
                    obj.style.backgroundColor = "blue";
                }
            }

            function changeTurn() {
                if (playerTurn == "Red") {
                    playerTurn = "Blue";
                    document.getElementById('turn').style.color = "blue";

                }
                else if (playerTurn == "Blue") {
                    playerTurn = "Red";
                    document.getElementById('turn').style.color = "red";
                };
                console.log(playerTurn);
                document.getElementById('turn').innerHTML = playerTurn + "'s Turn";
            }
h3 {
    font-family: Arial;
}
table {
    border-collapse: collapse;
}
.vLine {
    width: 10px;
    height: 60px;
}
.box {
    width: 60px;
    height: 60px;
}
.hLine {
    width: 60px;
    height: 10px;
}
.gap {
    width: 10px;
    height: 12px;
    background-color: black;
}
.vLine:hover, .hLine:hover {
    background-color: black;
}
<h3 id="turn"></h3>

<table>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
    <tr>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
        <td class="box" onclick="fillBox(this)"></td>
        <td class="vLine" onclick="addLine(this)"></td>
    </tr>
    <tr>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
        <td class="hLine" onclick="addLine(this)"></td>
        <td class="gap"></td>
    </tr>
</table>


0

正在进行中

一开始我只是想尝试将逻辑与视图分离,没想到会产生这么多的代码 :-| 无论如何,虽然它还远远不完美,但我认为初学者可能会从中发现一些有趣的东西。

关于代码本身的评论,逻辑位于类中(JavaScript部分的前半部分),而视图则是杂乱无章的函数集合(JavaScript部分的后半部分)。Cell类及其子类(EdgeCell和CoreCell)代表矩形。边缘单元格是可点击的,核心单元格是最大的单元格。

var GameError = extend(Error, {
    ctor: function () {
        Error.apply(this, arguments);
    }
});

var Game = extend(Object, {
    ctor: function () {
        var size = prompt('Board size: ', 3) || 3;
        this.board = new Board(size);
        this.currentPlayer = 1;
        this.players = [
            new Player(1, '#05A'),
            new Player(2, '#A11')
        ];
    },
    getBoard: function () {
        return this.board;
    },
    getCurrentPlayer: function () {
        return this.players[this.currentPlayer - 1];
    },
    nextTurn: function () {
        this.currentPlayer = this.currentPlayer ^ 3;
    },
    select: function (edge) {
        var coreCells, player;
        edge.switchOn();
        player = this.getCurrentPlayer();
        coreCells = this.board.getCoreCells(edge);
        each(coreCells, function (i, cell) {
            if (cell) {
                cell.incr();
                if (cell.isDone()) {
                    player.incrScore();
                }
            }
        });
    }
});

var Board = extend(Object, {
    ctor: function (width) {
        this.width = width;
        this.cells = this.makeCells();
    },
    getWidth: function () {
        return this.width;
    },
    getCell: function (i, j) {
        try { return this.cells[i][j]; }
        catch (e) { return null; }
    },
    makeCells: function () {
        var l = this.width * 2 + 1;
        var rows = new Array(l);
        loop.call(this, l, function (i) {
            var min = (i % 2) * 2 + 1;
            rows[i] = new Array(l);
            loop.call(this, l, function (j) {
                rows[i][j] = this.createCell(
                    min + j % 2, [i, j]
                );
            });
        });
        return rows;
    },
    createCell: function (type, idx) {
        var ctor;
        switch (type) {
            case Cell.types.CORNER: ctor = Cell; break;
            case Cell.types.H_EDGE: ctor = EdgeCell; break;
            case Cell.types.V_EDGE: ctor = EdgeCell; break;
            case Cell.types.CORE: ctor = CoreCell; break;
            default: throw new Error('Cell type not valid.');
        }
        return new ctor(type, idx);
    },
    getCoreCells: function (edge) {
        var i, j;
        i = edge.getRow();
        j = edge.getColumn();
        return i % 2 ? [
            this.getCell(i, j - 1),
            this.getCell(i, j + 1)
        ] : [
            this.getCell(i - 1, j),
            this.getCell(i + 1, j)
        ];
    }
});

var Player = extend(Object, {
    ctor: function (num, color) {
        this.num = num;
        this.color = color;
        this.score = 0;
    },
    getNum: function () {
        return this.num;
    },
    getColor: function () {
        return this.color;
    },
    getScore: function () {
        return this.score;
    },
    incrScore: function () {
        return this.score++;
    },
    toString: function () {
        return (
            '<span style="color:' + this.color + '">' +
                'Player ' + this.num +
            '</span>'
        );
    }
});

var Cell = extend(Object, {
    ctor: function (type, index) {
        this.type = type;
        this.index = index;
    },
    getType: function () {
        return this.type;
    },
    getIndex: function () {
        return this.index;
    },
    getRow: function () {
        return this.index[0];
    },
    getColumn: function () {
        return this.index[1];
    },
    toString: function () {
        return (
            Cell.names[this.type - 1]
        ) + (
            ' (' + this.index + ')'
        );
    }
});

Cell.types = {
    CORNER: 1,
    H_EDGE: 2,
    V_EDGE: 3,
    CORE: 4
};

Cell.names = [
    'corner',
    'h-edge',
    'v-edge',
    'core'
];

var EdgeCell = extend(Cell, {
    ctor: function (type, index) {
        Cell.call(this, type, index);
        this.on = false;
    },
    isOn: function () {
        return this.on;
    },
    switchOn: function () {
        if (!this.isOn()) this.on = true;
        else throw new GameError(this + ' already on.');
    }
});

var CoreCell = extend(Cell, {
    ctor: function (type, index) {
        Cell.call(this, type, index);
        this.count = 0;
    },
    isDone: function () {
        return this.count === 4;
    },
    incr: function () {
        if (!this.isDone()) this.count++;
        else throw new GameError(this + ' already done.');
    }
});

onload = function () {
    var game = new Game();
    var boardEl = makeBoardDom(game.getBoard());
    document.body.appendChild(boardEl);
    setupListeners(game);
    refreshLog(game);
};

function makeBoardDom (board) {
    var w = board.getWidth() * 2 + 1;
    var boardEl = document.createElement('div');
    boardEl.setAttribute('id', 'board');
    loop(w, function (i) {
        var rowEl = document.createElement('div');
        rowEl.setAttribute('class', 'row');
        boardEl.appendChild(rowEl);
        loop(w, function (j) {
            var cell = board.getCell(i, j);
            var cellEl = document.createElement('div');
            cellEl.setAttribute('class', Cell.names[cell.getType() - 1]);
            rowEl.appendChild(cellEl);
        });
    });
    return boardEl;
}

function setupListeners (game) {
    document.addEventListener('click', function (ev) {
        if (ev.target.className.indexOf('edge') + 1) {
            onEdgeClicked(game, ev.target);
        }
    });
}

function onEdgeClicked (game, edgeEl) {
    var idx, edge, coreCells, board;
    idx = getIndex(edgeEl);
    board = game.getBoard();
    edge = board.getCell.apply(board, idx);
    if (!edge.isOn()) {
        game.select(edge);
        edgeEl.className += ' on';
        coreCells = board.getCoreCells(edge);
        each(coreCells, function (i, cell) {
            if (cell && cell.isDone()) {
                refreshCoreCell(game, cell);
            }
        });
        game.nextTurn();
        refreshLog(game);
    }
}

function refreshCoreCell (game, cell) {
    var boardEl, rowEl, cellEl, player;
    player = game.getCurrentPlayer();
    boardEl = document.getElementById('board');
    rowEl = boardEl.childNodes[cell.getRow()];
    cellEl = rowEl.childNodes[cell.getColumn()];
    cellEl.style.background = player.getColor();
}

function refreshLog (game) {
    var turnEl = document.getElementById('turn');
    var scoresEl = document.getElementById('scores');
    var players = game.players.slice();
    players[0] += ': ' + players[0].getScore();
    players[1] += ': ' + players[1].getScore();
    turnEl.innerHTML = 'Turn: ' + game.getCurrentPlayer();
    scoresEl.innerHTML = players.join('<br />');
}

function getIndex (el) {
    var rowEl = el.parentNode;
    var boardEl = rowEl.parentNode;
    var indexOf = Array.prototype.indexOf;
    var j = indexOf.call(rowEl.childNodes, el);
    var i = indexOf.call(boardEl.childNodes, rowEl);
    return [i, j];
}

function each (list, fn) {
    var i, n = list.length;
    for (i = 0; i < n; i++) {
        fn.call(this, i, list[i]);
    }
}

function loop (n, fn) {
    var i = 0;
    while (i < n) {
        fn.call(this, i++);
    }
}

function extend (parent, proto) {
    var ctor = proto.ctor;
    delete proto.ctor;
    ctor.prototype = Object.create(parent.prototype);
    ctor.prototype.constructor = ctor;
    for (var k in proto) ctor.prototype[k] = proto[k];
    return ctor;
}
.row div {
    float: left;
}

.row::after {
    content: " ";
    display: block;
    clear: both;
}

.corner {
    width: 20px;
    height: 20px;
    background: #333;
}

.h-edge {
    width: 50px;
    height: 20px;
}

.v-edge {
    width: 20px;
    height: 50px;
}

.v-edge:hover,
.h-edge:hover {
    cursor: pointer;
    background: #999;
}

.v-edge.on,
.h-edge.on {
    cursor: pointer;
    background: #333;
}

.core {
    width: 50px;
    height: 50px;
}

#turn, #scores {
    font: normal 16px Courier;
    margin-bottom: .5em;
}
<div id="scores"></div>
<div id="turn"></div>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接