问题
I'want to add an eventListener to the table cells so each time a table cell is clicked to execute a function .
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>
I tried by creating a function that it will loop through rows and cells and add the eventListener to each . But it seems that its not working , its working on random instances which is really strange behavior . Here is the function i create:
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:
It seems your canvas is overlapping your table. Because of that td
elements in your table are never clicked.
You will need to add CSS property pointer-events:none
to your canvas.
#myCan {
...
pointer-events: none;
}
This way it won't block table from being clicked anymore.
You can also add event listeners to your cells way simpler:
document.querySelectorAll('#myTable td')
.forEach(e => e.addEventListener("click", function() {
// Here, `this` refers to the element the event was hooked on
console.log("clicked")
}));
That creates a separate function for each cell; instead, you could share one function without losing any functionality:
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));
Some browsers still don't have forEach
on the HTMLCollection returned by querySelectorAll
, but it's easily polyfilled:
if (!HTMLCollection.prototype.forEach) {
Object.defineProperty(HTMLCollection.prototype, "forEach", {
value: Array.prototype.forEach
});
}
If you have to support truly obsolete browsers that don't have Array.prototype.forEach
, see the polyfill on MDN.
回答2:
This is a case for event delegation: Hook the click event on the table (or table body), not individual cells, and then determine which cell was clicked by looking at event.target
and its ancestors.
Simplified example:
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);
}
});
Live Copy:
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>
Note that instead of the loop you could use the new (experimental) closest method on elements:
var td = event.target.closest("td");
...but A) It's still experimental, and B) It won't stop when it reaches the tbody
, so in theory if you had nested tables, would find the wrong cell.
If you need to support browsers that don't have Element.prototype.matches, in this specific case you could use td.tagName !== "TD"
instead of !td.matches("td")
(note the capitalization).
回答3:
Using only the DOM objects
Here's an example cell wise event listener added on an HTML table (TicTacToe). It can be achieved easily using 'this' keyword and 'querySelectorAll'
The logic is in the JavaScript file:
- First, get all the cells by their 'tag' ("td") using 'querySelectorAll' and save it as a list
- Add an event listener to each of the cells, and give a function name to do whatever you want
- Inside the event listener function, using this keyword update the cell content, or call other functions or do whatever task you have to complete.
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>
来源:https://stackoverflow.com/questions/46341171/how-to-addeventlistener-to-table-cells