2

I have a table with 10 columns and I want to add one or more event listeners to columns from 5th to 8th by using specifically addEventListener statement (so please no inline events like element.onclick). The table is dynamic, in fact I don't know at programming time how many records the table will have at runtime because it will be populated by data only at that moment.

I already know how to "aim" to a table or to a table row or to a specified table cell to add event listeners to them but unfortunately I don't understand how to add them to certain columns or to a range of columns at once ("at once" means a for loop that loops through every row to get the desired cell of each).

Edit

The table is just a normal table like the following example and it is populated by loading data records from csv file or by adding new records by a form placed into the tfoot of the table itself. Also it is possible to edit the data into the table itself thanks to the contenteditable attribute added (or removed) dynamically by js when needed.

If I want to add an event listener to a row I first get the row object then I add the eventlistener to it once: all td elements will be affected as I need thanks to event propagation/delegation. Is that possible to do the same with columns?

myRow.addEventListener(input, myfunc, false);

In other words is it possible to do something like:

myCol5.addEventListener(input, myfunc, false);
myCol6.addEventListener(mouseover, myfunc, false);
myCol7.addEventListener(click, myfunc, false);
myCol8.addEventListener(input, myfunc, false);

table, td, th
{
  border: 1px solid black;
  border-collapse: collapse;
}
<table>
  <tr>
    <th>Company</th>
    <th>Contact</th>
    <th>Country</th>
  </tr>
  <tr>
    <td>Alfreds Futterkiste</td>
    <td>Maria Anders</td>
    <td>Germany</td>
  </tr>
  <tr>
    <td>Centro comercial Moctezuma</td>
    <td>Francisco Chang</td>
    <td>Mexico</td>
  </tr>
  <tr>
    <td>Ernst Handel</td>
    <td>Roland Mendel</td>
    <td>Austria</td>
  </tr>
  <tr>
    <td>Island Trading</td>
    <td>Helen Bennett</td>
    <td>UK</td>
  </tr>
  <tr>
    <td>Laughing Bacchus Winecellars</td>
    <td>Yoshi Tannamuri</td>
    <td>Canada</td>
  </tr>
  <tr>
    <td>Magazzini Alimentari Riuniti</td>
    <td>Giovanni Rovelli</td>
    <td>Italy</td>
  </tr>
