用JavaScript检查活动元素是否具有特定类

3
我正在编写一个单字段表格,希望按下回车键可以将表格移至下一个输入字段。由于页面上还有另一个表格,因此我只想在该表格中的一个输入框是activeElement时按下回车键才能移动到下一个字段。
我已经通过以下看起来非常冗长的if()语句实现了这一点:
document.addEventListener( 'keydown', function( ev ) {
        var keyCode = ev.keyCode || ev.which;
        var ids = [ 'this', 'that', 'there', 'thing', 'other' ];
        if ( document.getElementById( ids[0] ) === document.activeElement || document.getElementById( ids[1] ) === document.activeElement || document.getElementById( ids[2] ) === document.activeElement || document.getElementById( ids[3] ) === document.activeElement || document.getElementById( ids[4] ) === document.activeElement) {
            if( keyCode === 13 ) {
                ev.preventDefault();
                self._nextQuestion();
            }
        }
    } );

每个输入都是同一个类:.questions。我尝试了以下内容:
document.addEventListener( 'keydown', function( ev ) {
    var keyCode = ev.keyCode || ev.which;
    var ids = [ 'this', 'that', 'there', 'thing', 'other' ];
    if ( document.querySelector('.questions') === document.activeElement) {
        if( keyCode === 13 ) {
            ev.preventDefault();
            self._nextQuestion();
        }
    }
} );

当然,这只访问页面上.questions的第一个实例。我不想迭代节点,因为它似乎并没有比我已经拥有的更好。
我是初学者,正在寻找更简洁的逻辑。

2
你能检查一下 document.activeElement.className,也许可以使用 .indexOf 方法吗? - qxz
表单有 ID 或其他可用于区分它们的标识吗?如果有,请监听表单而不是文档。 - adeneo
是的,它有一个ID和一个类,但当光标在输入框中时,它是唯一的“activeElement”,而不是表单。 - J. Adam Connor
1
我的意思是,事件会冒泡,首先到表单,然后到文档,如果你只想捕获一个表单上的关键事件,请使用选择器来定位该表单。 - adeneo
4个回答

7
只需检查activeElement是否具有questions类即可。
var pattern = /(?:^|\s)questions(?:\s|$)/
if (document.activeElement.className.match(pattern)) {
  ...
}

Squint提供了一个更好的正则表达式,可以考虑类名中更多奇怪的情况。

2
FYI:document.activeElement.classList.contains('questions') - adeneo
1
更简单,但失去了IE10兼容性。 - CollinD
正则表达式在某些情况下可能会失败,因为有些有效的类字符会被视为单词边界。要使用正则表达式,我认为你需要这样做:/(?:^|\s)questions(?:\s|$)/ - user1106925
这是个好观点,squint。我认为qzx的方法或者只是split可能是最好的选择。我会更新我的回答。 - CollinD
qxz的解决方案存在类似的问题,即它只考虑了一种空格字符。从技术上讲,它也应该包括制表符和换行符,尽管它们可能不太可能出现,这使得正则表达式非常实用。 - user1106925
对于那些想知道为什么不使用 document.activeElement.className === "foo" 的人,请参见我在此页面上的答案中的评论。 - J. Adam Connor

3

试试这个:

if ((" "+document.activeElement.className+" ").indexOf(" questions ") > -1) {
    ...
}

2
首先,您可以利用classList API检查元素是否具有给定的className,例如document.activeElement && document.activeElement.classList.contains('question')
所有提出的方法都非常有帮助,应该可以解决问题。另一方面,您可能希望将应用程序的状态保存在一个地方,以便更轻松地管理它。通过这样做,您使应用程序更可预测且更易于调试。
以下是您可能要采取的方法:
HTML代码:
<input class="question" value="1"/>
<input class="question" value="2"/>
<input class="question" value="3"/>
<input class="question" value="4"/>
<input class="question" value="5"/>

JavaScript代码:

var component = {
  activeQuestion: 0,
  questionSelector: '.question',
  get activeQuestionElement () {
    return component.allQuestionElements[component.activeQuestion];
  },
  get allQuestionElements () {
    return document.querySelectorAll(component.questionSelector);
  },
  next: function () {
    var activeElement = document.activeElement;

    if (activeElement && activeElement === component.activeQuestionElement && component.allQuestionElements[component.activeQuestion+1]) {
      component.activeQuestion++;
      component.highlightQuestion();
    }
  },
  highlightQuestion: function () {
    component.activeQuestionElement.focus();
  },
  install: function () {
    document.addEventListener('keyup', function (event) {
      if (event.which === 13) {
        component.next.call(component);
      }
    });
    document.addEventListener('click', function (event) {
      var className = component.questionSelector.slice(1);

      if (event.target.classList.contains(className)) {
        component.activeQuestion = [].slice.call(document.querySelectorAll(component.questionSelector)).indexOf(event.target);
        component.highlightQuestion();
      }
    })
  }
};

component.install();

如上所示,component是一个单独的对象实例,保存着有用的信息,比如activeQuestion索引、问题选择器和一些返回DOM元素的计算属性。 install方法绑定了事件监听器,管理当事件发生时的状态。
以下是演示: http://jsfiddle.net/maciejsmolinski/xzyvye8z/embedded/result/ 当你点击任何字段并聚焦时,按下enter键会将焦点移到下一个字段。此外,单击任何字段也会更改活动问题。您可以通过修改我上面发布的代码轻松更改其行为。这只是一个基本框架,您可以在其上构建自己的功能。
这是一个非常基本的组件。你可以在它上面构建任何东西。您可以引入ES6语法(类,箭头函数,const而不是var),但我故意保持那部分不变,以使其更容易理解。
希望对您有所帮助!

0

你们指导了我正确的方向,使用 document.activeElement.className ,所以我给所有提到它的人点了赞,但是这里似乎最简洁的解决方案是:

document.addEventListener( 'keydown', function( ev ) {
        var keyCode = ev.keyCode || ev.which;
        // enter
        if ( document.activeElement.className === 'questions') {
            if( keyCode === 13 ) {
                ev.preventDefault();
                self._nextQuestion();
            }
        }
    } );

在我把它标记为答案之前,我会给你们一些时间来批评这个...


1
如果 activeElement 有多个类,则此方法会失败。 - CollinD
@CollinD,你能再解释一下吗?你是指如果类有多个实例吗?还是指同时有多个类处于活动状态? - J. Adam Connor
1
元素的 className 例如 <div class="foo bar baz"> 实际上是 foo bar baz。对于查询目的(如css、document.querySelector等),它具有这三个类中的所有3个。但如果你只检查 className === "foo",那么它将会失败。 - CollinD
好的,我知道你在说什么。这个元素只有一个“className”,而且我看不到它会改变。考虑到这一点,用这种方式做是否会成为不良实践呢? - J. Adam Connor
1
现在修复它几乎不需要任何努力,并防止一个非常令人困惑的错误在您的项目开发中出现。但这取决于您作为维护者。只是似乎没有必要限制自己(我知道我在足够多的项目上说过“那不会改变”,所以当我开始这些日子时就闭嘴)。这个解决方案总体上缺乏,对于可能找到这个问题的其他人提供了较少的实用程序。我至少会编辑一下,说明它无法与具有多个类或类名称周围带空格的元素一起使用。 className ==='foo bar baz'同样糟糕。不能重新排列它们。 - CollinD
好的,对我来说已经足够了,希望对访问者是有指导意义的。我会接受你的答案。我也对这篇文章的标题不是很满意。你有什么建议吗? - J. Adam Connor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接