Hey @adamslowe,
that one is a great possibility for me to learn something about focus. I always thought it’s about tabindex and accessibility. Never heard of focus() and blur() before.
I’ve developed a quick snippet which should do exactly what you want to achieve. It’s based on the discussion here javascript - Is there a cross-browser solution for monitoring when the document.activeElement changes? - Stack Overflow.
What is basically does is adding a eventlistener to the window which listens to every focus() event. Whenever a focus is called it checks if the current focused element ( document.activelement
) is the same as the last item in your dropdown.
If the currently focused element is the last element in the dropdown it adds another eventlistener which listens to the blur() events.
So the next time the blur event get’s called it probably means that the dropdown should get closed. So far so good.
EDIT: While I was writing the lines above I realized that there could be a case where you move backwards, in that case the solution above would also close the dropdown.
What I’ve found after another research is the eventlistener (‘focusin’). Don’t know if focus also holds that information but focusin seemed perfect here! So with focusin you have access to the target of the next focus and also to the current focused element. With that I was able to write a small javascript-snippet which worked in my tests perfectly fine!
The javascript-code ready to copy and paste is here (I’ve also added the eventlistener DOMContentLoaded, which should prevent render-blocking if you place it in the head of your file, but you should place that file at the bottom of your body! ) And a few lines underneath the javascript code there would be a full HTML-Example to copy & paste where you can test that. The last input of the long row should simulate your last dropdown-item!
JS-Snippet:
document.addEventListener("DOMContentLoaded", () => {
// VARIABLES:
var dropdownWrapper = document.querySelector("#dropdown-wrapper");
var dropdownItems = document.querySelectorAll(".dropdown-item");
var lastItem = dropdownItems[dropdownItems.length - 1];
addEventListener("focusin", (event) => {
var hideDropdown = false;
// Means we are now jumping from the last dropdown-item in some direction:
if (event.relatedTarget == lastItem) {
var hideDropdown = true;
// Check if we move backwards, because if we jump to the element before the last dropdown element we probably don't want to hide our dropdown! ;-)
dropdownItems.forEach((item) => {
// Set the variable hideDropdown to false if we move backwards (means one of the loop items is the target of the next focus);
if (item == event.target) {
hideDropdown = false;
}
});
// After we looped through each element and are sure that we are moving forward we can hide the dropdown element:
if (hideDropdown) {
dropdownWrapper.style.display = "none";
}
}
});
});
Full HTML (For Testing):
<html>
<body>
<input type="text" id="Nav-Item1" name="Text2" value="" />
<div id="dropdown-wrapper">
<input type="text" id="1" class="dropdown-item" name="Text1" value="" />
<input type="text" id="2" class="dropdown-item" name="Text1" value="" />
<input type="text" id="3" class="dropdown-item" name="Text1" value="" />
<input type="text" id="lastitem" class="dropdown-item" name="Text1" value="" />
</div>
<input type="text" id="Nav-Item2" name="Text2" value="" />
<script>
document.addEventListener("DOMContentLoaded", () => {
// VARIABLES:
var dropdownWrapper = document.querySelector("#dropdown-wrapper");
var dropdownItems = document.querySelectorAll(".dropdown-item");
var lastItem = dropdownItems[dropdownItems.length - 1];
addEventListener("focusin", (event) => {
var hideDropdown = false;
// Means we are now jumping from the last dropdown-item in some direction:
if (event.relatedTarget == lastItem) {
var hideDropdown = true;
// Check if we move backwards, because if we jump to the element before the last dropdown element we probably don't want to hide our dropdown! ;-)
dropdownItems.forEach((item) => {
// Set the variable hideDropdown to false if we move backwards (means one of the loop items is the target of the next focus);
if (item == event.target) {
hideDropdown = false;
}
});
// After we looped through each element and are sure that we are moving forward we can hide the dropdown element:
if (hideDropdown) {
dropdownWrapper.style.display = "none";
}
}
});
});
</script>
</body>
</html>