简体   繁体   中英

Javascript: navigate table inputs with arrow keys

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>

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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM