Dales Vu
Dales Vu

Reputation: 169

Select a sibling element inside a 'tr' element

I have a table like this:

<table>
    <th>
        <!--the table heading-->
        <td>id</td>
        <td>type</td>
        <td>Price</td>
        <td>examine</td>
    </th>
    <tr>
        <td>0</td>
        <td>Book</td>
        <td>500</td>
        <button>examine</button> <!-- onclick, get the id value 0 -->
    </tr>
    <tr>
        <td>1</td>
        <td>Clothing</td>
        <td>30</td>
        <button>examine</button> <!-- onclick, get the id value 1 -->
    </tr>
    <tr>
        <td>2</td>
        <td>Food</td>
        <td>400</td>
        <button>examine</button> <!-- onclick, get the id value 2 -->
    </tr>
    <!--...
    there are 100 rows-->
    <tr>
        <td>99</td>
        <td>Book</td>
        <td>300</td>
        <button>examine</button> <!-- onclick, get the id value 99 -->
    </tr>
</table>

I want to add an eventListener to the button that when I click the button, it will get the corresponding id value to be passed as an argument in a function. How can I get this done without using JQuery?

Upvotes: 2

Views: 1450

Answers (2)

Ray Toal
Ray Toal

Reputation: 88428

Original Answer

The following worked for me:

document.querySelectorAll('button').forEach(b => {
  b.addEventListener('click', () =>
  alert(b.parentNode.parentNode.firstChild.nextSibling.innerHTML));
});

See https://jsfiddle.net/Luqy0u9m/ for a live demo.

Note that we had to take the second child because the first child of the tr is a text node. You might have to tweak this if the first td came directly after the tr.

Improved Answer

The original answer is bad because it relies on you knowing whether or not there is whitespace between the <tr> and its first <td>. This is not robust because if you change your HTML the JavaScript breaks.

Instead, look not for the first child node of the <tr> but for the first <td>. The best way to do this is with the cells property of the HTMLTableRowElement object (your <tr>). RobG already gave this answer, so I'll give an alternative (though slower) solution:

document.querySelectorAll('button').forEach(b => {
  b.addEventListener('click', () =>
    alert(b.parentNode.parentNode.querySelector('td').innerHTML));
});

It's a pretty compact solution, but it does assume that the your button is directly inside a td which is immediately inside a tr whose first td holds the value you want. Still it is way better than the first answer.

Upvotes: 0

RobG
RobG

Reputation: 147453

To do this robustly, you first need to go up from the button to the ancestor TR (if there is one), then get the text content of the first cell, e.g.

// Starting at el, get ancestor with tagName
// If no such ancestor, return null
function upTo(el, tagName) {
  tagName = tagName.toLowerCase();
  while (el.parentNode && el.parentNode.tagName) {
    el = el.parentNode;
    if (el.tagName.toLowerCase() == tagName) {
      return el;
    }
  }
  return null;
}

// Add click listener to all buttons
window.onload = function() {
  [].forEach.call(document.querySelectorAll('button'),function(button) {
    button.addEventListener('click',function() {
      // Get the ancestor row
      var row = upTo(this, 'tr');
      if (row) {
        // If there is a first cell, log it's textContent
        console.log(row.cells[0] && row.cells[0].textContent);
      }
    },false);
  });
}
<table>
    <tr><th>id<th>type<th>Price<th>examine
    <tr><td>0<td>Book<td>500<td><button>examine</button>
    <tr><td>1<td>Clothing<td>30<td><button>examine</button>
</table>

This adds the listener to all buttons, you might want to restrict that. Also fixed the HTML to put the buttons in side a cell, and added a first row.

Upvotes: 1

Related Questions