Reputation: 123
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>
Upvotes: 4
Views: 5032
Reputation: 371
<!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>
Upvotes: 0
Reputation: 2081
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>
Upvotes: 5