Skip to main content

How to Add a Click Event Listener to Table Rows in JavaScript

Making table rows clickable is a common requirement for creating interactive data tables. When a user clicks on a row, you might want to highlight it, navigate to a details page, or display more information about that specific record.

This guide will teach you the two primary methods for adding click event listeners to table rows. You will learn the modern and most efficient event delegation technique, which uses a single event listener on the table itself. You will also learn the more direct approach of adding an event listener to each individual row.

Goal: Making Each Table Row Interactive

Given a standard HTML table, we want to execute a piece of JavaScript code whenever a user clicks on any of the <tr> (table row) elements.

The HTML:

<table id="data-table">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr>
<td>101</td>
<td>Alice</td>
</tr>
<tr>
<td>102</td>
<td>Bob</td>
</tr>
</tbody>
</table>

Event delegation is the most performant and scalable technique. Instead of attaching a separate listener to every single row, you add one single listener to the parent <table> (or <tbody>) element. The listener then uses the event.target property to determine which row was actually clicked.

The logic:

  1. Add a click event listener to the table's <tbody>.
  2. Inside the listener, check if the clicked element (event.target) is a cell (<td>).
  3. If it is, find its parent <tr> element.
  4. Perform your action on that row.

The solution:

const tableBody = document.querySelector('#data-table tbody');

tableBody.addEventListener('click', (event) => {
// Find the closest parent <tr> element of the clicked element
const row = event.target.closest('tr');

// If a row was found, it means the click was inside the table body
if (row) {
console.log('You clicked a row!');

// Add a class to highlight the clicked row
row.classList.toggle('highlight');
}
});
note

This is the best practice because it's efficient and works even if rows are dynamically added to or removed from the table later.

Method 2: Adding an Event Listener to Each Row

For a small, static number of rows, you can add an event listener to each <tr> element individually.

The logic:

  1. Select all the <tr> elements using querySelectorAll().
  2. Loop through the resulting NodeList using forEach().
  3. In each iteration, add a click event listener directly to that row element.

The solution:

const rows = document.querySelectorAll('#data-table tbody tr');

rows.forEach(row => {
row.addEventListener('click', () => {
console.log('You clicked a row!');

// Here, 'this' refers to the row that was clicked
this.classList.toggle('highlight');
});
});
note

This method is simpler to understand for beginners but is less performant if you have a very large number of rows.

How to Access Data from the Clicked Row

Once you have a reference to the clicked row, you can easily access the content of its cells (<td>).

The problem

  • You have captured the clicked <tr> and now you need to get the "ID" and "Name" from its cells.

The solution:

const tableBody = document.querySelector('#data-table tbody');

tableBody.addEventListener('click', (event) => {
const row = event.target.closest('tr');
if (!row) return; // Exit if the click was not on a row

// Find all cells within the clicked row
const cells = row.querySelectorAll('td');

if (cells.length > 0) {
const id = cells[0].textContent; // First cell's text
const name = cells[1].textContent; // Second cell's text

console.log(`You clicked on the row for user ID: ${id}, Name: ${name}`);
}
});
note

A better practice is to store this data in data-* attributes on the row itself for easier access: <tr data-user-id="101">...</tr>.

Practical Example: A "Selectable and Highlightable" Table

This script allows a user to click on any row to select it. The selected row gets a unique "active" class. To ensure only one row is active at a time, we first remove the class from all other rows.

The HTML and CSS code:

<style>
#user-table tbody tr.active {
background-color: lightblue;
font-weight: bold;
}
</style>

<table id="user-table">
<tbody>
<tr data-user-id="101"><td>Alice</td></tr>
<tr data-user-id="102"><td>Bob</td></tr>
<tr data-user-id="103"><td>Charlie</td></tr>
</tbody>
</table>

The JavaScript code:

const userTable = document.getElementById('user-table');

userTable.addEventListener('click', (event) => {
const clickedRow = event.target.closest('tr');
if (!clickedRow) return;

// First, find and remove the 'active' class from any currently active row
const currentlyActiveRow = userTable.querySelector('tr.active');
if (currentlyActiveRow) {
currentlyActiveRow.classList.remove('active');
}

// Then, add the 'active' class to the row that was just clicked
clickedRow.classList.add('active');

const userId = clickedRow.dataset.userId;
console.log(`Selected user ID: ${userId}`);
});

Conclusion

Adding click events to table rows is a fundamental technique for building interactive data displays.

  • Event delegation (adding one listener to a parent like <tbody>) is the most efficient and recommended method. It is scalable and handles dynamic content gracefully.
  • Adding a listener to each row individually is a simpler alternative that works well for a small, fixed number of rows.
  • Once you have the clicked row (e.g., from event.target.closest('tr')), you can access its cell data with row.querySelectorAll('td') or, preferably, read from its data-* attributes.