me / src /runtime /app /search /highlight.js
cheymin's picture
Upload 136 files
e1ae2c6 verified
module.exports = function highlightSearchTerm(card, searchTerm) {
if (!card || !searchTerm) return;
const escapeRegExp = (string) => string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
try {
// 兼容 projects repo 卡片:title/desc 不一定是 h3/p
const titleElement = card.querySelector('h3') || card.querySelector('.repo-title');
const descriptionElement = card.querySelector('p') || card.querySelector('.repo-desc');
const hasPinyinMatch =
typeof PinyinMatch !== 'undefined' && PinyinMatch && typeof PinyinMatch.match === 'function';
const applyRangeHighlight = (element, start, end) => {
const text = element.textContent || '';
const safeStart = Math.max(0, Math.min(text.length, start));
const safeEnd = Math.max(safeStart, Math.min(text.length - 1, end));
const fragment = document.createDocumentFragment();
fragment.appendChild(document.createTextNode(text.slice(0, safeStart)));
const span = document.createElement('span');
span.className = 'highlight';
span.textContent = text.slice(safeStart, safeEnd + 1);
fragment.appendChild(span);
fragment.appendChild(document.createTextNode(text.slice(safeEnd + 1)));
while (element.firstChild) {
element.removeChild(element.firstChild);
}
element.appendChild(fragment);
};
const highlightInElement = (element) => {
if (!element) return;
const rawText = element.textContent || '';
const lowerText = rawText.toLowerCase();
if (!rawText) return;
if (lowerText.includes(searchTerm)) {
const regex = new RegExp(`(${escapeRegExp(searchTerm)})`, 'gi');
const fragment = document.createDocumentFragment();
let lastIndex = 0;
let match;
while ((match = regex.exec(rawText)) !== null) {
if (match.index > lastIndex) {
fragment.appendChild(
document.createTextNode(rawText.substring(lastIndex, match.index))
);
}
const span = document.createElement('span');
span.className = 'highlight';
span.textContent = match[0];
fragment.appendChild(span);
lastIndex = match.index + match[0].length;
// 防止无限循环
if (regex.lastIndex === 0) break;
}
if (lastIndex < rawText.length) {
fragment.appendChild(document.createTextNode(rawText.substring(lastIndex)));
}
while (element.firstChild) {
element.removeChild(element.firstChild);
}
element.appendChild(fragment);
return;
}
if (hasPinyinMatch) {
const arr = PinyinMatch.match(rawText, searchTerm);
if (Array.isArray(arr) && arr.length >= 2) {
const [start, end] = arr;
applyRangeHighlight(element, start, end);
}
}
};
highlightInElement(titleElement);
highlightInElement(descriptionElement);
} catch (error) {
console.error('Error highlighting search term');
}
};