</table>
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
willy wonka
  • 1,440
  • 1
  • 18
  • 31
  • 3
    Possible duplicate of [add event listener on elements created dynamically](http://stackoverflow.com/questions/14258787/add-event-listener-on-elements-created-dynamically) – 4castle Feb 02 '17 at 02:05
  • 1
    You should add some code. And what do you mean by dynamic? Does it change without reloading the page? Need to see the structure of your table (HTML)... also, have you considered using jQuery? It will make a task like this much easier. – Christopher Reid Feb 02 '17 at 02:09
  • 1
    @4castle I don't think that's OP's issue. The main question here appears to be identifying the column – Phil Feb 02 '17 at 02:11
  • @Phil I agree that's likely, but until they try anything, all that makes sense is to close to the canonical. – 4castle Feb 02 '17 at 02:14
  • 1
    add listener to row and figure out from classes or index of cells which column it is. Provide a [mcve] – charlietfl Feb 02 '17 at 02:17
  • @charlietfl I'd add the listener on the `` / ``
    – Phil Feb 02 '17 at 02:18
  • 2
    Trying to do lookups by `nth` position `td` inside a `tr` is going to be troublesome given that you can `rowspan` on a `td`. I would _STRONGLY_ suggest adding a class attribute (ie. `class="myColumn"`) to the `td`'s you're trying to target, and using that for your "aim"ing – haxxxton Feb 02 '17 at 02:28
  • @haxxxton This means I must loop to all td elements of all columns: it is what I'm trying to not do. Suppose if there are thousands of td elements it would be a bit inefficient. – willy wonka Feb 02 '17 at 02:37
  • @4castle Thanks for the response, I looked at the suggested probable duplicate but it is not what I'm looking for. – willy wonka Feb 02 '17 at 02:46
  • @Phil: exactly: The main question here appears to be identifying the target columns like if they were rows... – willy wonka Feb 02 '17 at 15:29

3 Answers3

4

With window.event.target.cellIndex is possible to get the cell index (from 0 to n where n is the number of table columns - 1) of a clicked cell that is the column index.

With window.event.target.parentNode.rowIndex is possible to get the row index (from 0 to n where n is the number of table rows - 1) of a clicked cell that is the row/record index.

Example:

  var col = window.event.target.cellIndex;
  var row = window.event.target.parentNode.rowIndex;

Working example:

document.getElementById('myTbl').addEventListener('click', function(){myFunction(event)}, false);

function myFunction()
{
  var col = window.event.target.cellIndex;
  var row = window.event.target.parentNode.rowIndex;
  alert('Col index is: ' + col + '\nRow index is: ' + row);
  
  // At this point it is easy to do something like:
  // if(col > 5 && col < 8)
  {
    //Execute this stuff 
  }
}
table, td {
    border: 1px solid black;
}
<p>Click on each td element to alert its position in the table row.</p>

<table id="myTbl">
  <tr>
    <td>Click to show cellIndex</td>
    <td>Click to show cellIndex</td>
    <td>Click to show cellIndex</td>
    <td>Click to show cellIndex</td>
  </tr>
  <tr>
    <td>Click to show cellIndex</td>
    <td>Click to show cellIndex</td>
    <td>Click to show cellIndex</td>
    <td>Click to show cellIndex</td>
  </tr>
  <tr>
    <td>Click to show cellIndex</td>
    <td>Click to show cellIndex</td>
    <td>Click to show cellIndex</td>
    <td>Click to show cellIndex</td>
  </tr>
  <tr>
    <td>Click to show cellIndex</td>
    <td>Click to show cellIndex</td>
    <td>Click to show cellIndex</td>
    <td>Click to show cellIndex</td>
  </tr>
</table>
willy wonka
  • 1,440
  • 1
  • 18
  • 31
0

Using the idea from user haxxton about adding classes, you can use event delegation to listen for events on the table cells that match your column requirements.

// sort of like jQuery's .closest
const findClosest = (element, selector, context) => {
  context = context || element.document || element.ownerDocument
  while (element !== context) {
    if (element.matches(selector)) {
      return element
    }
    element = element.parentNode
  }
  return null
}

document.querySelector('table > tbody').addEventListener('click', function(e) {
  const td = findClosest(e.target, 'td.handleClick', this)
  if (td) {
    console.log('Got a click on', td)
  }
}, false)
<!-- adding classes for columns 1 and 3 -->
<table border="1" cellpadding="10">
<tbody>
  <tr>
    <td class="handleClick">
      <span>ONE</span>
    </td>
    <td>TWO</td>
    <td class="handleClick">THREE</td>
  </tr>
  <tr>
    <td class="handleClick">ONE</td>
    <td>TWO</td>
    <td class="handleClick">THREE</td>
  </tr>
</tbody>
</table>

See Element.matches for more information on that method.

Community
  • 1
  • 1
Phil
  • 157,677
  • 23
  • 242
  • 245
0

In some use cases you can use event delegation and then iterate over the TR's children to find the index of the column from there you could trigger a function specific to each column easily and as it's delegated to the table any dynamically created cells will have the event listener ready to go.

This will be fine for simple tables but may not behave if rowspans are involved - classes or attributes may be the better option for tables like that.

document.getElementById('myTable').addEventListener('click', function(e){
  var elem = e.target;
  var tr = closestTag(elem, "TR"); // Find which TR was clicked
  var td = (elem.tagName == "TD") ? elem : closestTag(elem, "TD"); // Find the TD that was clicked
  if(tr && td){
    var column =  TdIndex(e.target, tr); // Figure out which column was clicked 
    console.log("Column Clicked", column);   
  }
});

function closestTag(elem, tag){ // Moves up the DOM tree till it finds <tag> || <body> 
   while(elem.parentNode.tagName != tag && elem.tagName != 'BODY'){
     elem = elem.parentNode;
   }
   return (elem.tagName == 'BODY') ? false : elem.parentNode; 
}

function TdIndex(elem, tr){ // Returns the index of a TD contained in a TR
  for(var i=0; i < tr.children.length; i++){
    if(tr.children[i] == elem) return i;
  }
  return false;
}
<table id="myTable">
  <tr>
    <th>Company</th>
    <th>Contact</th>
    <th>Country</th>
  </tr>
  <tr>
    <td>Alfreds Futterkiste</td>
    <td>Maria Anders</td>
    <td>Germany</td>
  </tr>
  <tr>
    <td>Centro comercial Moctezuma</td>
    <td>Francisco Chang</td>
    <td>Mexico</td>
  </tr>
  <tr>
    <td>Ernst Handel</td>
    <td>Roland Mendel</td>
    <td>Austria</td>
  </tr>
  <tr>
    <td>Island Trading</td>
    <td>Helen Bennett</td>
    <td>UK</td>
  </tr>
  <tr>
    <td>Laughing Bacchus Winecellars</td>
    <td>Yoshi Tannamuri</td>
    <td>Canada</td>
  </tr>
  <tr>
    <td>Magazzini Alimentari Riuniti</td>
    <td>Giovanni Rovelli</td>
    <td>Italy</td>
  </tr>
</table>
Brian
  • 2,822
  • 1
  • 16
  • 19