如何为表格单元格添加事件监听器

7

我想在表格单元格上添加事件监听器,以便每次单击表格单元格时执行一个函数。

var getDaysInMonth = function (year, month) {
    return new Date(year, month, 0).getDate();
}



var calendar = {
    month: function () {
        var d = new Date();
        return d.getMonth() + this.nextMonth;
    },

    year: function () {
        var y = new Date();
        return y.getFullYear();
    },

    nextMonth: 1,
    
    cellColor: 'white',
    
}



var loopTable = function () {
    var daysInMonth = getDaysInMonth(calendar.year(), calendar.month());
    var table = document.getElementById('myTable');
    var rows = table.rows;
    var l = 1;
    var month = calendar.month();
    var year = calendar.year();
    var firstDay = new Date(year + "-" + month).getDay();
    var currentDay = new Date().getDay();
    var dayOfMonth = new Date().getDate();

 
    for (let i = 1; i < rows.length; i++) {

        if (rows[i] == rows[1]) {

            var k = 1;

            for (let j = firstDay; j < rows[i].cells.length; j++) {

                if (k === dayOfMonth && calendar.nextMonth === 1) {
                    rows[i].cells[j].style.backgroundColor = calendar.cellColor;
                

                }

                if (k <= daysInMonth) {
                    rows[i].cells[j].innerHTML = k;
                    k++
                }

            }
        } else {
            for (let j = 0; j < rows[i].cells.length; j++) {
                if (k === dayOfMonth && calendar.nextMonth === 1) {
                    rows[i].cells[j].style.backgroundColor = calendar.cellColor;
                }
                if (k <= daysInMonth) {
                    rows[i].cells[j].innerHTML = k;
                    k++
                }
            }
        }
    }
}

loopTable();
clickCell();

function monthTitle() {

    var monthsArray = ['Jan.', 'Feb.', 'Mar.', 'Apr.', 'May', 'Jun.', 'Jul.', 'Aug.', 'Sept.', 'Oct.', 'Nov.', 'Dec.'];
    monthNum = calendar.month();
    var monthName = monthsArray[calendar.month() - 1] + '' + calendar.year();
    var title = document.getElementById('calendarTitle');
    var nextArrow = document.getElementById('nxt');
    var leftArrow = document.getElementById('prev');

  
    if (monthName === ('Dec.' + '' + calendar.year())){
        xmas();
    }
    if (monthNum >= 12) {
        nextArrow.className += ' inactiveLink';
    } else if (monthNum <= 1) {
        leftArrow.className += ' inactiveLink';
    } else {
        nextArrow.classList.remove('inactiveLink');
        leftArrow.classList.remove('inactiveLink');
    }

    title.innerHTML = '';
    var titleNode = document.createTextNode(monthName);
    title.appendChild(titleNode);


}
monthTitle();

function nextMonth() {
    clearTable();
    calendar.nextMonth += 1;
    monthTitle();
    loopTable();
}

function previousMonth() {
    clearTable();
    calendar.nextMonth -= 1;
    monthTitle();
    loopTable();
}

function clearTable() {
    var table = document.getElementById('myTable');
    var rows = table.rows;

    for (var i = 1; i < rows.length; i++) {
        cells = rows[i].cells;
        for (var j = 0; j < cells.length; j++) {
            if (cells[j].innerHTML = '') {
                cells[j].style.display = 'none';
            }
            cells[j].innerHTML = '';
            cells[j].style.backgroundColor = '#D9534F';
            cells[j].style.emptyCells = 'hide';
        }
    }
}

var next = document.getElementById('nxt');
var previous = document.getElementById('prev');
var table = document.getElementById('myTable');
var cell = table.rows;
next.addEventListener('click', nextMonth);
previous.addEventListener('click', previousMonth);



