Skip to content

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

Learn how to make HTML table rows interactive by adding click event listeners using event delegation (best practice) or individual row listeners, with code examples for data access and highlighting.

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');
  }
});

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');
  });
});

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}`);
  }
});

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