Home > Blockchain >  HTML table rows navigate by keyboard in JQuery
HTML table rows navigate by keyboard in JQuery

Time:10-27

I have a HTML table which is created by Ajax. Rendered markup of this table as below:

<table  id="tbl_byKeyboard">
  <thead>
    <tr>
      <th>#</th>
      <th>Date</th>
      <th>Customer</th>
      <th>Discount</th>
      <th>Total</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>
        <input type="radio" name="bid" value="122">
      </td>
      <td>2022-10-01</td>
      <td>Customer 01</td>
      <td>10%</td>
      <td>1000.00</td>
    </tr>

    <tr>
      <td>
        <input type="radio" name="bid" value="123">
      </td>
      <td>2022-10-01</td>
      <td>Customer 02</td>
      <td>10%</td>
      <td>2000.00</td>
    </tr>

    <tr>
      <td>
        <input type="radio" name="bid" value="124">
      </td>
      <td>2022-10-01</td>
      <td>Customer 03</td>
      <td>10%</td>
      <td>3000.00</td>
    </tr>
  </tbody>
</table>

Using this table, basically I want to navigate the table row using 'up' and 'down' arrow key on keyboard. After selecting the right row, the user should be able to check the radio button (within the selected row) by pressing the enter key.

This is my jQuery code sofar :

$(document).ready(function() {
  $(document).on('click', "#tbl_byKeyboard tbody tr", function(e) {
    var inp = this.querySelector('input')
    if (inp == null) return

    if (e.target.tagName != "INPUT") {
      inp.checked = !inp.checked
    }

    $('#tbl_byKeyboard tbody tr').removeClass('active');
    $(this).addClass('active');
  })
});


$(window).on('keydown', tbl_navigate)


function tbl_navigate(e) {
  var $tbl = $('#tbl_byKeyboard');
  var $cur = $('.active', $tbl).removeClass('active').first();

  if (e.keyCode === 40) { //down
    if ($cur.length) {
      $cur.next().addClass('active');
    } else {
      $tbl.children().first().addClass('active');
    }
  } else if (e.keyCode == 38) { //up
    if ($cur.length) {
      $cur.prev().addClass('active');
    } else {
      $tbl.children().last().addClass('active');
    }
  } else if (e.keyCode == 13) { //enter 
    var $this = $cur;
    var inp = $this.find('input')
    if (inp == null) return

    if (inp.is(':checked')) {
      inp.prop('checked', false);
    } else {
      inp.prop('checked', true);
      $('#tbl_byKeyboard tbody tr').removeClass('active');
      $this.addClass('active');
    }
  }
}

Stack snippet:

$(document).ready(function() {
  $(document).on('click', "#tbl_byKeyboard tbody tr", function(e) {
    var inp = this.querySelector('input')
    if (inp == null) return

    if (e.target.tagName != "INPUT") {
      inp.checked = !inp.checked
    }

    $('#tbl_byKeyboard tbody tr').removeClass('active');
    $(this).addClass('active');
  })
});


$(window).on('keydown', tbl_navigate)