function clickCell() {
    var row = document.getElementById('myTable').rows;
    
    for (var i = 0; i < row.length; i++) {
        for (var j = 0; j < row[i].cells.length; j++ ) {
       
            row[i].cells[j].addEventListener('click', function(){
       console.log('click');
            })
        }
    }
}
clickCell();
body {
            background-color: rgb(0, 121, 191);
        }
        
        table {
            width: 50%;
            background-color: #D9534F;
            border: 1px solid white;
            padding: 10px;
            padding-bottom: 20px;
            font-size: 25px;
            border-radius: 25px;
            position: relative;
            margin: auto;
        }
        
        td {
            border: 1px solid white;
            text-align: center;
            font-weight: 600;
            font-size: 20px;
            padding: 20px;
        }
        
        th {
            height: 50px;
        }
        
        .calArrows {
            text-decoration: none;
            color: white;
            font-size: 35px;
        }
        
        #nxt {
            font-size: 30px;
            position: absolute;
            top: 0;
            right: 25%
        }
        
        #prev {
            font-size: 30px;
            position: absolute;
            top: 0;
            left: 25%;
        }
        
        #calendarTitle {
            font-family: 'Indie Flower', cursive;
            font-weight: 600;
            font-size: 25px;
            color: white;
        }
        
        .inactiveLink {
            cursor: not-allowed;
            pointer-events: none;
             
        }
        
        #myTable {
            empty-cells: hide;
        }
        
        .xmasDec {
            width: 90%;
            height: 70%;
            position: absolute;
            top: -10%;
            left: 5%;
        }
        
        #calWraper {
            position: relative;
        }
        
        #myCan {
            position: absolute;
            top: 0;
            left: 10%;
            width: 90%;
            height: 70%;
            opacity: 0, 5;
        }
<body>
    <canvas class="myCan" width="100" height="100"></canvas>
    <div id="calWraper">
        <table id="myTable">
            <caption id="calendarTitle">Test</caption>
            <tr>
                <th>Sun</th>
                <th>Mon</th>
                <th>Tue</th>
                <th>Wed</th>
                <th>Thur</th>
                <th>Fri</th>
                <th>Sat</th>
            </tr>
            <tr>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
            </tr>
            <tr>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
            </tr>
            <tr>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
            </tr>
            <tr>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
            </tr>
            <tr>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
                <td></td>
            </tr>
            <tr>
                <td></td>
                <td></td>
            </tr>
        </table>
        <canvas id="myCan" width="200" height="200" style="background-color: transparent"></canvas>
            <a href="#" id="prev" class="calArrows"><i class="fa fa-arrow-left" ></i></a>
        <a href="#" id="nxt" class="calArrows"><i class="fa fa-arrow-right" ></i></a>



    </div>



</html>

我尝试创建一个函数,它将循环遍历行和单元格,并向每个单元格添加eventListener。但似乎它不起作用,在某些情况下会工作,这是非常奇怪的行为。这是我创建的函数:

function clickCell() {
    var row = document.getElementById('myTable').rows;
 for (var i = 0; i < row.length; i++) {
        for (var j = 0; j < row[i].cells.length; j++ ) {
                    console.log(row[i].cells[j].innerHTML);
            row[i].cells[j].addEventListener('click', function(){
                    console.log('click');
            })
        }
    }
}

1
你的 [mcve] 看起来不是很简洁。你能不能再缩减一下? - Quentin
因为控制台的缘故,我是唯一一个看不到东西的人吗? - Shinra tensei
@Shinratensei:请使用“全页”链接。 - T.J. Crowder
2
不要这样做。相反,将一个事件处理程序附加到表本身,并使用event.target找出单击的是哪个单元格。 - Niet the Dark Absol
我应该向表格添加事件监听器,并使用 e.target 来定位单元格吗? - Christodoulou Andreas
3个回答

12
似乎您的画布与表格重叠。因此,表格中的元素从未被点击过。
您需要向您的画布添加CSS属性pointer-events:none
#myCan {
    ...
   pointer-events: none;
}

这样就不会再阻止表格的点击了。
您也可以更简单地向单元格添加事件监听器:
document.querySelectorAll('#myTable td')
.forEach(e => e.addEventListener("click", function() {
    // Here, `this` refers to the element the event was hooked on
    console.log("clicked")
}));

这会为每个单元格创建一个单独的函数;相反,你可以分享一个函数而不失去任何功能:

function clickHandler() {
    // Here, `this` refers to the element the event was hooked on
    console.log("clicked")
}
document.querySelectorAll('#myTable td')
.forEach(e => e.addEventListener("click", clickHandler));

一些浏览器仍然没有在querySelectorAll返回的HTMLCollection上实现forEach方法,但可以轻松地进行polyfill:

if (!HTMLCollection.prototype.forEach) {
    Object.defineProperty(HTMLCollection.prototype, "forEach", {
        value: Array.prototype.forEach
    });
}

如果你需要支持那些不支持Array.prototype.forEach的过时浏览器,请查看MDN上的polyfill


