【问题标题】:Use arrow keys to navigate an HTML table使用箭头键导航 HTML 表格
【发布时间】:2014-05-14 02:10:08
【问题描述】:

我使用 HTML 表格创建了一个非常基本的电子表格。它工作得很好,除了用户必须使用鼠标单击每个<td> 才能对其进行编辑。我正在使用 jQuery 捕获点击事件并显示一个对话框来编辑它。我希望用户能够使用箭头键导航到每个单元格,单元格 css 背景更改以指示焦点,单击 Enter 键将触发 jQuery 对话框事件。我正在使用 jQuery 1.9。

这是一个jsfiddle,基本上是我所拥有的。

如何保存当前选中的单元格,以便当你用鼠标点击一个单元格,然后使用箭头键时,它会从“当前”单元格导航?

谢谢。

【问题讨论】:

标签: jquery html css html-table spreadsheet


【解决方案1】:

对于箭头键聚焦,我最终将这里发布的一堆不同的解决方案捆绑在一起并提出了这个。仍然不确定为什么 .next() 或 .prev() 不适用于行...似乎出于某种原因需要 .prevAll 和 .nextAll:

   $("input").keydown(function (e) {
    var textInput = this;
    var val = textInput.value;
    var isAtStart = false, isAtEnd = false;
    var cellindex = $(this).parents('td').index();
    if (typeof textInput.selectionStart == "number") {
        // Non-IE browsers


        isAtStart = (textInput.selectionStart == 0);
        isAtEnd = (textInput.selectionEnd == val.length);
    } else if (document.selection && document.selection.createRange) {
        // IE <= 8 branch
        textInput.focus();
        var selRange = document.selection.createRange();
        var inputRange = textInput.createTextRange();
        var inputSelRange = inputRange.duplicate();
        inputSelRange.moveToBookmark(selRange.getBookmark());
        isAtStart = inputSelRange.compareEndPoints("StartToStart", inputRange) == 0;
        isAtEnd = inputSelRange.compareEndPoints("EndToEnd", inputRange) == 0;
    }

      // workaround for text inputs of 'number' not working in Chrome... selectionStart/End is null.  Can no longer move cursor left or right inside this field.
    if (textInput.selectionStart == null) {
        if (e.which == 37 || e.which == 39) {

            isAtStart = true;
            isAtEnd = true;
        }
    }

    if (e.which == 37) {
        if (isAtStart) {
            $(this).closest('td').prevAll('td').find("input").focus();
        }
    }
    if (e.which == 39) {

        if (isAtEnd) {
            $(this).closest('td').nextAll('td').find("input").not(":hidden").first().focus();
        }
    }
    if (e.which == 40) {
              $(e.target).closest('tr').nextAll('tr').find('td').eq(cellindex).find(':text').focus();
    }
    if (e.which == 38) {
    $(e.target).closest('tr').prevAll('tr').first().find('td').eq(cellindex).find(':text').focus();
    }

});

