4

I have the standard DataTable code which populates the datatable and shows child rows on row clicks. Inside the child row (using the format function) i have added a button and i want to do something when it's clicked but each time i click the button(data table contains child row inside a new tr->td) the event of td click which is used to open/close child rows is fired and i get "Uncaught TypeError: Cannot read property '0' of undefined" for the d[] array in format function.

This is the code for the child rows open/close:

$('tbody').on('click', 'td', function() {
    var tr = $(this).closest('tr');
    id = $(this).closest('table').attr('id');
    table = $('#' + id).DataTable();
    var row = table.row(tr);
    if (row.child.isShown()) {
        // This row is already open - close it
        row.child.hide();
        tr.removeClass('shown');
    } else {
        // Open this row
        row.child(format(row.data(), id)).show();
        tr.addClass('shown');
    }
});

function format(d, id) {
// `d` is the original data object for the row
if(sessionStorage['name']!=undefined)
    return '<img src="images/photo.jpg" id="photo" />' + '<p>Session created by:' + d[0] + '</p>' + '<p>Start Date:' + d.start_date + '</p>' + '<p>Extra info:' + d[3] + '</p>' + "<button class='btn btn-default' id='joinbutton' role='button'>Join Session</button>";
else
return '<div>' + '<img src="images/photo.jpg" id="photo" />' + '<p>Session created by:' + d[0] + '</p>' + '<p>Start Date:' + d.start_date + '</p>' + '<p>Extra info:' + d[3] + '</p>' + '</div>';

}

Is there a way to either prevent the td click event from being fired for child rows or to detect the button click somehow? Thanks

3
  • 1
    you may add the event to your function $('tbody').on('click', 'td', function(e) { so that you may test for e.target element, if it is not the right one you may stop your function, or you may handle the event stoppropagation Commented Feb 13, 2016 at 11:37
  • Being too complex to make both functions inside the same function i exctracted the button to the table into a td. thanks for the help i may come back to your answer later on Commented Feb 13, 2016 at 14:23
  • Can you please provide complete example like using JSFiddle, which will be easier to understand the exact problem. Meanwhile you can try adding event.stopPropagation(); inside button click event. Commented Feb 13, 2016 at 14:58

3 Answers 3

6

I propose the snippet following my remark (see the comments, not those in the format function, they are required for me to make the snippet running):

function format(d, id) {
  // `d` is the original data object for the row
  //if(sessionStorage['name']!=undefined)
    return '<img src="http://icons.iconarchive.com/icons/treetog/junior/256/image-format-jpg-icon.png" id="photo" />' + '<p>Session created by:' + d[0] + '</p>' + '<p>Start Date:' + d.start_date + '</p>' + '<p>Extra info:' + d[3] + '</p>' + "<button class='btn btn-default' id='joinbutton' role='button'>Join Session</button>";
  //else
    //return '<div>' + '<img src="images/photo.jpg" id="photo" />' + '<p>Session created by:' + d[0] + '</p>' + '<p>Start Date:' + d.start_date + '</p>' + '<p>Extra info:' + d[3] + '</p>' + '</div>';

}

// to handle the joinbutton created dynamically you need:
// avoid to use the same ID for more than one element
$(document).on('click', 'button[role="button"]', function(e) {
  alert('This is the button');
});

$(function () {
  $('tbody').on('click', 'td', function(e) {
    // to avoid to receive the button click inside the td you need:
    if ($(e.target).is(':not(td)')) {
      alert('This is inside td');
      return;
    }
    var tr = $(this).closest('tr');
    id = $(this).closest('table').attr('id');
    table = $('#' + id).DataTable();
    var row = table.row(tr);
    if (row.child.isShown()) {
      // This row is already open - close it
      row.child.hide();
      tr.removeClass('shown');
    } else {
      // Open this row
      row.child(format(row.data(), id)).show();
      tr.addClass('shown');
    }
  });
});
table, th, td {
  border: 1px solid black;
  border-collapse: collapse;
}
th, td {
  padding: 10px;
}
<link href="http://code.jquery.com/ui/1.11.3/themes/smoothness/jquery-ui.css" rel="stylesheet"/>
<script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="http://code.jquery.com/ui/1.11.3/jquery-ui.js"></script>

<link href="//cdn.datatables.net/1.10.11/css/jquery.dataTables.min.css" rel="stylesheet">
<script src="//cdn.datatables.net/1.10.11/js/jquery.dataTables.min.js"></script>


<table id="tblId">
    <thead>
    <tr>
        <th>Month</th>
        <th>Savings</th>
    </tr>
    </thead>
    <tfoot>
    <tr>
        <td>Sum</td>
        <td>$180</td>
    </tr>
    </tfoot>
    <tbody>
    <tr>
        <td>January</td>
        <td>$100</td>
    </tr>
    <tr>
        <td>February</td>
        <td>$80</td>
    </tr>
    </tbody>
</table>

Sign up to request clarification or add additional context in comments.

Comments

2

I resolve my problem with @gaemaf solution:

// Handle click on checkbox
$("#example tbody").on("click", 'input[type="checkbox"]', function(e){
    var $row = $(this).closest("tr");

    if(this.checked){
        $row.addClass("active");
    } else {
        $row.removeClass("active");
    }

    // Prevent click event from propagating to parent
    e.stopPropagation();
});

// Handle click on table cells
$("#example tbody").on("click", "td", function(e){
    // Begin Solution
    if ($(e.target).is(':not(td)')) {
        return;
    }
    // End solution
    $(this).parent().find('input[type="checkbox"]').trigger("click");
});

Comments

2

SOLUTION

Add click handler to the button and return false from the handler to prevent default action and stop the event from propagating to parent element. Alternatively you may call stopPropagation() and preventDefault().

$('#example tbody').on('click', 'button', function (e) {

   // ... skipped ...

   return false;
});

It would be better to remove id='joinbutton' and use class name instead, for example btn-join. Then you would be able to target specific button with the code below.

$('#example tbody').on('click', '.btn-join', function (e) {

   // ... skipped ...

   return false;
});

DEMO

See this jsFiddle for code and demonstration.

4 Comments

returning false doesn't prevent event bubbling. see below simple example <div onclick="alert('a');"> <input type="submit" onclick="alert('b');return false;"/> </div>
@NitinGarg, see the demo where click event from the button doesn't cause other handler to be called.
Dear, please give me couple of minutes to analyse the demo.
@NitinGarg, your statement is not true. jQuery event handlers are different from handlers attached via onclick attribute, see on(): Returning false from an event handler will automatically call event.stopPropagation() and event.preventDefault().

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.