I have written some ES2015 JavaScript that adds a class to a root element when one of it's immediate children is focused.
Here is the markup for the code to be run on:
<div id="my-root-element">
<input type="text">
<button type="submit>
</div>
And the JS itself:
const rootElement = document.getElementById('my-root-element');
[...rootElement.children].forEach((element, _, children) => {
const update = () => {
const focused = children.indexOf(document.activeElement) !== -1;
rootElement.classList[focused ? 'add' : 'remove']('focused');
};
[ 'focus', 'blur' ].forEach((eventName) => {
element.addEventListener(eventName, () => setTimeout(update, 0));
});
});
Are there any ways to "simplify" this code or generally make it smaller? I know it's short as it is currently but I understand that ES2015 has a lot of features that can help in writing short, concise code.
Here is an actual example of the code being used (focusing either the button or the text field should make the word "Focused!" appear):
const rootElement = document.getElementById('my-root-element');
[...rootElement.children].forEach((element, _, children) => {
const update = () => {
const focused = children.indexOf(document.activeElement) !== -1;
rootElement.classList[focused ? 'add' : 'remove']('focused');
};
[ 'focus', 'blur' ].forEach((eventName) => {
element.addEventListener(eventName, () => setTimeout(update, 0));
});
});
.focused:after {
content: 'Focused!';
}
<div id="my-root-element">
<input type="text">
<button type="submit">Submit</button>
</div>
_index
to prevent collisions. I usedsetTimeout(/* thing */, 0);
as without it we end up having 1 frame whereactiveElement
is none of the child elements when clicking between them. Clicking between 2 children withoutsetTimeout
:e1.onBlur
->focused is removed from root
->e2.onFocus
->focused is added to root
. And withsetTimeout
:e1.onBlur
->setTimeout
->focused is left because activeElement is a child
->e2.onFocus
\$\endgroup\$ – Jack Wilsdon Jul 26 '16 at 15:22