【讨论】:

    【解决方案2】:

    此代码将正确帮助您使用箭头键导航表格。 如果要编辑按 f2 并编辑单元格,则在每个单元格中都有文本框。

    $(document).ready(function()
    		{
    			var tr,td,cell;
    			td=$("td").length;
    			tr=$("tr").length;
    			cell=td/(tr-1);//one tr have that much of td
    			//alert(cell);
    			$("td").keydown(function(e)
    			{
    				switch(e.keyCode)
    				{
    				
    					case 37 : var first_cell = $(this).index();
    							  if(first_cell==0)
    							  {
    								$(this).parent().prev().children("td:last-child").focus();
    							  }
    							  else
    								$(this).prev("td").focus();break;//left arrow
    					case 39 : var last_cell=$(this).index();
    							  if(last_cell==cell-1)
    							  {
    								$(this).parent().next().children("td").eq(0).focus();
    							  }
    							  $(this).next("td").focus();break;//right arrow
    					case 40 : var child_cell = $(this).index();	
    							  $(this).parent().next().children("td").eq(child_cell).focus();break;//down arrow
    					case 38 : var parent_cell = $(this).index();
    							  $(this).parent().prev().children("td").eq(parent_cell).focus();break;//up arrow
    				}
    				if(e.keyCode==113)
    				{
    					$(this).children().focus();
    				}
    			});
    			$("td").focusin(function()
    			{
    				$(this).css("outline","solid steelblue 3px");//animate({'borderWidth': '3px','borderColor': '#f37736'},100);
    			});
    			$("td").focusout(function()
    			{
    				$(this).css("outline","none");//.animate({'borderWidth': '1px','borderColor': 'none'},500);
    			});
    			
    		});
    input
    		{
    			width:100%;
    			border:none;	
    		}
    <html>
    	<head>
    	<title>Web Grid Using Arrow Key</title>
    	<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
      </head>
    <body>
    		<h1>Web Grid Table</h1>
    		<div id="abc" class="table_here" role="grid">
    			<table class="table" border="1" style="width:50%; padding:15px;">
    				<tr>
    					<th>Name</th>
    					<th>Email</th>
    					<th>Mobile</th>
    					<th>Address</th>
    				</tr>
    				<tr role="row">
    					<td role="gridcell" tabindex="0" aria-label="name" aria-describedby="f2_key">
    						<input type="text" class="link" tabindex="-1" name="name" aria-label="name">
    					</td>
    					<td role="gridcell" tabindex="-1" aria-label="Email Id" aria-describedby="f2_key">
    						<input type="text" class="link" tabindex="-1" name="email" aria-label="email">
    					</td>
    					<td role="gridcell" tabindex="-1" aria-label="Mobile Number" aria-describedby="f2_key">
    						<input type="text" class="link" tabindex="-1" name="mob" aria-label="mobile">
    					</td>
    					<td role="gridcell" tabindex="-1" aria-label="Address" aria-describedby="f2_key">
    						<input type="text" class="link" tabindex="-1" name="add" aria-label="address">
    					</td>
    					<p id="f2_key" style="display:none;" aria-hidden="true">Press F2 Key To Edit cell</p>
    				</tr>
    				<tr role="row">
    					<td role="gridcell" tabindex="-1" aria-label="name" aria-describedby="f2_key">
    						<input type="text" tabindex="-1" class="link" name="name">
    					</td>
    					<td role="gridcell" tabindex="-1" aria-label="Email Id" aria-describedby="f2_key">
    						<input type="text" tabindex="-1" class="link" name="email">
    					</td>
    					<td role="gridcell" tabindex="-1" aria-label="Mobile Number" aria-describedby="f2_key">
    						<input type="text" tabindex="-1" class="link" name="mob">
    					</td>
    					<td role="gridcell" tabindex="-1" aria-label="Address" aria-describedby="f2_key">
    						<input type="text" tabindex="-1" class="link" name="add">
    					</td>
    				</tr>
    								<tr role="row">
    					<td role="gridcell" tabindex="-1" aria-label="name" aria-describedby="f2_key">
    						<input type="text" tabindex="-1" class="link" name="name">
    					</td>
    					<td role="gridcell" tabindex="-1" aria-label="Email Id" aria-describedby="f2_key">
    						<input type="text" tabindex="-1" class="link" name="email">
    					</td>
    					<td role="gridcell" tabindex="-1" aria-label="Mobile Number" aria-describedby="f2_key">
    						<input type="text" tabindex="-1" class="link" name="mob">
    					</td>
    					<td role="gridcell" tabindex="-1" aria-label="Address" aria-describedby="f2_key">
    						<input type="text" tabindex="-1" class="link" name="add">
    					</td>
    				</tr>
          </table>
        </div>
       </body>
    </html>
    希望这对你有帮助..!!

    【讨论】:

      【解决方案3】:

      以下是使用 onkeydown 事件并使用 previousElementSiblingnextElementSibling 属性的普通 JavaScript 解决方案。

      https://jsfiddle.net/rh5aoxsL/

      使用 tabindex 的问题在于您无法像在 Excel 中那样导航,而您可以离开电子表格本身。

      HTML

      <table>
        <tbody>
          <tr>
            <td id='start'>1</td>
            <td>2</td>
            <td>3</td>
            <td>4</td>
          </tr>
          <tr>
            <td>5</td>
            <td>6</td>
            <td>7</td>
            <td>8</td>
          </tr>
          <tr>
            <td>9</td>
            <td>10</td>
            <td>11</td>
            <td>12</td>
          </tr>
          <tr>
            <td>13</td>
            <td>14</td>
            <td>15</td>
            <td>16</td>
          </tr>
        </tbody>
      </table>
      

      CSS

      table {
        border-collapse: collapse;
        border: 1px solid black;
      }
      table td {
        border: 1px solid black;
        padding: 10px;
        text-align: center;
      }
      

      JavaScript

      var start = document.getElementById('start');
      start.focus();
      start.style.backgroundColor = 'green';
      start.style.color = 'white';
      
      function dotheneedful(sibling) {
        if (sibling != null) {
          start.focus();
          start.style.backgroundColor = '';
          start.style.color = '';
          sibling.focus();
          sibling.style.backgroundColor = 'green';
          sibling.style.color = 'white';
          start = sibling;
        }
      }
      
      document.onkeydown = checkKey;
      
      function checkKey(e) {
        e = e || window.event;
        if (e.keyCode == '38') {
          // up arrow
          var idx = start.cellIndex;
          var nextrow = start.parentElement.previousElementSibling;
          if (nextrow != null) {
            var sibling = nextrow.cells[idx];
            dotheneedful(sibling);
          }
        } else if (e.keyCode == '40') {
          // down arrow
          var idx = start.cellIndex;
          var nextrow = start.parentElement.nextElementSibling;
          if (nextrow != null) {
            var sibling = nextrow.cells[idx];
            dotheneedful(sibling);
          }
        } else if (e.keyCode == '37') {
          // left arrow
          var sibling = start.previousElementSibling;
          dotheneedful(sibling);
        } else if (e.keyCode == '39') {
          // right arrow
          var sibling = start.nextElementSibling;
          dotheneedful(sibling);
        }
      }
      

      【讨论】:

      • 我制作了一个 jquery 版本并将其扩展为能够跨表导航,同时将一个类应用于“focussed”元素 - 虽然我不得不使用 $(document).keydown(function(e) {...,但我很失望并且我不能使用更有针对性的东西,例如$(document).on("keydown", "td", function(e) {... - 有谁知道这是否只是因为tabindex 没有应用于&lt;td&gt; - 我不想将tabindex 应用于&lt;td&gt;'s,因为表格动态添加和删除。有没有办法更具体地打电话给on()jsFiddle
      • 它还有:点击聚焦单元格,在行首或行尾时,左箭头或右箭头移动到上一行或下一行,如果存在,否则移动到上一个或下一个表存在,以及 Enter 按键事件的处理程序。
      • 您能否编辑您的小提琴并添加页面包含多个表格时的逻辑。您当前的解决方案仅在一张桌子上效果最佳。例如当我们点击特定表格后,该表格的导航将开始。
      • 我们可以对角材料表也应用相同的代码
      【解决方案4】:

      根据我在其他一些帖子中找到的信息,我想通了。我把它们卷在一起,结果很完美。

      注意:您必须在每个&lt;td&gt; 上添加一个tabindex 属性以允许导航。

      这里是the jsfiddle。下面分解出相同的代码。

      HTML:

      <table>
          <thead>
              <tr>
                  <th>Col 1</th>
                  <th>Col 2</th>
                  <th>Col 3</th>
                  <th>Col 4</th>
                  <th>Col 5</th>
                  <th>Col 6</th>
                  <th>Col 7</th>
                  <th>Col 8</th>
              </tr>
          </thead>
          <tbody>
              <tr>
                  <td tabindex="1">1</td>
                  <td tabindex="2">2</td>
                  <td tabindex="3">3</td>
                  <td tabindex="4">4</td>
                  <td tabindex="5">5</td>
                  <td tabindex="6">6</td>
                  <td tabindex="7">7</td>
                  <td tabindex="8">8</td>
              </tr>
              <tr>
                  <td tabindex="10">10</td>
                  <td tabindex="11">11</td>
                  <td tabindex="12">12</td>
                  <td tabindex="13">13</td>
                  <td tabindex="14">14</td>
                  <td tabindex="15">15</td>
                  <td tabindex="16">16</td>
                  <td tabindex="17">17</td>
              </tr>
          </tbody>
      </table>
      
      <div id="edit">
          <form>
              <input type="text" id="text" value="To edit..." />
              <input type="submit" value="Save" />
          </form>
      </div>
      

      CSS:

      * {
          font-size: 12px;
          font-family: 'Helvetica', Arial, Sans-Serif;
          box-sizing: border-box;
      }
      
      table, th, td {
          border-collapse:collapse;
          border: solid 1px #ccc;
          padding: 10px 20px;
          text-align: center;
      }
      
      th {
          background: #0f4871;
          color: #fff;
      }
      
      tr:nth-child(2n) {
          background: #f1f1f1;
      }
      td:hover {
          color: #fff;
          background: #CA293E;
      }
      td:focus {
          background: #f44;
      }
      
      .editing {
          border: 2px dotted #c9c9c9;
      }
      
      #edit { 
          display: none;
      }
      

      jQuery:

      var currCell = $('td').first();
      var editing = false;
      
      // User clicks on a cell
      $('td').click(function() {
          currCell = $(this);
          edit();
      });
      
      // Show edit box
      function edit() {
          editing = true;
          currCell.toggleClass("editing");
          $('#edit').show();
          $('#edit #text').val(currCell.html());
          $('#edit #text').select();
      }
      
      // User saves edits
      $('#edit form').submit(function(e) {
          editing = false;
          e.preventDefault();
          // Ajax to update value in database
          $.get('#', '', function() {
              $('#edit').hide();
              currCell.toggleClass("editing");
              currCell.html($('#edit #text').val());
              currCell.focus();
          });
      });
      
      // User navigates table using keyboard
      $('table').keydown(function (e) {
          var c = "";
          if (e.which == 39) {
              // Right Arrow
              c = currCell.next();
          } else if (e.which == 37) { 
              // Left Arrow
              c = currCell.prev();
          } else if (e.which == 38) { 
              // Up Arrow
              c = currCell.closest('tr').prev().find('td:eq(' + 
                currCell.index() + ')');
          } else if (e.which == 40) { 
              // Down Arrow
              c = currCell.closest('tr').next().find('td:eq(' + 
                currCell.index() + ')');
          } else if (!editing && (e.which == 13 || e.which == 32)) { 
              // Enter or Spacebar - edit cell
              e.preventDefault();
              edit();
          } else if (!editing && (e.which == 9 && !e.shiftKey)) { 
              // Tab
              e.preventDefault();
              c = currCell.next();
          } else if (!editing && (e.which == 9 && e.shiftKey)) { 
              // Shift + Tab
              e.preventDefault();
              c = currCell.prev();
          } 
      
          // If we didn't hit a boundary, update the current cell
          if (c.length > 0) {
              currCell = c;
              currCell.focus();
          }
      });
      
      // User can cancel edit by pressing escape
      $('#edit').keydown(function (e) {
          if (editing && e.which == 27) { 
              editing = false;
              $('#edit').hide();
              currCell.toggleClass("editing");
              currCell.focus();
          }
      });
      

      【讨论】:

      • 我正在尝试将其应用于主干视图。导航似乎根本不起作用。我必须点击选项卡才能将注意力集中到表格的行上,然后按箭头键没有任何效果。为了使这项工作与骨干一起工作,我必须做哪些更改?
      • @aliirz,我不确定。我看过Backbone,但我从未使用过它。如果您弄清楚需要更改的内容,请发布此问题的另一个答案;我想看看你的作品。