【问题标题】:Javascript: Checking Surrounding Table CellsJavascript:检查周围的表格单元格
【发布时间】:2015-07-01 01:26:11
【问题描述】:

我正在制作名为 Dots and Boxes 的游戏。

网格上有一堆点:

<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>

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

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

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

一旦你点击第四面,关闭盒子,盒子就会变成点击第四面的玩家的颜色:

目前,玩家必须手动点击该框来更改其颜色。但是,我希望它在盒子周围所有四个边都是黑色时自动用颜色填充盒子。

如何在 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>

【问题讨论】:

  • 目前还没有答案,但您应该真正将您的逻辑与您的标记分开 - 内联事件处理程序的形式很差,而且很多都是一场噩梦。您应该将行为与类相关联,并引用处理程序中的元素。看看this

标签: javascript html css


【解决方案1】:

这相当简单,尽管它需要很好地使用 DOM 遍历。

当您单击一条线时,最多只有 两个 可能的框可以填充。对于水平线,那些是上方和下方的框,需要跳行。对于垂直线,左右。

对于每个框,我们需要确认四行。同样,我们必须为上面和下面的行跳行。

实际上,我大部分时间都重新创建了游戏,因为它很有趣,而且这里有很多东西要学。所以让我们来看看重要的部分。我们将避免这段代码中出现错误的形式,例如直接在元素上设置内联处理程序和样式,我们将让 CSS 类充当控件,尽管更完整的选项是 data-attributes

让我们从两个基本的辅助函数开始。

第一个用于将类似数组的对象转换为实际的数组,因此我们可以对它们使用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。开始看到模式了吗? isFilled 类似于fill,它是我们定义的一个辅助函数,用于测试一行是否具有.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>

【讨论】:

    【解决方案2】:

    你可以使用previousElementSiblingnextElementSibling作为下一个和上一个元素,并结合这里提出的方法:Is it possible to get element's numerical index in its parent node without looping?,你可以得到上面和下面。像这样:

        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 中使用它来适应游戏规则,但是它似乎不起作用:jsfiddle.net/bvc0ta55/1 它给出了未定义函数并且无法读取 indexof 事物的错误.我不确定我是否做错了什么。
    【解决方案3】:

    一般来说,您应该尝试将游戏逻辑与 DOM 分离,并处理游戏对象(数组等)中的所有内容。 但是,在您的情况下,例如,您可以使用 data attributes 为每个感兴趣的元素分配特定值,创建元素数组,然后稍后使用这些数据属性检查框是否被包围。解决这个问题很有趣,所以我通过添加一些东西(创建数组和分配数据属性以及水平/垂直检查)来修改您的代码。 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>

    【讨论】:

      【解决方案4】:

      进行中

      一开始我只是想尝试将逻辑和视图分离,没想到会产生这么多代码:-|无论如何,虽然它远非完美,但我认为初学者可能会在某些方面觉得这很有趣。

      稍微评论一下代码本身,逻辑位于类(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>

      【讨论】:

        猜你喜欢
        • 2020-03-07
        • 1970-01-01
        • 2021-07-06
        • 1970-01-01
        • 1970-01-01
        • 2015-12-31
        • 2020-10-21
        • 1970-01-01
        • 2014-01-16
        相关资源
        最近更新 更多