高级 AJAX ListBox 组件 v0.4
响应特殊的键盘事件以改善行为。
引言
在 我之前的文章 中,我们修改了我们的 ListBox 以解决由于将水平滚动与滚动状态保存分离而引入的浏览器兼容性问题。 在本文中,我们将关注用户界面,并最终使此控件达到 Beta 级别。
背景
让我们回顾一下我们在第二篇文章中提出的需求清单
- 使用 SHIFT 和 CONTROL 键一次选择多个不连续的项目的操作比较困难或不可能,因为它们的
onkeydown
事件会触发滚动条根据选择的第一个(或多个)项目重新定位。 我们应该读取事件的keyCode
,并绕过类似这样的“元”按键按下的滚动代码。 - 使用鼠标滚轮滚动浏览项目的功能取决于浏览器以及用于加载页面的版本。 我们希望使鼠标滚动成为所有目标浏览器都支持的可配置选项。
垂直和水平滚动条位置仅在控件的HorizontalScrollEnabled
属性显式设置为true
时才会在回发时保留。 即使HorizontalScrollEnabled
为false
,我们也应该能够保留滚动状态。- 我们的事件处理策略不支持 Firefox 1.0,因为某些更改滚动位置的用户交互不会执行
_onContainerScroll
事件处理程序。 此外,如果您尝试调试此处理程序,您会注意到 IE 可能会为一次鼠标点击或按键触发四次。 拖动滚动条会导致我的 CPU 短暂跳到 30% 或更高,具体取决于我的滚动速度。 如此多次地更新隐藏字段会对客户端计算机的处理器造成不必要的负载。
从可用性的角度来看,我们面临的最大问题是 #1。 我们将从直观的角度开始,然后看看为什么我们需要做一些技巧来模仿正常的 ListBox 行为。
震撼和敬畏
isMetaKeyPress : function(e)
{
if (e.keyCode == 16 // SHIFT key
|| e.keyCode == 17 // CONTROL key
)
return true;
return false;
}
,
_onContainerKeyDown : function(e)
{
if (!this.isMetaKeyPress(e)) {
// previous code all goes inside this conditional block
}
}
,
这减轻了部分 CONTROL 和 SHIFT 键问题。 这将为控件添加一些色彩,但现在让我们把它提升一个档次...
轰!
isMetaKeyPress : function(e)
{
if (e.keyCode == 8 // BACKSPACE key
|| e.keyCode == 9 // TAB key
|| e.keyCode == 16 // SHIFT key
|| e.keyCode == 17 // CONTROL key
|| e.keyCode == 18 // ALT key
|| e.keyCode == 19 // PAUSEBREAK key
|| e.keyCode == 20 // CAPSLOCK key
|| e.keyCode == 27 // ESC key
|| e.keyCode == 45 // INSERT key
|| e.keyCode == 91 // WINDOWS key
|| e.keyCode == 93 // CONTEXT key
|| e.keyCode == 112 // F1 key
|| e.keyCode == 113 // F2 key
|| e.keyCode == 114 // F3 key
|| e.keyCode == 115 // F4 key
// skip F5 key, since it reloads the page
|| e.keyCode == 117 // F6 key
|| e.keyCode == 118 // F7 key
|| e.keyCode == 119 // F8 key
|| e.keyCode == 120 // F9 key
|| e.keyCode == 121 // F10 key
|| e.keyCode == 122 // F11 key
|| e.keyCode == 123 // F12 key
|| e.keyCode == 127 // DELETE key
|| e.keyCode == 144 // NUMLOCK key
|| e.keyCode == 145 // SCROLLLOCK key
)
return true;
return false;
}
,
因为普通的 ASP ListBox
在这些按键时不会滚动,所以这更适合那些留着长而滑的指甲的数据录入员。
保持最佳状态
有了这段代码,我们仍然存在一些问题。 从上到下选择一大块项目(使用 SHIFT 键)时,当您单击鼠标时,DIV
会跳回到您选择的顶部项目。 这是因为鼠标单击会触发 onchange
事件,而 SHIFT keyCode
未在该事件中捕获。 我们可以通过添加不应调用 updateListBoxScrollPosition()
函数的其他情况来解决这个问题。
supressAutoScroll : function(e)
{
if (this.isMetaKeyPress(e))
return true;
var selectedItems = this.getSelectedItemCount(true);
if (selectedItems > 1)
return true;
return false;
}
,
getSelectedItemCount : function(breakOnMultiple)
{
var selectedItems = 0;
for (i = 0; i<this.get_element().options.length; i++)
{
if (this.get_element().options[i].selected == true)
selectedItems++;
if (breakOnMultiple && selectedItems > 1)
break;
}
return selectedItems;
}
,
_onChange : function(e)
{
if (this.get_element() && !this.get_element().disabled)
{
if (!this.supressAutoScroll(e)) {
updateListBoxScrollPosition(this.get_elementContainer(),
this.get_element(), null);
}
// rest of the code stays the same
}
}
,
_onContainerKeyDown : function(e)
{
if (!this.supressAutoScroll(e)) {
// change condition from isMetaKeyPress
// to supressAutoScroll above
}
}
,
我们在这里所做的是添加了两个其他的帮助函数。 我们以一种可以在以后需要时被真实的 get_selectedItemCount
属性重用的方式编写了 getSelectedItemCount
函数。 但是,为了满足此要求,我们只需要知道在 ListBox
中是否选择了多个项目。 因此,我们传递了一个参数,该参数将导致计数停止并在选择了多个项目时返回 2。 我们从 supressAutoScroll
中执行此操作,以便我们可以组合这两个应禁止自动 DIV
滚动的情况
- 当
_onContainerKeyDown
响应元按键按下而执行时,并且 - 当在选择了多个项目时执行
_onChange
或_onContainerKeyDown
。
剩下的就是使用 supressAutoScroll
函数,在调用导致两个事件处理程序中自动滚动行为的 updateListBoxScrollPosition
方法之前执行条件检查。
吹毛求疵
关于普通的 ListBox
的一件事是用户可以选择一个项目,按住 SHIFT 键,然后点击另一个键来选择多个项目。 我们的 ListBox 也可以做到这一点,但是普通的 ListBox
会跳转以显示新选中的项目,而我们的不会。 此外,PAGEUP 和 PAGEDOWN 键不起作用,因为 ListBox 没有内部滚动条。 它们就像 HOME 和 END 键一样(确实按预期工作)。
虽然我确信可以模仿这些行为,但我们正在达到收益递减的地步。 就像我在第一篇文章中说的那样,完美主义是一种疾病。 当前控件对于绝大多数用户来说将足够直观。 现在可以使用 SHIFT 和 CONTROL 键单击选择多个项目,而不会导致滚动位置完全混乱,这足以从列表中勾选此要求。