4

I am working on an HTML grade book for a client. I am generating the gradebook with PHP, and then outputting a HTML table as seen in the example below. Each <td> contains a div with an <input> for the teacher to type in the student's score.

Here's what I'm trying to accomplish: how can I make it so the teacher can use the arrow keys on the keyboard to navigate inside of the gradebook? IE: The teacher should be able to click a cell, type in a grade, and then hit the left/right/up/down arrow key to move to the appropriate input and type in the next grade.

I have seen numerous examples on here about how to use javascript to accomplish this task in highlighting different <td> cells, but I cannot figure out how I would go about allowing the teacher to navigate inputs with her arrow keys. Any advice would be much appreciated.

   body {
     margin: 0;
     position: absolute;
     top: 105px; left: 0px;
     width: 100%;
     height: calc(100vh - 105px);
     background-color: #FCFCFC;
     display: grid;
     grid-template-rows: 1fr;
     grid-template-areas:
       "master"}

   .master {
     grid-area: master;
     overflow-x: scroll;}

   table {border-collapse: collapse}

   th, td {
     background-color: white;
     max-width: 110px;
     border: 1px solid lightgray;}

   th {overflow: hidden;}

  thead{
    top: 0;
    position: sticky;
    z-index: 1;}

  tr td:nth-child(1),
  tr th:nth-child(1){
    position: sticky;
    left: 0;}

   thead th.navigator { /* Top left cell with navigation controls */
     padding: 10px;
     z-index: 3;}

   tr td:first-child, tr td:nth-child(2) { /* First two columns of each row */
     white-space: nowrap;
     max-width: fit-content !important;}

   td input {
     border: none;
     outline: none;
     text-align: center;
     max-width: 80%;
     font-size: 18px;
     padding: 6px 0px;
     cursor: cell;}

   th select {
     outline: none;
     -webkit-appearance: none;
     padding: 8px 12px;
     box-sizing: border-box;
     border-radius: 8px;
     width: 100%;
     border: 1px solid lightgray}

  tr:focus-within td:not(.gray) {background-color: #E9DCF9}
  tr:focus-within td:not(.gray) input {background-color: #E9DCF9}

  .due {
    font-size: 11px;
    color: darkgray;}

   .assign {padding: 20px}
   .assign span {
     cursor: pointer;
     font-size: 15px;
     overflow: hidden;
     color: #581F98}

   .avg {padding: 10px}

   .studentInfo {
     display: flex;
     align-items: center;
     margin: 10px 12px 10px 6px;}

   .studentInfo img {
     width: 25px;
     margin-right: 10px;
     clip-path: circle();}

   .red {background-color: red;}
   .gray, .gray input {background-color: #F2F2F2;}

  .score {
    display: flex;
    justify-content: center;
    align-items: center;}
        <table>
          <thead>
            <tr>
              <th class='navigator' colspan='2' rowspan='4'>
                <form method='GET'>
                  <select name='subID' onchange='this.form.submit()'>
                    <option value='1' >Reading</option>
                    <option value='2' >Social Studies</option>
                  </select>
                  <select name='week' onchange='this.form.submit()' disabled>
                    <option value='all'>Entire Quarter</option>
                  </select>
                </form>
              </th>
              <tr>
                <th class='due'><span title='Monday'>10/11</span> to <span title='Wednesday'>10/13</span></th>
                <th class='due'><span title='Wednesday'>10/20</span> to <span title='Friday'>10/22</span></th>
                <th class='due'><span title='Monday'>10/18</span> to <span title='Friday'>10/22</span></th>
                <th class='due'><span title='Wednesday'>10/20</span> to <span title='Friday'>10/22</span></th>
              </tr>
              <tr>
                <th class='assign'>
                  <span title='Assignment ID: 130' onclick='assignInfo("130");'>📚 Quiz</span>
                </th>
                <th class='assign'>
                  <span title='Assignment ID: 146' onclick='assignInfo("146");'>📚 Homework</span>
                </th>
                <th class='assign'>
                  <span title='Assignment ID: 145' onclick='assignInfo("145");'>💻 Test</span>
                </th>
                <th class='assign'>
                  <span title='Assignment ID: 147' onclick='assignInfo("147");'>✏️ Project</span>
                </th>
            </tr>
            <tr>
              <th class='avg gray'><span title='9.111/10'>91%</span></th>
              <th class='avg gray'><span title='8.672/10'>87%</span></th>
              <th class='avg gray'><span title='4.348/5'>87%</span></th>
              <th class='avg gray'><span title='8.007/10'>80%</span></th>
            </tr>
          </thead>
          <tr>
            <td>
              <div class='studentInfo'>
                <span title='Student ID: 11'><img src='../../resources/pics/students/11.jpg'></span>
                <span>John Doe</span>
              </div>
            </td>
            <td class='avg gray'>
              <span data-studentAvg='11' title='97.5/110'>89%</span>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='130' data-usid='11' data-workID='7280' data-curScore='10' value='10'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='131' data-usid='11' data-workID='7282' data-curScore='9' value='9'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='132' data-usid='11' data-workID='7340' data-curScore='10' value='10'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='130' data-usid='11' data-workID='7280' data-curScore='10' value='10'>
              </div>
            </td>
          </tr>
          <tr>
            <td>
              <div class='studentInfo'>
                <span title='Student ID: 12'><img src='../../resources/pics/students/12.jpg'></span>
                <span>Jane Doe</span>
              </div>
            </td>
            <td class='avg gray'>
              <span data-studentAvg='12' title='97.5/110'>69%</span>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='130' data-usid='12' data-workID='7250' data-curScore='6' value='6'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='131' data-usid='12' data-workID='7211' data-curScore='9' value='9'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='132' data-usid='12' data-workID='7110' data-curScore='4' value='4'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='130' data-usid='12' data-workID='7233' data-curScore='10' value='10'>
              </div>
            </td>
          </tr>
          <tr>
            <td>
              <div class='studentInfo'>
                <span title='Student ID: 13'><img src='../../resources/pics/students/13.jpg'></span>
                <span>Sally Martin</span>
              </div>
            </td>
            <td class='avg gray'>
              <span data-studentAvg='13' title='97.5/110'>100%</span>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='130' data-usid='13' data-workID='6250' data-curScore='10' value='10'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='131' data-usid='13' data-workID='6211' data-curScore='10' value='10'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='132' data-usid='13' data-workID='7610' data-curScore='10' value='10'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='130' data-usid='13' data-workID='7933' data-curScore='10' value='10'>
              </div>
            </td>
          </tr>
        </table>

5
  • Just use the Tab Key. But if you really want to use arrow keys, you have to add some JavaScript to your code. You could try something with an event listener to focus some element based on your input. Commented Dec 10, 2021 at 19:58
  • It sounds like you're trying to create a spreadsheet editor. You may want to look for a client side library that can do this for you. Commented Dec 10, 2021 at 20:06
  • @TheOrigin tab key would only work to move right, but I need to move up/down also. Commented Dec 10, 2021 at 20:55
  • @Kyle I'm not sure I would even know where to begin with that. I really don't need many editor features, only the ability to move up/down with arrow keys. Commented Dec 10, 2021 at 20:56
  • As @TheOrigin says you'll need some js, listen for keydown event, get the cell index you're in, then depending on the key pressed (arrows) go from there by getting the right element in your table and focus the input with input.focus(). Commented Dec 11, 2021 at 1:33

2 Answers 2

5

It's not perfect but it should give you a place to start. You'll have to add some error handling and handle edge cases.

document.addEventListener( 'keydown', ( event ) => {

  const currentInput = document.activeElement;
  const currentTd = currentInput.parentNode.parentNode;
  const currentTr = currentTd.parentNode;
  const index = Array.from(currentTr.children).indexOf(currentTd);

  switch (event.key) {
    case "ArrowLeft":
        // Left pressed
        currentTd.previousElementSibling.getElementsByTagName('input')[0].focus();
        break;
    case "ArrowRight":
        // Right pressed
        currentTd.nextElementSibling.getElementsByTagName('input')[0].focus();
        break;
    case "ArrowUp":
        // Up pressed
        Array.from( currentTr.previousElementSibling.children )[index].getElementsByTagName('input')[0].focus();
        break;
    case "ArrowDown":
        // Down pressed
        Array.from( currentTr.nextElementSibling.children )[index].getElementsByTagName('input')[0].focus();
        break;
  }
} )
body {
     margin: 0;
     position: absolute;
     top: 105px; left: 0px;
     width: 100%;
     height: calc(100vh - 105px);
     background-color: #FCFCFC;
     display: grid;
     grid-template-rows: 1fr;
     grid-template-areas:
       "master"}

   .master {
     grid-area: master;
     overflow-x: scroll;}

   table {border-collapse: collapse}

   th, td {
     background-color: white;
     max-width: 110px;
     border: 1px solid lightgray;}

   th {overflow: hidden;}

  thead{
    top: 0;
    position: sticky;
    z-index: 1;}

  tr td:nth-child(1),
  tr th:nth-child(1){
    position: sticky;
    left: 0;}

   thead th.navigator { /* Top left cell with navigation controls */
     padding: 10px;
     z-index: 3;}

   tr td:first-child, tr td:nth-child(2) { /* First two columns of each row */
     white-space: nowrap;
     max-width: fit-content !important;}

   td input {
     border: none;
     outline: none;
     text-align: center;
     max-width: 80%;
     font-size: 18px;
     padding: 6px 0px;
     cursor: cell;}

   th select {
     outline: none;
     -webkit-appearance: none;
     padding: 8px 12px;
     box-sizing: border-box;
     border-radius: 8px;
     width: 100%;
     border: 1px solid lightgray}

  tr:focus-within td:not(.gray) {background-color: #E9DCF9}
  tr:focus-within td:not(.gray) input {background-color: #E9DCF9}

  .due {
    font-size: 11px;
    color: darkgray;}

   .assign {padding: 20px}
   .assign span {
     cursor: pointer;
     font-size: 15px;
     overflow: hidden;
     color: #581F98}

   .avg {padding: 10px}

   .studentInfo {
     display: flex;
     align-items: center;
     margin: 10px 12px 10px 6px;}

   .studentInfo img {
     width: 25px;
     margin-right: 10px;
     clip-path: circle();}

   .red {background-color: red;}
   .gray, .gray input {background-color: #F2F2F2;}

  .score {
    display: flex;
    justify-content: center;
    align-items: center;}
<table>
          <thead>
            <tr>
              <th class='navigator' colspan='2' rowspan='4'>
                <form method='GET'>
                  <select name='subID' onchange='this.form.submit()'>
                    <option value='1' >Reading</option>
                    <option value='2' >Social Studies</option>
                  </select>
                  <select name='week' onchange='this.form.submit()' disabled>
                    <option value='all'>Entire Quarter</option>
                  </select>
                </form>
              </th>
              <tr>
                <th class='due'><span title='Monday'>10/11</span> to <span title='Wednesday'>10/13</span></th>
                <th class='due'><span title='Wednesday'>10/20</span> to <span title='Friday'>10/22</span></th>
                <th class='due'><span title='Monday'>10/18</span> to <span title='Friday'>10/22</span></th>
                <th class='due'><span title='Wednesday'>10/20</span> to <span title='Friday'>10/22</span></th>
              </tr>
              <tr>
                <th class='assign'>
                  <span title='Assignment ID: 130' onclick='assignInfo("130");'>📚 Quiz</span>
                </th>
                <th class='assign'>
                  <span title='Assignment ID: 146' onclick='assignInfo("146");'>📚 Homework</span>
                </th>
                <th class='assign'>
                  <span title='Assignment ID: 145' onclick='assignInfo("145");'>💻 Test</span>
                </th>
                <th class='assign'>
                  <span title='Assignment ID: 147' onclick='assignInfo("147");'>✏️ Project</span>
                </th>
            </tr>
            <tr>
              <th class='avg gray'><span title='9.111/10'>91%</span></th>
              <th class='avg gray'><span title='8.672/10'>87%</span></th>
              <th class='avg gray'><span title='4.348/5'>87%</span></th>
              <th class='avg gray'><span title='8.007/10'>80%</span></th>
            </tr>
          </thead>
          <tr>
            <td>
              <div class='studentInfo'>
                <span title='Student ID: 11'><img src='../../resources/pics/students/11.jpg'></span>
                <span>John Doe</span>
              </div>
            </td>
            <td class='avg gray'>
              <span data-studentAvg='11' title='97.5/110'>89%</span>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='130' data-usid='11' data-workID='7280' data-curScore='10' value='10'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='131' data-usid='11' data-workID='7282' data-curScore='9' value='9'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='132' data-usid='11' data-workID='7340' data-curScore='10' value='10'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='130' data-usid='11' data-workID='7280' data-curScore='10' value='10'>
              </div>
            </td>
          </tr>
          <tr>
            <td>
              <div class='studentInfo'>
                <span title='Student ID: 12'><img src='../../resources/pics/students/12.jpg'></span>
                <span>Jane Doe</span>
              </div>
            </td>
            <td class='avg gray'>
              <span data-studentAvg='12' title='97.5/110'>69%</span>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='130' data-usid='12' data-workID='7250' data-curScore='6' value='6'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='131' data-usid='12' data-workID='7211' data-curScore='9' value='9'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='132' data-usid='12' data-workID='7110' data-curScore='4' value='4'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='130' data-usid='12' data-workID='7233' data-curScore='10' value='10'>
              </div>
            </td>
          </tr>
          <tr>
            <td>
              <div class='studentInfo'>
                <span title='Student ID: 13'><img src='../../resources/pics/students/13.jpg'></span>
                <span>Sally Martin</span>
              </div>
            </td>
            <td class='avg gray'>
              <span data-studentAvg='13' title='97.5/110'>100%</span>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='130' data-usid='13' data-workID='6250' data-curScore='10' value='10'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='131' data-usid='13' data-workID='6211' data-curScore='10' value='10'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='132' data-usid='13' data-workID='7610' data-curScore='10' value='10'>
              </div>
            </td>
            <td>
              <div class='score'>
                <input type='text' data-assID='130' data-usid='13' data-workID='7933' data-curScore='10' value='10'>
              </div>
            </td>
          </tr>
        </table>

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

1 Comment

Record down the index user clicked, pass this index to onkeydown, then user can navigate through input fields by arrow keys
0
<!DOCTYPE html>
<html>

<head>
    <title>
        Navigation through input fields by arrow keys
   </title>

<script type="text/javascript" src="http://code.jquery.com/jquery-1.8.2.js"></script>


<script type="text/javascript">
    $(function() {
    var index=0;
    $(document).mousedown( function( e ) { 
    console.log(e.target);
        if( e.target.nodeName == "INPUT" || e.target.nodeName == "TEXTAREA" )
        {
            var current_field = e.target.id;
            var current_element = current_field.substr(10,1);
            var current_position= parseInt(current_element);
            
            
            index = (current_position-1) * 6;
            
            if(current_field.includes('content'))
                index= index+0;
            else if(current_field.includes('component'))
                index= index+1;
            else if (current_field.includes('method'))
                index= index+2;
            else if (current_field.includes('delivery_lead'))
                index= index+3;
            else if (current_field.includes('total_hours'))
                index= index+4;
            else if (current_field.includes('included_in_otj'))
                index= index+5;
            console.log(index);
            
        }
    })
    var elements = document.getElementsByClassName("arrow-togglable");
    var currentIndex = 0;

    document.onkeydown = function(e) {
        currentIndex = index;
        console.log("currentIndex1:"+currentIndex);
        switch (e.keyCode) {
            case 37:
              currentIndex = (currentIndex == 0) ? elements.length - 1 : --currentIndex;
              elements[currentIndex].focus();
              //alert(currentIndex);
              index= currentIndex;
              break;
            case 38:
              currentIndex = (currentIndex == 0) ? elements.length - 1 : currentIndex-6;
              elements[currentIndex].focus();
              index= currentIndex;
              //alert(currentIndex);
              break;
            case 39:
              currentIndex = ((currentIndex + 1) == elements.length) ? 0 : ++currentIndex;
              elements[currentIndex].focus();
              index= currentIndex;
              break;
            case 40:
              currentIndex = ((currentIndex + 1) == elements.length) ? 0 : currentIndex+6;
              elements[currentIndex].focus();
              index= currentIndex;
              break;
            
        }
        console.log("currentIndex2:"+currentIndex);
    };
});
</script>
</head>
<body >
  <div class="container" >
    <div class='h4_div'>                    
        <h4 class='col-xs-12 blue_h4'>Navigate through input fields</h4> 
    </div>
    <div  style='text-align: center;margin-top:0px;margin-bottom:5px;'> 
        <table  class='col-xs-12 cs_table' id='planned_off_year1'>
            <tr  style ='width:90%; '>
                <td class='td_office_box_label_2'> Content</td>
                <td class='td_office_box_label_2'> Component</td>
                <td class='td_office_box_label_2'> Method</td>
                <td class='td_office_box_label_2'> Delivery Lead</td>
                <td class='td_office_box_label_2'> Total hours</td>
                <td class='td_office_box_label_2'> Included in OTJ</td>
            </tr>
            <tr style ='width:90%; '>
              <td ><input id="year1_term1_content" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term1_component" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term1_method"class="arrow-togglable" style='width:100%'value='' ></td>
              <td ><input id="year1_term1_delivery_lead" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term1_total_hours" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term1_included_in_otj" class="arrow-togglable" style='width:100%' value='' ></td>
            </tr>
            <tr style ='width:90%; '>
              <td ><input id="year1_term2_content" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term2_component" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term2_method" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term2_delivery_lead" class="arrow-togglable" style='width:100%' value=''></td>
              <td ><input id="year1_term2_total_hours" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term2_included_in_otj" class="arrow-togglable" style='width:100%' value='' ></td>
            </tr>
            <tr style ='width:90%; '>
              <td ><input id="year1_term3_content" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term3_component" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term3_method" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term3_delivery_lead" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term3_total_hours" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term3_included_in_otj" class="arrow-togglable" style='width:100%' value='' ></td>
            </tr>
            <tr style ='width:90%; '>
              <td ><input id="year1_term4_content" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term4_component" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term4_method" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term4_delivery_lead" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term4_total_hours" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term4_included_in_otj" class="arrow-togglable" style='width:100%' value='' ></td>
            </tr>
            <tr style ='width:90%; '>
              <td ><input id="year1_term5_content" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term5_component" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term5_method" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term5_delivery_lead" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term5_total_hours" class="arrow-togglable" style='width:100%' value='' ></td>
              <td ><input id="year1_term5_included_in_otj" class="arrow-togglable" style='width:100%' value='' ></td>
            </tr>
            
        </table>
    </div>
            
</div>
            
</body>
</html>

Comments

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.