CSS Custom Highlight API به شما امکان میدهد بدون تغییر ساختار DOM، به محدودههای نوشتاری دلخواه در صفحه سبک بدهید. این کار باعث میشود برجستهسازی نتایج جستجو، رنگآمیزی نحو، مکاننماهای ویرایش مشارکتی، یا نشانگرهای خطای املایی و دستوری بدون پیچیدن نوشتار در عناصر اضافی یا تکیه بر دستکاری innerHTML فعال شود.
پیادهسازی اصلی
برای برجسته کردن محدودههای نوشتار، باید گرههای نوشتار هدف را جمعآوری کنید، اشیاء Range و Highlight را ایجاد کنید، آنها را در HighlightRegistry ثبت کنید، و سپس با شبهعنصر ::highlight() به آنها سبک دهید.
۱. جمعآوری گرههای نوشتاری و ایجاد محدودهها
از TreeWalker برای جمعآوری همه گرههای نوشتاری در عنصر هدف استفاده کنید، سپس اشیاء Range را که به انحرافهای نویسهای که میخواهید برجسته شوند اشاره میکنند ایجاد کنید.
const article = document.querySelector("article");
// MANDATORY: Use TreeWalker to collect text nodes — do not manipulate innerHTML.
const treeWalker = document.createTreeWalker(article, NodeFilter.SHOW_TEXT);
const allTextNodes = [];
let currentNode = treeWalker.nextNode();
while (currentNode) {
allTextNodes.push(currentNode);
currentNode = treeWalker.nextNode();
}
// MANDATORY: Set range start/end on text nodes, not element nodes.
const range = new Range();
range.setStart(textNode, matchStartIndex);
range.setEnd(textNode, matchEndIndex);
فهرست گره نوشتار را ذخیره کنید و فقط زمانی آن را بازسازی کنید که محتوای DOM واقعاً تغییر کند، زیرا پیمایش درخت هزینه زیادی دارد.
۲. ایجاد کردن بخش جالب از محدودهها
یک یا چند شیء Range را در یک Highlight گروهبندی کنید. چندین گستره که سبک یکسانی دارند به یک برجستهسازی تعلق دارند.
const searchHighlight = new Highlight(...matchingRanges);
۳. ثبت کردن بخش جالب در دفتر ثبت
هر Highlight را بااستفاده از CSS.highlights که HighlightRegistry شبیه Map است تحت نامی سفارشی ثبت کنید.
// MANDATORY: Clear previous highlights before registering new ones
// to avoid stale ranges persisting on the page.
CSS.highlights.clear();
CSS.highlights.set("search-results", searchHighlight);
وقتی چند برجستهسازی همپوشانی دارند، از ویژگی priority برای کنترل ترتیب پشتهسازی استفاده کنید. نکات برجسته با اولویت بالاتر در بالا قرار میگیرند.
const primary = new Highlight(...primaryRanges);
primary.priority = 1;
const secondary = new Highlight(...secondaryRanges);
secondary.priority = 0; // painted first (behind primary)
CSS.highlights.set("primary", primary);
CSS.highlights.set("secondary", secondary);
۴. سبک با ::highlight()
از شبهعنصر ::highlight() در CSS برای سبکدهی به هر برجستهسازی ثبتشده براساس نام استفاده کنید.
::highlight(search-results) {
background-color: #ffdd00;
color: black;
}
فقط مجموعه محدودی از ویژگیهای CSS در ::highlight() کار میکنند: color، background-color، text-decoration و شکلهای طولانی آن، text-shadow، -webkit-text-stroke-color، -webkit-text-fill-color، و -webkit-text-stroke-width. ویژگیهایی مثل background-image، font-size، یا padding نادیده گرفته میشوند.
دسترسپذیری
AVOID: استفاده از برجستهسازیهای سفارشی بهعنوان جایگزینی برای HTML معنایی.
برجستهسازیهای سفارشی صرفاً نمایشی هستند و در درخت دسترسپذیری نشان داده نمیشوند. اگر نوشتار برجستهشده ازنظر معنایی با سند مرتبط است (مثلاً بخشی که کاربر انتخاب کرده است)، بهجای آن از <mark> استفاده کنید. برجستهسازیهای سفارشی را برای جلوههای گذرا و فقط دیداری مثل نتایج جستجو یا رنگآمیزی نحو رزرو کنید.
بخشهای برجسته نباید صرفاً برای انتقال معنی به رنگ متکی باشند. اگر برجستهسازی نشاندهنده خطا است، آن را با نشانگر دیداری دیگری مثل text-decoration: wavy underline یا برچسب نوشتاری مجاور جفت کنید. مطمئن شوید که کنتراست کافی بین پسزمینه برجستهسازی و رنگ نوشتار وجود دارد تا الزامات WCAG 2.1 (حداقل ۴٫۵:۱ برای نوشتار عادی) برآورده شود.
راهبردهای جایگزین
برای مرورگرهایی که از CSS Custom Highlight API پشتیبانی نمیکنند، باید یک تجربه پایه کاربردی ارائه دهید که در آن نوشتار همچنان خوانا باشد، حتی بدون برجستهسازی دیداری.
میتوانید قبلاز استفاده از API، پشتیبانی را شناسایی کنید:
if (CSS.highlights) {
// CSS Custom Highlight API is supported.
} else {
// Fallback: wrap matches in <mark> elements.
}
اگر برجستهسازی برای تجربه کاربری ضروری است، به پیچیدن نوشتار منطبق در عناصر <mark> برگردید. این کار DOM را اصلاح میکند، بنابراین مراقب باشید شنوندگان رویداد را حفظ کنید و ساختار سند را خراب نکنید.
if (!CSS.highlights) {
// Walk text nodes and wrap matches in <mark>, preserving structure.
const walker = document.createTreeWalker(article, NodeFilter.SHOW_TEXT);
const nodes = [];
for (let n = walker.nextNode(); n; n = walker.nextNode()) nodes.push(n);
const term = searchTerm.toLowerCase();
for (const textNode of nodes) {
const text = textNode.textContent;
let pos = text.toLowerCase().indexOf(term);
if (pos === -1) continue;
const frag = document.createDocumentFragment();
let last = 0;
while (pos !== -1) {
frag.append(text.slice(last, pos));
const mark = document.createElement("mark");
// textContent assignment avoids HTML injection.
mark.textContent = text.slice(pos, pos + term.length);
frag.append(mark);
last = pos + term.length;
pos = text.toLowerCase().indexOf(term, last);
}
frag.append(text.slice(last));
textNode.replaceWith(frag);
}
}