关于使用键盘来在Web页的输入框中导航这个事我一周前就说了,今天才把它整理完,真是不好意思。不过整理后的代码结构清晰、使用方便、价格公道、童叟无欺。
关于使用键盘来在Web页的输入框中导航这个事我一周前就说了,今天才把它整理完,真是不好意思。不过整理后的代码结构清晰、使用方便、价格公道、童叟无欺。
主要的核心技术就是两个,一个是:使用TextRange获取输入框中光标的位置,另一个是:为Web页中的TABLE对象创建一个映射表。其中获取输入框中的光标位置,是为了使用户在输入框间切换时,还能在同一个输入框中继续使用上下左右键移动光标,否这一但使用方向键就切换回很难与使用的。建立Table的映射表是为了解决查找当前输入框的切换目标输入框。
由于昨天介绍了Mapping的方法,所以获取sibling的输入框就变的非常的简单,代码为:
<script language="javascript">
KeyNavigation.GetSiblingCell = function(tbl, cell, dir)
{
KeyNavigation.CreateTableMapping(tbl);
var colIndex = -1;
var row = cell.parentElement;
var rowIndex = row.rowIndex;
for ( var i=0 ; i < tbl.columnCount ; ++i )
{
if ( tbl.tableMap[rowIndex][i] == cell )
{
colIndex = i;
break;
}
}
if ( colIndex == -1 )
{
throw "can't find the cell in table.";
}
var siblingCell = null;
var incV, incH;
incV = 1, incH = 1;
switch(dir)
{
case KeyNavigation.Up :
{
incV = -1;
}
case KeyNavigation.Down :
{
for ( var i=1 ; i < tbl.rowCount ; ++i )
{
var tmpRowIdnex = (tbl.rowCount+rowIndex+i*incV)%tbl.rowCount;
siblingCell = tbl.tableMap[tmpRowIdnex][colIndex];
if ( KeyNavigation.IsAvailableCell(siblingCell) )
{
break;
}
}
break;
}
case KeyNavigation.Left :
{
incH = -1;
}
case KeyNavigation.Right :
{
for ( var i=1 ; i < tbl.columnCount ; ++i )
{
var tmpColumnIdnex = (tbl.columnCount+colIndex+i*incH)%tbl.columnCount;
siblingCell = tbl.tableMap[rowIndex][tmpColumnIdnex];
if ( KeyNavigation.IsAvailableCell(siblingCell) )
{
break;
}
}
break;
}
}
return siblingCell;
};
script>
由于up和down,left和right其实是相同的搜索代码,所以使用一个incV和incH把它们合并成了一段代码。如果在被查询单元格的同列或同行上没有别的输入框,那么siblingCell返回null。
由于使用了Table的Mapping结果后,键盘的处理反而比查询sibling cell还复杂那么一点点了,处理代码如下:
<script language="javascript">
KeyNavigation.DoKeyDown = function(elmt)
{
var tbl = elmt;
var input = event.srcElement;
var keyCode = event.keyCode;
var iPsn = KeyNavigation.GetCaretPosition(input);
var cell = input.parentElement;
var siblingCell = null;
var directionV = KeyNavigation.Down;
switch(keyCode)
{
case 27 : /**//* Escape */
{
KeyNavigation.GetCaretPosition(txb);
break;
}
case 38 : /**//* Move Up */
{
directionV = KeyNavigation.Up;
}
case 40 : /**//* Move Down */
{
var isTab = false;
if ( input.tagName == 'TEXTAREA' )
{
if ( iPsn == KeyNavigation.PreviousCursorPosition ||
( ( iPsn == 0 && directionV == KeyNavigation.Up ) ||
( iPsn == input.value.length && directionV == KeyNavigation.Down ) ) )
{
isTab = true;
}
else
{
KeyNavigation.PreviousCursorPosition = iPsn;
}
}
if ( input.tagName == 'INPUT' || isTab )
{
siblingCell = KeyNavigation.GetSiblingCell(tbl, cell, directionV);
}
break;
}
case 37 : /**//* Move Left */
{
if ( iPsn == 0 )
{
siblingCell = KeyNavigation.GetSiblingCell(tbl, cell, KeyNavigation.Left);
}
break;
}
case 39 : /**//* Move Right */
{
if ( iPsn == input.value.length )
{
siblingCell = KeyNavigation.GetSiblingCell(tbl, cell, KeyNavigation.Right);
}
break;
}
}
if ( siblingCell && siblingCell != cell )
{
var siblingInput = null;
var inputs = siblingCell.all.tags('INPUT');
if ( inputs.length > 0 )
{
siblingInput = inputs[0];
siblingInput.focus();
}
else
{
siblingInput = siblingCell.all.tags('TEXTAREA')[0];
siblingInput.focus();
}
}
};
script> 麻烦的地方是,需要区别对待INPUT和TEXTAREA这两个输入框元素。对于INPUT的处理很简单,UP和DOWN直接就执行跳离,LEFT的时候看看光标POSITION是否为0,RIGHT的时候看看是否为input.value.length。而TEXTAREA的LEFT和RIGHT和INPUT的处理是一样的,但是它的UP和DOWN为了让用户用起来比较的自然,加入了一个KeyNavigation.PreviousCursorPosition,用来记忆上一次的按键时TEXTAREA里的光标位置,当响应UP和DOWN的时候,如果光标不在输入框TEXTAREA的两头(0和input.value.length),那么需要KeyNavigation.PreviousCursorPosition==iPsn才执行跳离操作。
说了半天不明白,自己试试Navigate的效果就知道了。
操作体验还算自然吧
?