Reputation: 11
i have a filter dropdown that has a search button, button activates if there is no item on the list however the dropdown has multiple items in a line so what i want is to be able to navigate between them in every way using arrow keys and open their link when pressed enter, when there is no item on the list focus back to the search button
i have tried listening to make it work but i was not able to do it in 4 axis it was only working horizontally
here is the code:
document.addEventListener('click', function(event) {
const ul = document.getElementById("tagList");
const input = document.getElementById('search');
if (!ul.contains(event.target) && event.target !== input) {
ul.style.visibility = "hidden"
input.style = "border-radius: 1px"
input.style = "padding: 5px";
enterBtn.style.display = "none"
}
});
function searchMenu() {
let input, filter, ul, li, a, i, txtValue;
input = document.getElementById('search');
filter = input.value.toUpperCase();
ul = document.getElementById("tagList");
li = ul.getElementsByTagName('li');
enterBtn = document.getElementById('enterButton');
// Check if there are no matching list items
let noMatches = true;
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("a")[0];
txtValue = a.textContent || a.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "block"
enterBtn.style.display = "none"
noMatches = false; // When no match found, enable button
} else {
enterBtn.style = "background-color: #000000; color: #ffffff";
enterBtn.style.display = "block"
li[i].style.display = "none";
}
}
// Show the itemwindow only if there are matches and disable advanced search to prevent the system from unnecessary datafetch from db
if (!noMatches) {
ul.style.visibility = "visible"
input.style = "border: 3px solid #000000"
input.style.margin = "0",
enterBtn.style = "background-color: #f2f2f2; color: #ffffff; border: none; box-shadow: none;";
enterBtn.disabled = true;
enterBtn.style.display = "block";
enterBtn.style.cursor = "default";
} else {
enterBtn.disabled = false;
ul.style.visibility = "hidden"
input.style = "border: 2pxsolid #000000";
// Hide the taglist if no matches
}
}
// Call searchMenu func initially to hide the taglist if there are no items to list
searchMenu();
// Click event listener to the search input to show the ul when clicked
const searchInput = document.getElementById('search');
searchInput.addEventListener('click', function(event) {;
event.stopPropagation(); // Prevent the click event from reaching the doc click listener
searchMenu(); // Call the searchMenu func again when the input is clicked
});
#smContainer {
display: flex;
}
#searchBox {
display: flex;
justify-content: end;
max-height: 100px;
width: 400px;
}
#search {
padding: 5px;
outline: none;
width: inherit;
max-width: inherit;
}
#enterButton {
position: absolute;
border: 0.2rem solid #000000;
background-color: #f2f2f2;
color: rgb(0, 0, 0);
display: none;
cursor: pointer;
}
#tagList {
margin: 30px auto;
position: absolute;
list-style-type: none;
border: 1px solid #000000;
background-color: white;
visibility: hidden;
display: flex;
flex-wrap: wrap;
width: 400px;
}
#tagList li a {
padding: 2px 2px;
text-decoration: none;
color: #000000;
display: block;
border: 1px solid #000000;
margin: 2px;
}
#tagList li a:hover {
background-color: #000000;
color: #ffffff;
}
<div id="smContainer">
<div id="searchBox">
<input type="text" id="search" autocomplete="off" onclick="searchMenu()" onkeyup="searchMenu()" placeholder="filterbox" />
<button type="submit" id="enterButton">SEARCH</button>
<ul id="tagList">
<li><a href="#">Ipsum</a></li>
<li><a href="#">Dolor</a></li>
<li><a href="#">Sit</a></li>
<li><a href="#">Amet</a></li>
<li><a href="#">Amet</a></li>
<li><a href="#">Dolor</a></li>
<li><a href="#">Sit</a></li>
<li><a href="#">Amet</a></li>
<li><a href="#">Ipsum</a></li>
<li><a href="#">Dolor</a></li>
<li><a href="#">Sit</a></li>
<li><a href="#">Amet</a></li>
<li><a href="#">Amet</a></li>
<li><a href="#">Dolor</a></li>
<li><a href="#">Sit</a></li>
<li><a href="#">Amet</a></li>
<li><a href="#">Ipsum</a></li>
<li><a href="#">Dolor</a></li>
<li><a href="#">Sit</a></li>
<li><a href="#">Amet</a></li>
<li><a href="#">Amet</a></li>
<li><a href="#">Dolor</a></li>
<li><a href="#">Sit</a></li>
<li><a href="#">Amet</a></li>
</ul>
</div>
</div>
Upvotes: 1
Views: 55
Reputation: 3223
Here's a partial solution. It works by updating internal coordinates, x and y, to correspond to positions on screen.
Also, of note, you should use display:grid
rather than display:flex
to set up items in a grid.
In addition to what's here, you'll need to find a way to dynamically set the bounds of the coordinates, xMax and yMax based on your selection.
You should also set up a listener to take action on the currently selected element.
let x;
let y;
let xMax = 2;
let yMax = 2;
document.getElementById('tagList').addEventListener('keyup', function(event) {
if (!x) {
x = 0;
}
if (!y) {
y = 0;
}
const previousTarget = document.querySelector(`[data-coordinates="${y},${x}"]`);
previousTarget.classList.remove('selected');
switch (event.key) {
case "ArrowDown":
y++;
break;
case "ArrowUp":
y--;
break;
case "ArrowRight":
x++;
break;
case "ArrowLeft":
x--;
break;
}
if (x > xMax) {
x = 0;
}
if (y > yMax) {
y = 0;
}
if (x < 0) {
x = xMax;
}
if (y < 0) {
y = yMax;
}
const target = document.querySelector(`[data-coordinates="${y},${x}"]`);
target.classList.add('selected');
});
document.addEventListener('click', function(event) {
const ul = document.getElementById("tagList");
const input = document.getElementById('search');
if (!ul.contains(event.target) && event.target !== input) {
ul.style.visibility = "hidden"
input.style = "border-radius: 1px"
input.style = "padding: 5px";
enterBtn.style.display = "none"
}
});
function searchMenu() {
let input, filter, ul, li, a, i, txtValue;
input = document.getElementById('search');
filter = input.value.toUpperCase();
ul = document.getElementById("tagList");
li = ul.getElementsByTagName('li');
enterBtn = document.getElementById('enterButton');
// Check if there are no matching list items
let noMatches = true;
for (i = 0; i < li.length; i++) {
a = li[i].getElementsByTagName("a")[0];
txtValue = a.textContent || a.innerText;
if (txtValue.toUpperCase().indexOf(filter) > -1) {
li[i].style.display = "block"
enterBtn.style.display = "none"
noMatches = false; // When no match found, enable button
} else {
enterBtn.style = "background-color: #000000; color: #ffffff";
enterBtn.style.display = "block"
li[i].style.display = "none";
}
}
// Show the itemwindow only if there are matches and disable advanced search to prevent the system from unnecessary datafetch from db
if (!noMatches) {
ul.style.visibility = "visible"
input.style = "border: 3px solid #000000"
input.style.margin = "0",
enterBtn.style = "background-color: #f2f2f2; color: #ffffff; border: none; box-shadow: none;";
enterBtn.disabled = true;
enterBtn.style.display = "block";
enterBtn.style.cursor = "default";
} else {
enterBtn.disabled = false;
ul.style.visibility = "hidden"
input.style = "border: 2pxsolid #000000";
// Hide the taglist if no matches
}
}
// Call searchMenu func initially to hide the taglist if there are no items to list
searchMenu();
// Click event listener to the search input to show the ul when clicked
const searchInput = document.getElementById('search');
searchInput.addEventListener('click', function(event) {;
event.stopPropagation(); // Prevent the click event from reaching the doc click listener
searchMenu(); // Call the searchMenu func again when the input is clicked
});
#smContainer {
display: flex;
}
#searchBox {
display: flex;
justify-content: end;
max-height: 100px;
width: 400px;
}
#search {
padding: 5px;
outline: none;
width: inherit;
max-width: inherit;
}
#enterButton {
position: absolute;
border: 0.2rem solid #000000;
background-color: #f2f2f2;
color: rgb(0, 0, 0);
display: none;
cursor: pointer;
}
#tagList {
margin: 30px auto;
position: absolute;
list-style-type: none;
border: 1px solid #000000;
background-color: white;
visibility: hidden;
display: grid;
grid-template-columns: auto auto auto;
flex-wrap: wrap;
width: 400px;
}
#tagList li a {
padding: 2px 2px;
text-decoration: none;
color: #000000;
display: block;
border: 1px solid #000000;
margin: 2px;
}
#tagList li a:is(:hover, .selected) {
background-color: #000000;
color: #ffffff;
}
<div id="smContainer">
<div id="searchBox">
<input type="text" id="search" autocomplete="off" onclick="searchMenu()" onkeyup="searchMenu()" placeholder="filterbox" />
<button type="submit" id="enterButton">SEARCH</button>
<ul id="tagList">
<li><a data-coordinates="0,0" href="#">Ipsum</a></li>
<li><a data-coordinates="0,1" href="#">Dolor</a></li>
<li><a data-coordinates="0,2" href="#">Sit</a></li>
<li><a data-coordinates="1,0" href="#">Amet</a></li>
<li><a data-coordinates="1,1" href="#">Amet</a></li>
<li><a data-coordinates="1,2" href="#">Dolor</a></li>
<li><a data-coordinates="2,0" href="#">Sit</a></li>
<li><a data-coordinates="2,1" href="#">Amet</a></li>
<li><a data-coordinates="2,2" href="#">Ipsum</a></li>
</ul>
</div>
</div>
Upvotes: 0