Поиск на сайте по меню
Это пример реализации поиска по пунктам бокового меню. Алгоритм поиска может быть и не идеальный, местами сложный, с избыточными проверками, но кода немного и вот если нужно быстренько состряпать, то можно использовать.
Это скриншот, чтобы было понятно, что меню у нас многоуровневое и что нам нужно скрывать все пункты не подходящие под поиск, но оставить видимыми родительские, чтобы было понятно куда мы перейдем.

Кроме того, чтобы была понятна структура меню, чтобы понимать нижеприведенный js-код поиска, в скриншот включена и она.
Это собственно код поиска, который пользуется дополнительной функцией hide_li()
, которая возвращает true если пункт был скрыт, или false в противном случае. Когда мы пробегаемся по всем элементам, то накапливаем в переменной hide_triggered информацию о том, бы ли скрыт хотя бы один пункт за пробег по списку. Это нужно для того чтобы учесть особенность работы функции hide_li() - она не будет скрывать пункты у которых есть видимые дочерние элементы поскольку мы решили оставить родителей видимыми. Но при таком подходе нам нужно сделать множество итераций по всему списку. На первой итерации исчезнут пункты у которых нет дочерних подпунктов и дочерние пункты у которых нет дочерних подпунктов. На второй итерации исчезнуть пункты у которых есть дочерние, но эти дочерние были скрыты на прошлой итерации. И так нам нужно повторять до тех пор, пока за целый пробег не обнаружится пунктов которые были скрыты за эту итерацию - значит мы попрятали все что лишнее.
// Поиск
document.querySelectorAll( "aside input[name=search]" ).forEach( function( search_el ) {
search_el.value = "";
search_el.addEventListener( "input", function( e ) {
let find_str = search_el.value;
let re = new RegExp( find_str, "i" );
let hide_triggered;
do {
hide_triggered = false;
// Пробегаемся по всем элементам
document.querySelectorAll( "aside nav a" ).forEach( function( a_el ) {
hide_triggered = hide_triggered || hide_li( a_el, re );
} );
// и если хоть раз сработало - повторяем
} while ( hide_triggered );
} );
} );
Нижеприведенная функция принимает элемент меню и поисковое выражение, берет текст элемента, сверяет и начинается второй интересный этап - нам нужно убедиться что либо нет дочерних подпунктов, либо они уже скрыты и определяется это довольно кривым способом - по высоте блока, содержащего дочерние элементы. Если он схлопнулся, высота его стала меньше 10px, поскольку там есть еще паддинг в 7px, то считаем что все пункты скрыты. Кроме того учитываем, а не скрыт ли данный проверяемый пункт на прошлой итерации, чтобы не получить бесконечный цикл скрытия уже скрытых и зависание браузера.
function hide_li( a_el, re ) {
let hide_triggered = false;
// Проверяем текст
let a_text = a_el.innerText;
let not_found = a_text.search( re ) < 0;
let parent_classes = a_el.parentElement.classList;
if ( not_found ) {
// И что дочерних нет или уже скрыты
let childs = a_el.nextElementSibling;
let childs_height = 0;
if ( childs !== null ) {
childs_height = childs.offsetHeight;
}
if ( childs_height < 10 && ! parent_classes.contains( "not_match" ) ) {
parent_classes.add( "not_match" );
hide_triggered = true;
}
} else {
parent_classes.remove( "not_match" );
}
return hide_triggered;
}
Вот такой вот вам шляпо-код для размышлений. Надо бы все это переписать в красивую рекурсивную функцию, чтобы не было многократных проверок, да и код при этом может сократиться, но это в следующий раз.
Это просто реализация первого пришедшего в голову алгоритма. Код функции можно сделать еще более последовательным, но статейка эта ради некой пробы - подумать, нужно ли обмениваться маленькими кусочками кода которые решают ту или иную задачу и как бы это красиво сделать. Где все это аккумулировать и какой должен быть вид.