0

I'm trying to implement a simple dropdown with a slideDown effect. To build this effect I used a CSS transition applied to the height property.

Problem is that if I press the Tab ↹ key, any targetable element (tab stops) inside the dropdown will be targeted, even when it is hidden as I am not using display: none.

Here's the code:

const button = document.getElementById('button');
const dropdown = document.getElementById('dropdown');

dropdown.style.setProperty('height', 'auto', 'important');
dropdown.style.setProperty('height', dropdown.clientHeight + 'px');

button.addEventListener('click', function(e) {
  e.target.classList.toggle('active');
  e.target.nextElementSibling.classList.toggle('active');
});
#dropdown {
  overflow: hidden;
  transition: height 330ms linear;
  background-color: lightgrey;
  height: 200px;
}

#dropdown:not(.active) {
  height: 0 !important;
}

#dropdown.active {
  visibility: visible;
}
<button id="button">Click me!</button>

<div id="dropdown">
  <a href="#">I should not be accessible with tab when dropdown is hidden</a>
</div>

<div id="info">This link will be focused after three tabs, instead of two: <a href="#">Tab me!</a></div>

I have tried to modify the code a little bit using the transitionend event to add and remove display: none when the transition ends thus making any targetable elements inside the dropdown untargetable, but this messes up with the starting animation of the transition.

See:

const button = document.getElementById('button');
const dropdown = document.getElementById('dropdown');

dropdown.style.setProperty('height', 'auto', 'important');
dropdown.style.setProperty('height', dropdown.clientHeight + 'px');
dropdown.classList.add('hidden');

button.addEventListener('click', function(e) {
  if (!e.target.classList.contains('active'))
    dropdown.classList.remove('hidden');


  e.target.classList.toggle('active');
  e.target.nextElementSibling.classList.toggle('active');
});

dropdown.addEventListener('transitionend', function(e) {
  dropdown.classList.add('hidden');
});
#dropdown {
  overflow: hidden;
  transition: height 330ms linear;
  background-color: lightgrey;
  height: 200px;
}

#dropdown:not(.active) {
  height: 0 !important;
}

#dropdown.active {
  visibility: visible;
}

a {
  display: block; /* so it doesn't move when dropdown is hidden */
}

.hidden {
  display: none;
}
<button id="button">Click me!</button>

<div id="dropdown">
  <a href="#">I should not be accessible with tab when dropdown is hidden</a>
</div>

<div id="info">This link will now be focused after <strong>two</strong> tabs, as expected: <a href="#">Tab me!</a></div>
Flerex
  • 324
  • 2
  • 12
  • so, you need the tab key to not do its natural function? – assembler Dec 19 '17 at 14:25
  • @assembler I need the tab key to do its natural function, but as I'm not actually hidding the dropdown with display: none, the tab key will stop at the link inside the dropdown, which is not what the user expects. – Flerex Dec 19 '17 at 14:26
  • so, if `display: none;` the tab should jump to the next component... right? – assembler Dec 19 '17 at 14:29
  • @assembler Exactly. If the `display` property in `#dropdown` is set to `none`, the link inside it won't be targetable by the tab key. – Flerex Dec 19 '17 at 14:31
  • set the component `tabindex` to a negative value according to https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/tabIndex – assembler Dec 19 '17 at 14:37
  • @assembler I am not able to know the content of the dropdown, so the only way would be to get all child elements of it and add this attribute, which I don't think is an efficient solution. – Flerex Dec 19 '17 at 14:42

2 Answers2

1

Try setting attribute "tabindex" to -1, this should prevent link from selecting with tab. You can also simply remove this attribute with JS when dropdown is active

Elijah Ellanski
  • 892
  • 1
  • 8
  • 23
  • `tabindex` can only be applied to the element that can't be selected with tab, not the parent. If I want to do this with JS I would have to get all content from a dropdown and add this attribute (and remove it when is visible). Is this the only way? – Flerex Dec 19 '17 at 14:41
0

You can modify the element’s tabIndex (-1 to make it untargetable, very high number to make it targeted last). How to ignore HTML element from tabindex?

zhark
  • 391
  • 3
  • 8