CSS Custom Highlight API, DOM yapısını değiştirmeden sayfadaki rastgele metin aralıklarını biçimlendirmenize olanak tanır. Bu sayede, metni ek öğelere sarmalamadan veya innerHTML manipülasyonuna güvenmeden arama sonucu vurgulama, söz dizimi renklendirme, ortak düzenleme imleçleri veya yazım ve dil bilgisi hatası işaretçileri etkinleştirilir.
Temel uygulama
Metin aralıklarını vurgulamak için hedef metin düğümlerini toplamanız, Range ve Highlight nesneleri oluşturmanız, bunları HighlightRegistry'ye kaydetmeniz ve ardından ::highlight() sözde öğesiyle stillendirmeniz gerekir.
1. Metin düğümlerini toplama ve aralık oluşturma
Hedef öğedeki tüm metin düğümlerini toplamak için TreeWalker kullanın, ardından vurgulamak istediğiniz karakter ofsetlerini işaret eden Range nesneleri oluşturun.
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);
Ağaçta gezinmek maliyetli olduğundan metin düğümü listesini önbelleğe alın ve yalnızca DOM içeriği gerçekten değiştiğinde yeniden oluşturun.
2. Aralıklardan öne çıkan an oluşturma
Bir veya daha fazla Range nesnesini bir Highlight içinde gruplandırın. Aynı stili paylaşan birden fazla aralık tek bir vurguya aittir.
const searchHighlight = new Highlight(...matchingRanges);
3. Öne çıkanı kayıt defterine kaydetme
Her Highlight öğesini, Map benzeri bir HighlightRegistry olan CSS.highlights kullanarak özel bir adla kaydedin.
// 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);
Birden fazla vurgu çakıştığında, yığınlama sırasını kontrol etmek için priority özelliğini kullanın. Daha yüksek öncelikli vurgular üstte gösterilir.
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);
4. ::highlight() ile stil oluşturun
Kayıtlı her vurguyu ada göre stillendirmek için CSS'de ::highlight() sözde öğesini kullanın.
::highlight(search-results) {
background-color: #ffdd00;
color: black;
}
::highlight() içinde yalnızca sınırlı sayıda CSS özelliği çalışır: color, background-color, text-decoration ve uzun biçimleri, text-shadow, -webkit-text-stroke-color, -webkit-text-fill-color ve -webkit-text-stroke-width. background-image, font-size veya padding gibi özellikler yoksayılır.
Erişilebilirlik
AVOID: Semantik HTML yerine özel vurgular kullanmak.
Özel vurgulamalar yalnızca sunum amaçlıdır ve erişilebilirlik ağacına gösterilmez. Vurgulanan metin, dokümanla anlamsal olarak alakalıysa (ör. kullanıcı tarafından seçilen bir pasaj) bunun yerine <mark> öğesini kullanın. Özel vurgulamaları, arama sonuçları veya söz dizimi renklendirme gibi geçici, yalnızca görsel efektler için kullanın.
Vurgulamalar, anlamı iletmek için yalnızca renge dayalı olmamalıdır. Vurgulama bir hatayı gösteriyorsa bunu text-decoration: wavy underline gibi başka bir görsel göstergeyle veya bitişik bir metin etiketiyle eşleştirin. WCAG 2.1 şartlarını karşılamak için vurgulanan arka plan ile metin rengi arasında yeterli kontrast olduğundan emin olun (normal metin için en az 4,5:1).
Yedek stratejiler
CSS Custom Highlight API'yi desteklemeyen tarayıcılar için görsel vurgulama olmasa bile metnin okunabilir olduğu işlevsel bir temel deneyim sunmanız gerekir.
API'yi kullanmadan önce desteği algılayabilirsiniz:
if (CSS.highlights) {
// CSS Custom Highlight API is supported.
} else {
// Fallback: wrap matches in <mark> elements.
}
Vurgulama, kullanıcı deneyimi açısından kritikse eşleşen metni <mark> öğeleriyle sarmalamaya geri dönün. Bu işlem DOM'u değiştirdiğinden etkinlik işleyicileri korumaya ve belge yapısını bozmamaya dikkat edin.
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);
}
}