2

I have a table that handles mouse clicks using onclick for the entire row. The problem is that these rows also contain checkboxes that should not be activating the onclick event. Clicking on a row should cause an event but selecting a checkbox should not.

I found a lot of answers to this but I've been unable to make it work. I'm using simple html/css/js without anything like jQuery.

The answer seems to be due to event propagation and lots of answers say to use stopPropagation(). But Chrome says this function undefined for the event.

enter image description here

Here is my test example.

Clicking on the table results in a "table!" alert. I don't want clicks on the checkbox within the table to cause this alert.

function clickTable(e) {
   alert('table!');
}

function clickCheckbox(e) {
   // Error: e.stopPropagation is not a function
   e.stopPropagation();
}
table {
   border: 1px solid black;
   width: 120px;
   height: 120px;
}

input {
   width: 32px;
   height: 32px;
}
<!DOCTYPE html>
<html lang="en">
<head>
   <meta charset="utf-8">
   <title>Checkbox Click</title>
</head>
<body>

<table>
   <tr onclick="clickTable(this);">
      <th><label>
         <input type="checkbox" name="test" onclick="clickCheckbox(this);">
      </label></th>
   </tr>
</table>

<script>
</script>

</body>
</html>

I can however use window.event.stopPropagation(); which works as expected. But my IDE PhpStorm reports that using window.event in this way is deprecated.

What is the correct use of stopPropagation() for this event?

Zhro
  • 2,279
  • 1
  • 18
  • 34
  • i'd trigger the click on the cells instead of the rows. Each cell should have a common class except for the one holding the checkbox – Lelio Faieta Mar 14 '21 at 19:07
  • This is probably what I'm going to end up doing. It's probably better to have the cell with the checkbox check the box as well to account for an inaccurate click rather than activating the entire row event. I'm still interested in learning what I'm doing wrong in my example though. – Zhro Mar 14 '21 at 19:10
  • 1
    If I am not wrong it should be event.stopPropagation() and not e.stopPropagation() – Lelio Faieta Mar 14 '21 at 19:16
  • I believe that's just shorthand for `window.event`. It's also being styled as deprecated. – Zhro Mar 14 '21 at 19:17
  • Not sure about the deprecation. See documentation: https://developer.mozilla.org/it/docs/Web/API/Event/stopPropagation – Lelio Faieta Mar 14 '21 at 19:18
  • you got everything wrong: `clickCheckbox(this)` send the entire item (`this`), but never an event – Mister Jojo Mar 14 '21 at 19:28
  • See my answer at the bottom for the most correct explanation of your problem as well as another HTML problem you have. – Scott Marcus Mar 14 '21 at 19:33

3 Answers3

0

Inline event listeners do not pass the event object to the function.

Therefore, the function doesn't receive the event object(e) argument, preventing you from invoking the stopPropagation() method.

That is why I would also advice you avoid inline event listeners.

If you need the element that invoked the event, rather than using this you can use e.target to get the event's targeted element

function clickTable(e) {
  alert('table!');
}

function clickCheckbox(e) {
  e.stopPropagation();
}

const tr = document.querySelector('tr');
const input = document.querySelector('input[type=checkbox]');

tr.addEventListener('click', clickTable);
input.addEventListener('click', clickCheckbox);
table {
  border: 1px solid black;
  width: 120px;
  height: 120px;
}

input {
  width: 32px;
  height: 32px;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>Checkbox Click</title>
</head>

<body>

  <table>
    <tr>
      <th><label>
         <input type="checkbox" name="test">
      </label></th>
    </tr>
  </table>

  <script>
  </script>

</body>

</html>
a.mola
  • 2,222
  • 3
  • 16
0

.stopPropagation() is the correct method, but in order to use it, you must call it on the event object reference that is passed to your event handler. This object reference is automatically passed to DOM event handlers that are set up using the .addEventListener() method and not using the legacy onXyz inline event attributes (which should not be used in today's code).

And, when setting up DOM event handlers using .addEventListener(), there's no need to pass this as this will automatically be bound to the element that triggered the event in the first place, so you can just use this in your callback functions without passing it.

Additionally, your HTML is not valid. <th> elements belong inside of <thead> elements, not <tr> elements.

So, just rework the event handler setup:

table {
   border: 1px solid black;
   width: 120px;
   height: 120px;
}

input {
   width: 32px;
   height: 32px;
}
<table>
   <thead>
      <th>
        <label>
         <input type="checkbox" name="test">
        </label>
      </th>
   </thead>
</table>

<script>
  // Do the event binding in JavaScript, not in HTML
  document.querySelector("input[name='test']").addEventListener("click", clickCheckbox);
  document.querySelector("thead").addEventListener("click", clickTable);

  function clickTable(e) {
    alert('table!');
  }

  function clickCheckbox(e) {
    // Now, e will properly recieve the event reference
    e.stopPropagation();
  }
</script>
Scott Marcus
  • 57,085
  • 6
  • 34
  • 54
0

You should use Event delegation where you will get clicked element in table using event.target and do stuff accordingly.

document.querySelector("table").addEventListener("click", function(event) {
  event.target ? console.log(event.target.nodeName) : console.log(event.target.nodeName)
});
table {
  border: 1px solid black;
  width: 120px;
  height: 120px;
}

input {
  width: 32px;
  height: 32px;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>Checkbox Click</title>
</head>

<body>

  <table>
    <tr>
      <th><label>
         <input type="checkbox" name="test">
      </label></th>
    </tr>
  </table>

  <script>
  </script>

</body>

</html>

What is DOM Event delegation?

ikiK
  • 5,467
  • 3
  • 11
  • 27
  • Event delegation is a good thing, but technically is not the solution to this problem. It's simply that inline event handlers do not get the event object that triggered them passed to the event callback. You do not need to use event delegation to make this work. – Scott Marcus Mar 14 '21 at 19:23
  • @ScottMarcus My point was as suggested by removing the inline event handlers that they should/could not be used as a suggestion for resolving the problem. – ikiK Mar 14 '21 at 19:25
  • You can remove the inline event attributes and use `.addEventListener()` without using event delegation. As I said, event delegation is a good use in this case, but not related to the issue being asked about. – Scott Marcus Mar 14 '21 at 19:29