Skip to content

Instantly share code, notes, and snippets.

@dinhanhthi
Last active December 15, 2020 22:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dinhanhthi/908585062181e6f34bf8fb098ba3b001 to your computer and use it in GitHub Desktop.
Save dinhanhthi/908585062181e6f34bf8fb098ba3b001 to your computer and use it in GitHub Desktop.
Search result with navigation
// Maybe on separated file (where we create DOM of result)
// mouse hover trigger for li
ulRes.querySelectorAll("li").forEach((item) => {
item.addEventListener("mousemove", () => {
addSelected2(ulRes, item);
}, false);
// if <a> focused by a Tab key
item
.getElementsByClassName("item__content")[0]
.firstChild.firstChild.addEventListener("focus", () => {
addSelected2(ulRes, item);
}, false);
});
<div id="nav-search" class="nav-search">
<form>
<input id="nav-search__input"
class="nav-search__input"
type="search"
onfocusin="inFocus(this)"
placeholder='search notes (type "/" to focus)...'
aria-label='search notes (type "/" to focus)...'>
</form>
<div id="nav-search__result-container" style="display: none;">
<ul id="nav-search__ul"></ul>
<div id="nav-search__no-result" style="display: none;">
<p>No results found.</p>
</div>
</div>
</div>
var divNavSearch = document.getElementById("nav-search");
var divRes = document.getElementById("nav-search__result-container");
var ulRes = document.getElementById('nav-search__ul'); // targets the <ul>
// show again if on focus
function inFocus(x) {
if (x.value != "") {
divRes.style.display = "block";
}
}
// lose focus from input but don't close divRes when mouse hover on it
var isOnDiv = false;
divRes.addEventListener("mouseover", function () {
isOnDiv = true;
});
divRes.addEventListener("mouseout", function () {
isOnDiv = false;
});
var inputSearch = document.getElementById("nav-search__input");
window.addEventListener("click", function () {
// check if there is any result?
var hasResult = ulRes.getElementsByTagName('li').length >= 1;
if (!hasResult || !isOnDiv) {
divRes.style.display = 'none';
}
});
inputSearch.addEventListener("click", e => {
e.stopPropagation();
});
// add .selected to current li
const addSelected = (li) => {
// remove class "selected" from all li
ulRes.querySelectorAll("li").forEach((item) => {
item.classList.remove("selected");
});
// add class "selected" to the current li
li.classList.add("selected");
}
// Check the selected li is in the view
var isInView = (selectedLi, divRes) => {
var isOutBelow = selectedLi.offsetTop + selectedLi.offsetHeight - divRes.scrollTop > divRes.offsetHeight;
var isOutAbove = selectedLi.offsetTop < divRes.scrollTop;
if (isOutAbove) {
return "above";
} else if (isOutBelow) {
return "below";
} else {
return "in";
}
}
const updateScroll = (selectedLi, divRes) => {
// below
if (selectedLi.offsetTop + selectedLi.offsetHeight - divRes.scrollTop > divRes.offsetHeight) {
divRes.scrollTop = selectedLi.offsetTop + selectedLi.offsetHeight - divRes.offsetHeight;
}
// above
if (selectedLi.offsetTop < divRes.scrollTop) {
divRes.scrollTop = selectedLi.offsetTop;
}
return;
}
// ARROW TRIGGER
// -----------------------------------------------------------------------------
document.onkeydown = (e) => {
// press "/" to focus to input
checkInInput = document.activeElement == inputSearch;
if (e.key === "/" && !checkInInput) {
e.stopPropagation();
e.preventDefault();
inputSearch.focus();
}
};
document.addEventListener("focusin", e => {
if (!divNavSearch.contains(e.target)) {
divRes.style.display = 'none';
}
})
// prevent default Enter + go to selected li's a
inputSearch.onkeydown = (e) => {
if (e.key === "Enter") {
e.stopPropagation();
e.preventDefault();
var selectedLi = ulRes.querySelector('li[class*="selected"]');
window.location.href = selectedLi.getElementsByClassName("item__content")[0].firstChild.firstChild.href;
}
// release
if (e.key === "Escape") {
divRes.style.display = 'none';
inputSearch.blur();
}
}
divNavSearch.onkeydown = (e) => {
// check if there is any result?
hasResult = ulRes.getElementsByTagName('li').length >= 1;
if (hasResult) {
if (["ArrowUp", "ArrowDown"].indexOf(e.key) > -1) {
// prevent normal action of up/down
e.preventDefault();
}
var firstLi = ulRes.firstChild; // targets the first <li>
var lastLi = ulRes.lastChild; // last li
// selected li before keydown
var selectedLi = ulRes.querySelector('li[class*="selected"]');
switch (e.key) {
case "ArrowUp":
if ( !selectedLi || selectedLi == firstLi ) {
nextLi = lastLi;
} else {
nextLi = selectedLi.previousSibling;
}
addSelected(nextLi);
selectedLi = nextLi;
updateScroll(selectedLi, divRes);
break;
case "ArrowDown":
// ae = document.activeElement;
if ( !selectedLi || selectedLi == lastLi) {
nextLi = firstLi;
} else {
nextLi = selectedLi.nextSibling;
}
addSelected(nextLi);
selectedLi = nextLi;
updateScroll(selectedLi, divRes);
break;
}
} // end if hasResult
}
#nav-search__result-container {
position: absolute;
max-height: 80vh;
overflow: auto;
width: 100%;
background: $c-bg-nav;
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
padding: 0;
filter: drop-shadow(2px 4px 6px black);
@media (max-width: $grid-md - 1px) {
position: fixed;
left: 0;
right: 0;
border-radius: 0;
}
ul#nav-search__ul {
padding: 0;
margin: 0;
list-style: none;
li {
padding: 10px 10px 10px 25px;
h3 {
margin: 0;
a {
color: #efb232;
white-space: inherit;
padding: 0;
text-align: left;
font-weight: normal;
font-size: $font-size;
font-family: $font-main;
line-height: 1.4;
&:hover {
border-bottom: none;
color: $c-blue;
}
}
}
p {
text-align: left;
margin: 0;
line-height: 1.4;
}
&.selected {
background: $c-background;
&::before {
content: "";
float: left;
color: $c-blue;
margin-left: -20px;
}
}
}
mark{
background: none;
color: #cd8aff;
}
}
#nav-search__no-result {
padding: 0 10px;
p {
text-align: left;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment