【问题标题】:Finding the first focusable item of any type within a div在 div 中查找任何类型的第一个可聚焦项
【发布时间】:2010-01-05 13:59:14
【问题描述】:

我在 Javascript(使用 Prototype)中要求将光标聚焦在特定 div 中的第一个表单项上。

简单示例:

<form id="theForm">
  <div id="part1">
    <input id="aaa" .../>
    <select id="bbb">...</select>
  </div>
  <div id="part2">
    <select id="ccc">...</select>
    <input id="ddd" .../>
  </div>
</form>

我想编写一个 Javascript 函数,它采用 theForm 中一个 div 的名称并将光标聚焦在该 div 中的第一项上 - 例如

focusOnFirst('part1'); // will put focus on input "aaa"
focusOnFirst('part2'); // will put focus on select "ccc"

我想我也许可以像这样使用 Prototype 的 select 方法:

$(pDiv).select('input','select',...etc.);

但是,这会返回一个数组,其中包含所有输入,然后是所有选择,等等。它不会按照它们在 div 中出现的顺序返回项目。在我上面的示例中,这导致将焦点放在输入“ddd”而不是选择“ccc”。

另一种可能是Form getElements 方法:

$('theForm').getElements();

但是现在我需要检查每个返回的项目,看看它是否在所需的 div 内。目前我知道的唯一方法是获取它的所有祖先,看看是否有选定的 div。我可以这样做,但我担心这不是最有效的解决方案。

谁能提出更好的方法?

【问题讨论】:

    标签: javascript dom prototype


    【解决方案1】:

    如何使用 CSS3 选择器:

    $$('#theForm input:first-of-type')[0].focus()
    

    它返回一个数组,即使我们知道它只会包含一个元素,所以只需抓住第一个并聚焦它。

    I've worked up a quick example.

    --Edit OK,我才发现它错过了选择元素。

    --Edit 这似乎可行 - 选择具有name 属性的第一个元素(假设您有名称属性,可以使用其他属性):

    function focusOnFirst2(divName) {
        $$('#' + divName + ' [name]')[0].focus();
    }
    

    我已经更新了我的链接示例以包含这个以及我错误的第一次尝试。

    --最后一次编辑:)

    我已经采用了您的功能并尝试将其转过来,以便它根据 div 的子项而不是表单的子项进行搜索。我把它循环调用了 100 次,在 IE 中看到了大约 45% 的改进,我不确定在现实世界中它是否会更好:

    function focusFirstItem (divId)
    {
        if ($(divId))
        {
            var elems = $('myForm').getElements();
            var elems2 = $(divId).select('*');
            for (i=0, l=elems2.length; i<l; ++i) {
                var item = elems2[i];
                if (item.id && !item.disabled && !item.readOnly && item.visible) {
                    if (elems.find(function(s){return s.id = item.id}))
                    item.focus();
                    break;
                }
            }
        }
    }
    

    In example page with rudimentary timing.

    【讨论】:

    • 感谢,特别是添加示例。但是,当项目可以是输入或选择(或文本区域等)时,我仍然遇到同样的问题:根据您指定它们的顺序,您会得到不同的结果。如果您将项目“三”更改为选择列表并将代码更改为“$$('#' + divName + ' input:first-of-type', '#' + divName + ' select:first-of- type')[0].focus()" 然后 Div Two 按钮将焦点放在项目“Four”而不是“Three”上,除非您交换 2 个参数。尽管 Prototype 文档说“返回扩展 DOM 元素的文档顺序数组”
    • 是的,我刚刚意识到 - 根本没有考虑选择元素。
    • 你上面的代码中没有name属性,我想如果你必须添加属性,你也可以添加一个类名并选择它。
    • 我的真实代码中确实有名称和类,这只是一个快速而肮脏的例子!不幸的是,我正在尝试编写一些适用于许多现有页面的通用代码,所以我不想修改现有的 HTML。
    • 很公平 - 我选择了 name 属性,因为我的所有表单元素都有一个,无法准确回忆,但我认为这是因为 IE6 在 POST 时无法识别元素,除非有名称.
    【解决方案2】:

    这是我迄今为止想出的:

    focusFirstItem (divId)
    {
        if ($(divId))
        {
            var elems = $('myForm').getElements();
            for (i=0, l=elems.length; i<l; ++i) {
                var item = $(elems[i]);
                if (!item.disabled && !item.readOnly && item.visible) {
                    if (item.ancestors().find(function(s){return s.id == divId})) {
                        $(item).focus();
                        break;
                    }
                }
            }
        }
    }
    

    可怕,我知道。在 IE6 上运行大约需要半秒,在 FF 上运行要好得多 70ms 左右。我知道我应该能够使用 Prototype 摆脱“for”循环,但我在尝试这样做时失败了。

    【讨论】:

    • 我认为 .ancestors 调用可能是优化的好目标 - 每次循环都会收集很多相同的元素?你能计算出 $('myForm').getElements() 本身需要多长时间吗?
    • 我尝试将您的方法稍微侧向,添加到我的答案中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-27
    • 2014-05-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-03
    相关资源
    最近更新 更多