Search the page feature
See it here. Add this to an html block on the page.

<div class="poc-search-wrapper" role="search">
<label for="poc-search" style="display:block; font-weight:600;">Find person/content</label>
<div class="poc-search-controls">
<input id="poc-search" type="text" placeholder="Type a name or keyword" aria-label="Search within items" />
<button id="poc-search-prev" type="button" aria-label="Find previous match">Prev</button>
<button id="poc-search-next" type="button" aria-label="Find next match">Next</button>
<button id="poc-search-clear" type="button" aria-label="Clear search">Clear</button>
</div>
<div id="poc-search-status" aria-live="polite" style="margin-top:0.5rem;"></div>
</div>
<style>
/* Layout + responsive */
.poc-search-controls { display:flex; gap:0.5rem; align-items:center; flex-wrap:wrap; margin-top:0.25rem; }
#poc-search { padding:0.5rem; min-width:16rem; max-width:100%; }
/* Highlighting */
.poc-hit { outline: 3px solid #1a73e8; outline-offset: 4px; border-radius: 6px; }
.poc-hit:focus { box-shadow: 0 0 0 4px rgba(26,115,232,0.3); }
/* Mark styling with strong contrast */
mark.poc-mark {
background: #ffeb3b; /* bright yellow */
color: #111; /* dark text for contrast */
padding: 0.05em 0.15em;
border-radius: 0.2em;
}
/* Buttons */
#poc-search-prev, #poc-search-next, #poc-search-clear {
padding: 0.5rem 0.75rem;
cursor: pointer;
border: 1px solid #ccc;
border-radius: 6px;
background: #f7f7f7;
}
#poc-search-prev:focus, #poc-search-next:focus, #poc-search-clear:focus { outline: 3px solid #1a73e8; outline-offset: 2px; }
/* Avoid affecting other pages if helpful:
body:not(.events-page) .poc-search-wrapper { } */
</style>
<script>
(function () {
const SELECTOR = '.full-poc.has-no-date.has-no-image';
const input = document.getElementById('poc-search');
const btnPrev = document.getElementById('poc-search-prev');
const btnNext = document.getElementById('poc-search-next');
const btnClear = document.getElementById('poc-search-clear');
const status = document.getElementById('poc-search-status');
let hits = [];
let hitIndex = -1;
let lastQuery = '';
let debounceTimer;
function escapeRegExp(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
function highlightInNode(node, regex) {
if (node.nodeType !== 3) return 0;
const text = node.nodeValue;
const match = regex.exec(text);
if (!match) return 0;
const mark = document.createElement('mark');
mark.className = 'poc-mark';
mark.textContent = match[0];
const after = node.splitText(match.index);
after.nodeValue = after.nodeValue.substring(match[0].length);
node.parentNode.insertBefore(mark, after);
return 1 + highlightInNode(after, regex);
}
function traverseAndHighlight(el, regex) {
let count = 0;
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, {
acceptNode(n) {
const p = n.parentNode;
if (!p) return NodeFilter.FILTER_REJECT;
const tag = p.nodeName.toLowerCase();
if (tag === 'script' || tag === 'style') return NodeFilter.FILTER_REJECT;
return /\S/.test(n.nodeValue) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
}
});
const textNodes = [];
while (walker.nextNode()) textNodes.push(walker.currentNode);
for (const tn of textNodes) {
regex.lastIndex = 0;
count += highlightInNode(tn, regex);
}
return count;
}
function clearHighlights() {
document.querySelectorAll('mark.poc-mark').forEach(mark => {
const parent = mark.parentNode;
if (!parent) return;
parent.replaceChild(document.createTextNode(mark.textContent), mark);
parent.normalize();
});
document.querySelectorAll(SELECTOR + '.poc-hit').forEach(el => el.classList.remove('poc-hit'));
hits = [];
hitIndex = -1;
}
function buildHits() {
hits = [];
document.querySelectorAll(SELECTOR + ' mark.poc-mark').forEach((m, idx) => {
const container = m.closest(SELECTOR) || m.parentElement;
if (container) hits.push({ el: container, mark: m, idx });
});
}
function announce(msg) {
status.textContent = msg;
}
function scrollToHit(index) {
if (index < 0 || index >= hits.length) return;
document.querySelectorAll(SELECTOR + '.poc-hit').forEach(el => el.classList.remove('poc-hit'));
const { el, mark } = hits[index];
el.classList.add('poc-hit');
mark.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
const prevTabIndex = el.getAttribute('tabindex');
el.setAttribute('tabindex', '-1');
el.focus({ preventScroll: true });
if (prevTabIndex === null) setTimeout(() => el.removeAttribute('tabindex'), 1000);
announce(`Result ${index + 1} of ${hits.length}`);
}
function runSearch(startDirection = 'next') {
const q = input.value.trim();
// Auto-clear if empty or if query changed
clearHighlights();
if (!q) {
lastQuery = '';
announce('Search cleared.');
return;
}
const regex = new RegExp(escapeRegExp(q), 'gi');
const containers = document.querySelectorAll(SELECTOR);
let totalMarks = 0;
containers.forEach(el => {
if (regex.test(el.textContent)) {
totalMarks += traverseAndHighlight(el, regex);
}
regex.lastIndex = 0;
});
buildHits();
if (!hits.length) {
announce('No matches found.');
lastQuery = q;
return;
}
hitIndex = startDirection === 'prev' ? hits.length - 1 : 0;
scrollToHit(hitIndex);
announce(`Found ${hits.length} match${hits.length > 1 ? 'es' : ''}. Showing ${hitIndex + 1}.`);
lastQuery = q;
}
function gotoNext() {
const q = input.value.trim();
if (!hits.length || q !== lastQuery) return runSearch('next');
hitIndex = (hitIndex + 1) % hits.length;
scrollToHit(hitIndex);
}
function gotoPrev() {
const q = input.value.trim();
if (!hits.length || q !== lastQuery) return runSearch('prev');
hitIndex = (hitIndex - 1 + hits.length) % hits.length;
scrollToHit(hitIndex);
}
// Buttons
btnNext.addEventListener('click', gotoNext);
btnPrev.addEventListener('click', gotoPrev);
btnClear.addEventListener('click', () => {
input.value = '';
runSearch('next'); // will auto-clear & announce
});
// Enter = Next, Shift+Enter = Prev
input.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
if (e.shiftKey) gotoPrev(); else gotoNext();
}
});
// ★ NEW: Auto-clear & re-search on input (debounced)
input.addEventListener('input', () => {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
const q = input.value.trim();
if (!q) {
runSearch(); // clears + status
} else if (q !== lastQuery) {
runSearch('next'); // clears old marks, applies new, scrolls
}
}, 150); // small delay for performance & mobile typing
});
announce('Type a term; matches update automatically. Enter = Next, Shift+Enter = Prev.');
})();
</script>