0

I've hardly used javascript and I'm stuck:

I've got a table with id JamTable

I'm trying to write some JS that will get me an array of each <td> value for any row clicked on, so that I can present it in a popup, wing it back to the server via POST request using an ajax call and then update the elements on the table so no postback is required - but so far I can't even get an array populated.

I've got:

    $(document).ready(function () {

        // Get all table row elements <tr> in table 'JamTable' into var 'tr'
        var tr = $('#JamTable').find('tr');

        // Bind a 'click' event for each of those <tr> row elements
        tr.bind('click', function (e) {
            // so that when a row is clicked:
            
            var r = $(this).closest('tr').row;
            var myArray = new Array(r.cells);

            for (var c = 0, col; col = r.cells[c]; c++) {
                alert(col.text)
            }
        });
    });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="JamTable">
      <tbody>
          <tr>
              <td>1</td>
              <td>JAM</td>
              <td>0.004</td>
          </tr>
          <tr>
              <td>3</td>
              <td>BOB</td>
              <td>0.24</td>
          </tr>
          <tr>
              <td>9</td>
              <td>Nasty Simon</td>
              <td>94.3</td>
          </tr>
      </tbody>
    </table>

Yeah I'm totally lost when it comes to JS

matthias_h
  • 11,162
  • 9
  • 19
  • 38
jamheadart
  • 4,270
  • 4
  • 19
  • 50

4 Answers4

1

You don't need jquery for that.

You may use querySelectorAll to get the trs and simply children on the tr node to get the tds

const trs = [...document.querySelectorAll('tr')]
trs.forEach(tr => tr.addEventListener('click', e => {
  // whenever it is clicked, get its tds
  const values = [...tr.children].map(td => td.innerText)
  console.log('click', values)
}, false))
<table>
  <tbody>
    <tr>
      <td>1</td>
      <td>JAM</td>
      <td>0.004</td>
    </tr>
    <tr>
      <td>3</td>
      <td>BOB</td>
      <td>0.24</td>
    </tr>
    <tr>
      <td>9</td>
      <td>Nasty Simon</td>
      <td>94.3</td>
    </tr>
  </tbody>
</table>

As @vsync suggested, better to use event delegation in case you have a lot of rows to avoid binding several clicks. This also allows to add more rows later on without to have to bind more click handler

edit2 still thx to @vsync, avoid using onclick and prefer addEventListener to avoid overriding existing events

const table = document.querySelector('table')
table.addEventListener('click', e => {
  if (e.target.nodeName !== 'TD') { return }
  const values = [...e.target.parentNode.children].map(c => c.innerText)
  console.log(values)
}, false)
<table>
  <tbody>
    <tr>
      <td>1</td>
      <td>JAM</td>
      <td>0.004</td>
    </tr>
    <tr>
      <td>3</td>
      <td>BOB</td>
      <td>0.24</td>
    </tr>
    <tr>
      <td>9</td>
      <td>Nasty Simon</td>
      <td>94.3</td>
    </tr>
  </tbody>
</table>
grodzi
  • 5,260
  • 1
  • 13
  • 13
  • Very good, but not optimal, not using event delegation is never recommended. – vsync Apr 16 '20 at 11:43
  • 1
    You should also never ever bind events without `addEventListener`, it will override previous event that might have been there. – vsync Apr 16 '20 at 11:53
  • Well spotted, I'll have a lot of rows so don't want to bind to all of them if I can just check the click target instead. – jamheadart Apr 16 '20 at 11:57
1

You can delegate the event from tr. On click of it get the children. Using Array.from will create an array of td. Using map to iterate that and get the text from the td

$("#JamTable").on('click', 'tr', function(e) {
  let k = Array.from($(this).children()).map((item) => {
    return item.innerHTML;
  })
  console.log(k)

})
td {
  border: 1px solid green;
  cursor: pointer;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id='JamTable'>
  <tbody>
    <tr>
      <td>1</td>
      <td>JAM</td>
      <td>0.004</td>
    </tr>
    <tr>
      <td>3</td>
      <td>BOB</td>
      <td>0.24</td>
    </tr>
    <tr>
      <td>9</td>
      <td>Nasty Simon</td>
      <td>94.3</td>
    </tr>
  </tbody>


</table>
brk
  • 43,022
  • 4
  • 37
  • 61
  • no need for the `return` statement if you are already using *arrow-function*. Also no need to sometimes use `function(e)` and sometimes *arrow-function* – vsync Apr 16 '20 at 12:04
1

Using proper event-delegation is key to success in such scenarios. Catching "click" events on rows is guaranteed to work, even with dynamically-added rows (which were added to the DOM after the event listener was defined)

Breakdown (see comments):

const tableElm = document.querySelector('table')

// listen to "click" event anywhere on the <table>
tableElm.addEventListener('click', onTableClick)

function onTableClick(e){
  // event delegation 
  const rowElm = e.target.closest('tr')

  // traverse each child of the row (HTMLCollection). map the text value into an Array
  // https://stackoverflow.com/a/34250397/104380
  const values = rowElm ? [...rowElm.children].map(td => td.innerText) : []

  // print result
  console.clear()
  console.log(  values  )
}
<table>
  <tbody>
    <tr>
      <td>1</td>
      <td>JAM</td>
      <td>0.004</td>
    </tr>
    <tr>
      <td>3</td>
      <td>BOB</td>
      <td>0.24</td>
    </tr>
    <tr>
      <td>9</td>
      <td>Nasty Simon</td>
      <td>94.3</td>
    </tr>
  </tbody>
</table>

You should probably also have some unique id on the <tr> if you are sending data back to the server, it might need to know to which row it belongs to

Community
  • 1
  • 1
vsync
  • 87,559
  • 45
  • 247
  • 317
  • 1
    I've went for this answer as it most closely reflects my original attempt so is easiest to understand, while optimising performance – jamheadart Apr 16 '20 at 12:12
0

$('#JamTable tbody tr').click(function () {
    var arr = [];
    $($(this).children('td')).each(function (index, val) {
        arr.push(val.innerText);
    });
    console.log(arr);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="JamTable">
    <tbody>
        <tr>
            <td>1</td>
            <td>JAM</td>
            <td>0.004</td>
        </tr>
        <tr>
            <td>3</td>
            <td>BOB</td>
            <td>0.24</td>
        </tr>
        <tr>
            <td>9</td>
            <td>Nasty Simon</td>
            <td>94.3</td>
        </tr>
    </tbody>
</table>
  • no need to write `.children('td')` because only `` elements can be a child of rows inside ``, so writing `.children()` is suffice, Also, it's best-practice to utilize *event-delegation* method, if possible. See my answer for details – vsync Apr 16 '20 at 12:51