注意!!
全てのシーンでのインクリメンタルサーチに対応できません。
数が少なく、更新頻度がかなり少ないorないレコードをインクリメンタルサーチする場合のみ適用できます。
何を言ってるの?
フロント検索UIで文字をタイピングした段階で検索した文字列にヒットしたDBにあるレコードを探す機能を作成した際のTipsです。
たとえば、100レコードほどしかなく、本人しか追加、削除しないなどの性質をもったテーブルです。
最初のレンダリング時に、DOMに持っておいて、DOM上で検索できるようにすれば、サーバー側に通信なく、データを表示非表示できると思ったので、実践してみました。
普段使わないあまり使わないであろうXpathでの実装です。
コードはこんな感じ
CSS
display-none {
display: none;
}
TypeScript
// 検索フォーム
this.search = <HTMLInputElement>>document.querySelector('#search');
// 検索対象のDOM
this.searchTargets = <NodeListOf<HTMLElement>>document.querySelectorAll('.target');
// イベントリスナー
this.search.addEventListener("keyup", this.searchKeyUpHandler());
/**
* 検索窓に入力された文字列で検索
*
* @param e HTMLElementEvent<HTMLInputElement>
*/
private searchCallTrackingWordKeyUpHandler = (e: HTMLElementEvent<HTMLInputElement>) => {
e.preventDefault();
// 文字が入力されてなければ、全ての行を表示し処理終了
if (e.currentTarget.value.length == 0) {
for (let i: number = 0, len: number = this.searchTargets.length; i < len; i++) {
this.searchTargets[i].classList.remove("display-none");
}
return;
}
// 初期化(全て非表示)
for (let i: number = 0, len: number = this.searchTargets.length; i < len; i++) {
this.searchTargets[i].classList.add("display-none");
}
// xPathに変換し、検索窓に入力された文字列でテキストノードを取得
const xPathTextNodeResult: XPathResult = document.evaluate(
`//div[contains(@class, 'target')]/div/text()[contains(., "${e.currentTarget.value}")]`,
document,
null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
null
);
// テキストノードの数
let snapshotLength: number = xPathTextNodeResult.snapshotLength;
// テキストノードの親要素を取得し、表示する
while (snapshotLength--) {
const searchTarget = xPathTextNodeResult.snapshotItem(snapshotLength)!.parentElement!.closest('.
target')!;
searchTarget.classList.remove('display-none');
}
}
メリット・デメリット
-
メリット
フロント・サーバー側に記述が少なくなるはず。
DOMそのものを書き換えないので、検索した結果にイベントリスナーなどする場合は一度で済む(API通信だと、今描画してる検索結果のDOMを削除して、再度、createElementし、イベントリスナーする流れだと思われるが、この手法だと表示非表示なので、SSR時などに一度だけイベントリスナーすれば良い) -
デメリット
裏で、更新された場合、リアルタイムで最新の情報が得られない。あくまでSSR時の情報だけ。