Search & Filters

✎ Improve this page

JekyllLMS ships two complementary search tools: a site-wide search in the nav bar (courses + blog + docs), and per-page filtering on /courses/ and /blog/ (category chips, in-page title search). Both are client-side, no external search service required.

The magnifying-glass button in the top-right of the nav (next to the GitHub button) opens a search panel backed by search.json — a Liquid-generated index of every course, post, and doc’s title, url, and a short excerpt, built at bundle exec jekyll build time.

assets/js/search.js fetches search.json once (lazily, on first open), then filters it client-side as you type, matching against title + excerpt and showing up to 8 results grouped by type (📚 Course / 📝 Blog / 📖 Docs). No Algolia, Lunr, or other search service — the whole index is typically a few KB of JSON.

To add another collection to the index, extend the loop in search.json following the existing site.courses / site.posts / site.docs pattern.

Per-Page Filtering

/courses/ has a working search box and category chips; /blog/ has a search box — both entirely client-side, filtering DOM elements already rendered by Jekyll (no JSON fetch, no server round-trip).

assets/js/filter.js exposes one helper, initCardFilter(opts), that wires a search input and an optional row of category chips to a list of DOM elements:

initCardFilter({
  searchInput: document.getElementById('course-search'),
  chipsContainer: document.getElementById('course-chips'),
  cards: document.querySelectorAll('.catalog-grid .course-card'),
  emptyState: document.getElementById('course-empty')
});

It reads data-title and data-category attributes straight off each card/item — _includes/course-card.html sets these from the course’s front matter, and blog/index.html sets data-title on each .post-item. Filtering and searching combine (a chip narrows by category, the search box narrows by title), and an empty-state message appears when nothing matches. Note that on /blog/ this only searches the posts on the current page of pagination — the nav’s site-wide search covers every post regardless of page.

Extending to a New Collection

To add per-page filtering to another listing page:

  1. Add a data-title (and any other data-* attribute you want to filter on) to each item’s wrapper element.
  2. Add a search <input> and, optionally, a chips container with data-cat="..." buttons.
  3. Call initCardFilter({...}) for those elements inside the DOMContentLoaded listener in assets/js/filter.js.

initCardFilter no-ops if its cards selector matches nothing, so it’s safe to call unconditionally from the shared script.