是的先生,您说得对,画布覆盖在表格上,导致我无法点击单元格。 - Christodoulou Andreas
1
我已经添加了关于forEach的注释。你还应该更改示例,不要使用箭头函数作为事件处理程序,因为在该处理程序中使用this(以引用被点击的td)很可能很重要。但是我没有改变那一部分,不想做太多的修改。 - T.J. Crowder
1
@ChristodoulouAndreas:当然,您完全可以接受任何答案,但真正的问题是上面提到的画布问题,而不是我的答案所解决的事件挂钩问题。如果是这样,您可以考虑将已接受的答案切换到此答案。 - T.J. Crowder
2
@T.J.Crowder,我基本上是在寻找有关循环遍历表格单元格的通用答案(最好的方式是什么),其次是解决我无法单击第一行单元格的问题。 - Christodoulou Andreas
@ChristodoulouAndreas:很酷。FWIW,我对上面的结尾进行了一些编辑;没有必要为每个单元格创建单独的函数。 - T.J. Crowder

9
这是一个关于“事件委托”的案例:在表格(或表格主体)上挂接点击事件,而不是单独的单元格,然后通过查看event.target及其祖先来确定被点击的单元格。
简化示例:
document.querySelector("#my-table tbody").addEventListener("click", function(event) {
  var td = event.target;
  while (td !== this && !td.matches("td")) {
      td = td.parentNode;
  }
  if (td === this) {
      console.log("No table cell found");
  } else {
      console.log(td.innerHTML);
  }
});

实时复制:

document.querySelector("#my-table tbody").addEventListener("click", function(event) {
  var td = event.target;
  while (td !== this && !td.matches("td")) {
      td = td.parentNode;
  }
  if (td === this) {
      console.log("No table cell found");
  } else {
      console.log(td.innerHTML);
  }
});
table, td, th {
  border: 1px solid #ddd;
}
table {
  border-collapse: collapse;
}
td, th {
  padding: 4px;
}
<table id="my-table">
  <thead>
    <tr>
      <th>First</th>
      <th>Last</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Joe</td>
      <td>Bloggs</td>
    </tr>
    <tr>
      <td>Muhammad</td>
      <td>Abdul</td>
    </tr>
    <tr>
      <td>Maria</td>
      <td>Gonzales</td>
    </tr>
  </tbody>
</table>

请注意,您可以使用元素上的新(实验性)closest方法,而不是循环:
var td = event.target.closest("td");

...但是A)它仍然是实验性的,B)它不会在达到tbody时停止,因此理论上如果你有嵌套表格,就会找到错误的单元格。

如果您需要支持不具有Element.prototype.matches的浏览器,在这种特定情况下,您可以使用td.tagName !== "TD"而不是!td.matches("td")(注意大写)。


你能解释一下为什么这比为每个单元格添加点击监听器更好吗? - dgrogan
2
@dgrogan:主要是在添加和删除元素时非常有用,但相对于OP的代码,它之所以有用是因为我们只创建了一个事件处理程序函数,而不是几十个、几百个或成千上万个。但是这个问题也可以通过不使用事件委托来解决(定义一次函数,将其连接数十、数百或成千上万次)。但仍然需要更少的事件注册来完成并跟踪... - T.J. Crowder

1

仅使用DOM对象

这是一个在HTML表格(TicTacToe)上添加单元格事件监听器的示例。可以使用“this”关键字和“querySelectorAll”轻松实现

逻辑在JavaScript文件中:

  1. 首先,使用“querySelectorAll”按其“tag”(“td”)获取所有单元格,并将其保存为列表
  2. 为每个单元格添加事件侦听器,并为要执行的任何操作指定函数名称
  3. 在事件侦听器函数内部,使用此关键字更新单元格内容,或调用其他函数或完成其他任务。

var cells = document.querySelectorAll("td");

for (var cell of cells) {
  cell.addEventListener('click', marker)
}

function marker() {
  if (this.textContent === 'X') {
    this.innerHTML = "O";
  } else if (this.textContent === 'O') {
    this.innerHTML = " ";
  } else {
    this.innerHTML = "X";
  }
}
td {
  text-align: center;
  font-size: 50px
}

table,
th,
td {
  border: 2px solid black;
  width: 300px;
  height: 100px;
}
<!DOCTYPE html>
<html lang="en" dir="ltr">

<head>
  <meta charset="utf-8">

  <title>Tic Tac Toe</title>

</head>

<body>
  <table id="ticTac">
    <tbody>
      <tr>
        <td>. </td>
        <td>. </td>
        <td>. </td>
      </tr>

      <tr>
        <td>. </td>
        <td>. </td>
        <td>. </td>
      </tr>
      <tr>
        <td>. </td>
        <td>. </td>
        <td>. </td>
      </tr>
    </tbody>
  </table>

</body>

</html>


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