5

I have a html table and I'm trying to use jJavascript to trigger an event whenever a row loses focus but the "blur" event doesn't seem to be right as nothing is firing:

(quick example of what I'm doing)

<tr class="tableRow">
    <td class="tg-amwm" contentEditable="true">hours</td>
    <td class="tg-amwm" contentEditable="true">minutes</td>
    <td class="tg-amwm" contentEditable="true">hours</td>
    <td class="tg-amwm" contentEditable="true">minutes</td>
</tr>

and I"m using the following:

var rows = document.getElementsByClassName("tableRow");

for(i = 0; i < rows.length; i++) {
    rows[i].addEventListener("blur", function(){console.log("row left!");});
}

but nothing is apprearing in the console - am I misunderstanding the event/DOM structure?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
SierraOscar
  • 17,507
  • 6
  • 40
  • 68
  • "focus" is something that happens to interactive elements like ``. – Pointy Sep 10 '16 at 15:26
  • @Pointy: No, non-interactive elements can have focus too. – T.J. Crowder Sep 10 '16 at 15:26
  • @Pointy poor choice of words on my part - I'm using the "blur" event because it's not an input element and for cross-browser support. – SierraOscar Sep 10 '16 at 15:26
  • 1
    @ Macro Man: The table row probably never receives focus. If you click a table *cell*, the cell will receive focus, but not the row. – T.J. Crowder Sep 10 '16 at 15:27
  • @T.J.Crowder really? Like with contenteditable? What does "focus" even mean for something like a `
    ` or a ``? Does it just mean that the text is selected or something? (I'm certain you're right of course.)
    – Pointy Sep 10 '16 at 15:27
  • @T.J.Crowder not sure if it's relevant - I've updated the code to show that the cells are editable in the row, apologies for missing that out – SierraOscar Sep 10 '16 at 15:28
  • @Pointy: Doesn't even have to be editable. – T.J. Crowder Sep 10 '16 at 15:29
  • @T.J.Crowder I think I understand what you mean about the row never taking focus - so I guess my question is how can I fire the event when the user moves away from cells in that row? – SierraOscar Sep 10 '16 at 15:30
  • @Pointy: But I'm not at all sure `focus` and `blur` fire... In my experiment just now on Chrome, it didn't for a non-contenteditable td, and did for one that was. – T.J. Crowder Sep 10 '16 at 15:31
  • 1
    Wow it seems like it's kind-of complicated and open to user agent interpretation http://w3c.github.io/html/editing.html#focusable – Pointy Sep 10 '16 at 15:31

2 Answers2

4

The row probably never receives focus, the cells in it do.

Unfortunately, blur doesn't bubble. But if you hook blur on each cell, then click one of those cells to give it focus, then click something else to take focus away, it should work:

var cells = document.querySelectorAll(".tableRow td");

for (var i = 0; i < cells.length; i++) {
  cells[i].addEventListener("blur", handler);
}

function handler() {
  console.log("row left!");
}
<p>Click a cell below to give it focus</p>
<table>
  <tbody>
    <tr class="tableRow">
      <td class="tg-amwm" contenteditable>hours</td>
      <td class="tg-amwm" contenteditable>minutes</td>
      <td class="tg-amwm" contenteditable>hours</td>
      <td class="tg-amwm" contenteditable>minutes</td>
    </tr>
  </tbody>
</table>
<p>Click here to take focus away</p>

Alternately, use focusout, which was originally an IE-only event but which has been added to Chrome but not, as far as I can tell, Firefox:

document.querySelector("table").addEventListener("focusout", function() {
  console.log("Left!");
});
<p>Click a cell below to give it focus</p>
<table>
  <tbody>
    <tr class="tableRow">
      <td class="tg-amwm" contenteditable>hours</td>
      <td class="tg-amwm" contenteditable>minutes</td>
      <td class="tg-amwm" contenteditable>hours</td>
      <td class="tg-amwm" contenteditable>minutes</td>
    </tr>
  </tbody>
</table>
<p>Click here to take focus away</p>

Side note for jQuery users: jQuery makes focus and blur bubble even though they don't natively, so you could use event delegation for the above with jQuery.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Unfortunately this is triggered every time a cell loses focus rather than the row (i.e. if I click an adjascent cell, the code still fires) but I'm after the _row_ specifically - is it even possible considering the row never technically _gains_ focus? – SierraOscar Sep 10 '16 at 15:37
  • @MacroMan: You'll have to keep track of the last cell that had focus and compare the row it's in to the one you're currently leaving. Or use `focusout`, but I don't think Firefox supports it. *(Apologies, running out the door...)* – T.J. Crowder Sep 10 '16 at 15:39
  • I was deliberately using the blur event as I need to be cross-browser compatible, but I can probably work something out by _comparing_ the rows as you've suggested which makes perfect sense. I'll still mark as the answer, because it does actually still answer my question `am I misunderstanding the event/DOM structure?` - Yes, because the row never gets focus and the blur event doesn't bubble. Cheers for the quick response :) – SierraOscar Sep 10 '16 at 15:42
  • how to know which cell loss focus? –  Feb 09 '19 at 18:32
  • @Soorya - As always, it will referenced from the `target` property on the event object the event handler receives. – T.J. Crowder Feb 10 '19 at 09:49
  • How can I get the data or cell number which triggered the event? I'm new to JavaScript.. can you please give me a code snippet? –  Feb 10 '19 at 10:21
  • @Soorya - You declare a parameter to the event handler, which will receive the event object. On the event object, there's a property called `target` which is the element the event targeted (the [`HTMLTableCellElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) for the table cell, or an element within it if the event originated in an element (like a `span`) within the cell. To handle the case where there's a `span` in the `td` and you want the `td`, you can use [`closest`](https://developer.mozilla.org/en-US/docs/Web/API/Element/closest) (note you may... *(cont'd)* – T.J. Crowder Feb 10 '19 at 10:43
  • *(continuing)* ...need to polyfill it on older browsers). Then you can use any of the properties on `HTMLTableCellElement` or its superclasses to get the contents of the cell. So for instance: `container.addEventListener("focusout", function(e) { var td = e.target.closest("td"); console.log(td.textContent); };` – T.J. Crowder Feb 10 '19 at 10:44
0

I got around this limitation by rather a troublesome way, but it suits my use-case,

The main idea behind the code below is that each row has a unique ID, which I check via a jQuery method that checks each time a row element is clicked, then I store that ID in a temp variable, then update it when a new row is clicked.

I've included a code snippet which you can test by going from one row to another, it should show "same row" alert if you're still in the same row, and "new row" when you leave the row and click on another row.

 var tempID = 0
  $(document).ready(function () {
    $('#MainContent_tbl tr').click(function (event) {
        var elID = $(this).attr('id');
        if (elID){
          if(tempID !=  elID){
            if(tempID == 0){
              tempID = elID;
            }
            else{
              alert('New Row')
              tempID = elID;
            }
            
          }
          else{
            alert('same row')
          }
          
          
        }
        
    });
  });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<table id="MainContent_tbl">
<thead>
      <tr>
          <th>Supplier ID</th>
          <th>Shop Name</th>

  </thead>
  <tbody>
  <tr  id="1"> 
  <td>
  supplier 1
  </td>
    <td>
  Shop 1
  </td>
  </tr>
  
    <tr  id="2"> 
  <td>
  supplier 2
  </td>
    <td>
  Shop 2
  </td>
  </tr>
  
  </tbody>
  
</table>
</html>