function tbl_navigate(e) {
  var $tbl = $('#tbl_byKeyboard');
  var $cur = $('.active', $tbl).removeClass('active').first();

  if (e.keyCode === 40) { //down
    if ($cur.length) {
      $cur.next().addClass('active');
    } else {
      $tbl.children().first().addClass('active');
    }
  } else if (e.keyCode == 38) { //up
    if ($cur.length) {
      $cur.prev().addClass('active');
    } else {
      $tbl.children().last().addClass('active');
    }
  } else if (e.keyCode == 13) { //enter 
    var $this = $cur;
    var inp = $this.find('input')
    if (inp == null) return

    if (inp.is(':checked')) {
      inp.prop('checked', false);
    } else {
      inp.prop('checked', true);
      $('#tbl_byKeyboard tbody tr').removeClass('active');
      $this.addClass('active');
    }
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<table  id="tbl_byKeyboard">
  <thead>
    <tr>
      <th>#</th>
      <th>Date</th>
      <th>Customer</th>
      <th>Discount</th>
      <th>Total</th>
    </tr>
  </thead>
  
  <tbody>
    <tr>
      <td>
        <input type="radio" name="bid" value="122">
      </td>
      <td>2022-10-01</td>
      <td>Customer 01</td>
      <td>10%</td>
      <td>1000.00</td>
    </tr>

    <tr>
      <td>
        <input type="radio" name="bid" value="123">
      </td>
      <td>2022-10-01</td>
      <td>Customer 02</td>
      <td>10%</td>
      <td>2000.00</td>
    </tr>

    <tr>
      <td>
        <input type="radio" name="bid" value="124">
      </td>
      <td>2022-10-01</td>
      <td>Customer 03</td>
      <td>10%</td>
      <td>3000.00</td>
    </tr>
  </tbody>
</table>

My problem is, with this code I can navigate table rows using 'up' and 'down' arrow key, but enter key not working for me.

Hope somebody may help me to figure this out.

CodePudding user response:

The problem is, that at the beginning of your keydown handler you remove the class active:

var $cur = $('.active', $tbl).removeClass('active').first();

But in the if for the Enter case you don't set that class again, when the input is checked:

if (inp.is(':checked')) {
  inp.prop('checked', false);
}

Furthermore the manipulation of the class isn't necessary in the Enter case - it's only important for the Up and Down keys. Therefor you should remove the active class only in these two cases:

var $cur = $('.active', $tbl).first();

if (e.keyCode === 40) { //down
  $cur.removeClass('active');
  ...
} else if (e.keyCode == 38) { //up
  $cur.removeClass('active');
  ...
}

Because the removal for the Enter case isn't necessary, you can omit it there:

if (inp.is(':checked')) {
  inp.prop('checked', false);
} else {
  inp.prop('checked', true);
}

Working example:

i added a small line to focus the first radiobutton for demonstration:

$('#tbl_byKeyboard input[type=radio]').eq(0).focus();

$(document).ready(function() {
  $('#tbl_byKeyboard input[type=radio]').eq(0).focus();

  $(document).on('click', "#tbl_byKeyboard tbody tr", function(e) {
    var inp = this.querySelector('input')
    if (inp == null) return

    if (e.target.tagName != "INPUT") {
      inp.checked = !inp.checked
    }

    $('#tbl_byKeyboard tbody tr').removeClass('active');
    $(this).addClass('active');
  })
});


$(window).on('keydown', tbl_navigate)


function tbl_navigate(e) {
  var $tbl = $('#tbl_byKeyboard');
  var $cur = $('.active', $tbl).first();

  if (e.keyCode === 40) { //down
    $cur.removeClass('active');
    
    if ($cur.length) {
      $cur.next().addClass('active');
    } else {
      $tbl.children().first().addClass('active');
    }
  } else if (e.keyCode == 38) { //up
    $cur.removeClass('active');
    
    if ($cur.length) {
      $cur.prev().addClass('active');
    } else {
      $tbl.children().last().addClass('active');
    }
  } else if (e.keyCode == 13) { //enter 
    var $this = $cur;
    var inp = $this.find('input')
    if (inp == null) return

    if (inp.is(':checked')) {
      inp.prop('checked', false);
    } else {
      inp.prop('checked', true);
    }
  }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<table  id="tbl_byKeyboard">
  <thead>
    <tr>
      <th>#</th>
      <th>Date</th>
      <th>Customer</th>
      <th>Discount</th>
      <th>Total</th>
    </tr>
  </thead>
  
  <tbody>
    <tr>
      <td>
        <input type="radio" name="bid" value="122">
      </td>
      <td>2022-10-01</td>
      <td>Customer 01</td>
      <td>10%</td>
      <td>1000.00</td>
    </tr>

    <tr>
      <td>
        <input type="radio" name="bid" value="123">
      </td>
      <td>2022-10-01</td>
      <td>Customer 02</td>
      <td>10%</td>
      <td>2000.00</td>
    </tr>

    <tr>
      <td>
        <input type="radio" name="bid" value="124">
      </td>
      <td>2022-10-01</td>
      <td>Customer 03</td>
      <td>10%</td>
      <td>3000.00</td>
    </tr>
  </tbody>
</table>

  • Related