Reputation: 1734
I'm writing a single-field form and want the Enter key to advance the form to the next input field. Since there's another form on the page, I only want the Enter key to advance the form when one of the inputs in that form is the activeElement
.
I've achieved this with what seems like an extremely verbose if()
statement here:
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();
}
}
} );
Each of the inputs are the same class: .questions
. I've tried something like:
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();
}
}
} );
But of course, this only accesses the first instance of .questions
on the page. I don't want to iterate over nodes, as it doesn't seem much better than what I have already.
I'm a novice, and I'm looking for more concise logic.
Upvotes: 4
Views: 10867
Reputation: 326
First of all, you can utilise classList
api to check element for given className presence, e.g. document.activeElement && document.activeElement.classList.contains('question')
.
All the proposed approaches are really helpful and should fix the problem. On the other hand, you might want to keep you application's state in one place so you can manage it easily. By doing so, you make your application more predictable and easier to debug.
Here's an approach you might want to take:
HTML Code:
<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 Code:
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();
As you can see above, component
is a single object instance that holds useful information like activeQuestion
index, question selector, some computed properties that return DOM elements. The install
method binds event listeners which manage the state when events occur.
Here's a demo:
http://jsfiddle.net/maciejsmolinski/xzyvye8z/embedded/result/
When you click enter
when any of the fields is focused, it is going to move focus to the next one. In addition to that, clicking on any of the fields changes active question as well. You can easily alter the behaviour by modifying the code I posted above. It is just a basic skeleton you can use to build your own functionality on top of.
This is a very basic component. You can build anything on top of it. You can introduce ES6 syntax (classes, arrow functions, const
instead of var
) but I intentionally left that part untouched to make it easier to understand.
Hope it helps!
Upvotes: 3
Reputation: 7573
Just check if the activeElement
has the questions
class.
var pattern = /(?:^|\s)questions(?:\s|$)/
if (document.activeElement.className.match(pattern)) {
...
}
squint provided an improved regex that will account for more funky situations in the classname.
Upvotes: 7
Reputation: 1734
You guys pointed me in the right direction with document.activeElement.className
, so I upvoted all who mentioned it, but the most concise solution that works here seems to be this:
document.addEventListener( 'keydown', function( ev ) {
var keyCode = ev.keyCode || ev.which;
// enter
if ( document.activeElement.className === 'questions') {
if( keyCode === 13 ) {
ev.preventDefault();
self._nextQuestion();
}
}
} );
I'll give you guys some time to critique this before I mark it as the answer...
Upvotes: 0
Reputation: 3864
Try this:
if ((" "+document.activeElement.className+" ").indexOf(" questions ") > -1) {
...
}
Upvotes: 3