やること

対象のHTMLファイルに以下の2行を実行するだけで完了です。 別ファイルでやらない場合はもうちょっと複雑になります。 bootstrap@5は見た目を綺麗にするために入っている。

echo '<script type="text/javascript" src="main.js"></script>' >> index.html
echo '<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">' >> index.html

main.js

今回の本体。

  • 検索:よくある検索機能。
  • 登録:表示したくない要素をキーワードでミュートする機能。
  • 反転:表示・非表示を切り替え。

できることはこれだけ。 中途半端に便利でしょ?

function search() {
    let keyword = searchInput.value;
    let is_reset = false;
    if (keyword.length === 0) {
        is_reset = true;
    }
    [...document.querySelectorAll('li')].forEach((li) => {
        if (li.firstChild.textContent.includes(keyword)) {
            li.style.display = "";
        } else {
            li.style.display = "none";
        }
    });
}

function register() {
    let keyword = searchInput.value;
    if (keyword.length === 0) {
        return;
    }
    localStorage.setItem(keyword, [...document.querySelectorAll('li')]
            .filter((li) => li.firstChild.textContent.includes(keyword))
            .map((e) => e.firstChild.textContent).join(', '));
}

function reverse() {
  [...document.querySelectorAll('li')].forEach((li) => {
      if (li.style.display === 'none') {
          li.style.display = '';
      } else {
          li.style.display = 'none';
      }
  })
}

function hide() {
    let titles = Object.values(localStorage);

    [...document.querySelectorAll('li')].forEach((li) => {
        if (titles.find((value) => value.match(li.firstChild.textContent.replace(/\(/, "\\(").replace(/\)/, "\\)")))) {
            li.style.display = "none";
        } else {
            li.style.display = "";
        }
    });
}

let searchInput = document.createElement('input');
searchInput.setAttribute('type', 'search');
searchInput.classList.add('form-control');
searchInput.placeholder = "";
searchInput.setAttribute('aria-label', "検索");
searchInput.setAttribute('aria-describedby', "search-button");
searchInput.onkeydown = (event) => {
    if (event.key === 'Enter') {
        search();
    }
};

let searchButton = document.createElement('button');
searchButton.textContent = '検索';
searchButton.classList.add('btn');
searchButton.classList.add('btn-outline-secondary');
searchButton.setAttribute('id', "search-button");
searchButton.setAttribute('type', 'button');
searchButton.addEventListener('click', search);

let registerButton = document.createElement('button');
registerButton.textContent = '登録';
registerButton.classList.add('btn');
registerButton.classList.add('btn-outline-secondary');
registerButton.setAttribute('id', "register-button");
registerButton.setAttribute('type', 'button');
registerButton.addEventListener('click', register);

let reverseButton = document.createElement('button');
reverseButton.textContent = '反転';
reverseButton.classList.add('btn');
reverseButton.classList.add('btn-outline-secondary');
reverseButton.setAttribute('id', "reverse-button");
reverseButton.setAttribute('type', 'button');
reverseButton.addEventListener('click', reverse);

let searchInputGroupPrepend = document.createElement('div');
searchInputGroupPrepend.classList.add('input-group-prepend');

let searchInputGroup = document.createElement('div');
searchInputGroup.classList.add('input-group');
searchInputGroup.classList.add('mb-3');

searchInputGroupPrepend.appendChild(searchButton);
searchInputGroupPrepend.appendChild(registerButton);
searchInputGroup.appendChild(searchInputGroupPrepend);
searchInputGroup.appendChild(searchInput);

document.body.insertBefore(searchInputGroup, document.querySelector('ul'));

let searchInputGroupAppend = document.createElement('div');
searchInputGroupAppend.classList.add('input-group-append');
searchInputGroupAppend.appendChild(reverseButton);
searchInputGroup.appendChild(searchInputGroupAppend);


window.onload = () => {
    hide();
}