6

jQuery v1.11

Given an HTML table with 6 columns, I want the cells in the table in columns two, three, five and six to respond to click events. So if a user clicks on a cell in column one or four, the click event handler should not be called.

This prevents the event handler from being called when the user clicks in the first column:

 $('#my-table').on('click', 'tbody td:not(:first-child)', function (e) {
    alert("I've been clicked on!");
 });

And his prevents the event handler from being called when the user clicks in column 4:

 $('#my-table').on('click', 'tbody td:not(:nth-child(4))', function (e) {
     alert("I've been clicked on!");
 });

My question is, how do I modify the above so that the event handler is not called when a click occurs in either column one or four.

JSFiddle

Edit: @micnil answered my specific question and I will find knowing the pattern he suggested useful. However, @Oleg took the time to point out a better approach. Rather than binding the event handler to each cell, he suggested that I should bind an event handler to the table. In my case this proves to be better.

Using performance.now(), discussed here, I get the following results setting up the binding for a jQuery DataTable containing 1,000 rows in Chrome:

 Binding the click event to cells took 0.14627581768183972 milliseconds.

 Binding the click event to the table took 0.04619236347855349 milliseconds.
Community
  • 1
  • 1
Karl
  • 1,814
  • 1
  • 25
  • 37

4 Answers4

5

You can just put a coma inside the selector:

 $('#my-table').on('click', 'tbody td:not(:nth-child(4), :first-child)', function (e) {
     alert("I've been clicked on!");
 });
micnil
  • 4,705
  • 2
  • 28
  • 39
  • This one is the best, clean and simple. – Mouser Aug 02 '15 at 10:27
  • So that's the pattern. I played with it for a while and couldn't figure it out. This is what I was after. This avoids binding an event handler to the two columns in question. The other answers attach an event handler to all columns and then ignores the event if an undesired column was clicked on. – Karl Aug 02 '15 at 10:30
2

I think the best choice in your case is to use the JQuery function index() that will give you the index of clicked td and you can do the condition you want based to the returned index, take a look at Your updated fiddle.

JS :

 $('#my-table').on('click', 'tbody td', function () {

     if($(this).index() < 4){ //click in td  between 1 and 4
         alert('td between 1 and 4 clicked');
     }else{ //click in another td
         alert('td between 5 and 6 clicked');   
     }

 });

Hope that help.

Zakaria Acharki
  • 66,747
  • 15
  • 75
  • 101
2

It's important to understand, that the code like $('#my-table').on('click', 'tbody td:not(:first-child)', function (e) {...}); creates first jQuery wrapper with all <td> element which corresponds 'tbody td:not(:first-child)' selector and then bind the event handler separately to every from DOM elements in jQuery object.

I would recommend you to choose another way. You can make one binding of click on the whole <table>. The event bubbling will forward the click on the cell to the parent <tr> and later to the <table>. It's important that e.target get your the clicked <td>.

So the code could be the following:

var columnIndexesIgnore = [0, 3];
$('#my-table').on('click', function (e) {
    var $td = $(e.target).closest("td"); // e.target can be <span> instead of <td>
    if ($td.length > 0 && $.inArray($td[0].cellIndex, columnIndexesIgnore) < 0) {
        // cellIndex is 0-based index. We display in alert 1-based column index
        alert("I've been clicked on column " + ($td[0].cellIndex + 1) + "!");
    }
});

I used cellIndex property of DOM of <td>. It's 0-based index of column of the <td> element. So you need ignore clicks if $td[0].cellIndex is 0 or 3.

See your demo after the modification: http://jsfiddle.net/OlegKi/spckrjvf/5/

Oleg
  • 220,925
  • 34
  • 403
  • 798
  • Thank you for taking the time to suggest a change to my approach. See my edited question. Up vote. – Karl Aug 02 '15 at 11:47
  • @Karl: You are welcome! The approach which I described in my answer will be used in jqGrid ([free jqGrid](https://github.com/free-jqgrid/jqGrid) is the fork of jqGrid which I develop) which is alternative to [DataTables](http://datatables.net/). What you measured by `performance.now()` exactly: binding only or processing of the click event? It's clear that binding to one table element (like I suggested) will be faster as selection of all tds and binding to every td. I think that processing of event inclusive bubbling will be also very quickly and one will don't see any significant degradation. – Oleg Aug 02 '15 at 11:58
  • @Oleg Thank you for your solution. This is good from the performance perspective. – Karthik Chintala Dec 20 '18 at 09:15
  • 1
    @KarthikChintala: You are welcome! I think too, that it's better to register one click handler on the whole table as to register separate click handler on every td element. – Oleg Dec 20 '18 at 10:02
  • @Oleg Though the OP writes events for all the TD elements as in the accepted answer. OP will certainly but surely comes back to this single listener. Unless a developer doesn't understand how bubbling works, they will never use this solution. I believe this should be the answer. But anyway it involves delegating to respective event handlers if we want anything specific to TD elements. Wow. I wrote too much in the comment. – Karthik Chintala Dec 20 '18 at 10:28
0

You can check the desired condition by doing this.

$('td').click(function () {
    var col = $(this).parent().children().index($(this));
    var row = $(this).parent().parent().children().index($(this).parent());
    if (col == 3 || col == 0) {
        alert("I have clicked on column " + col);
    } else {
        alert("I have clicked on another column");
    }
});
Aruna Tebel
  • 1,436
  • 1
  • 12
  • 24