Açıklamayı bir DOM Range'ine bağlayın, ardından etiketini range'in işlenmiş parça dikdörtgenlerini kullanarak konumlandırın. Bu kılavuz, bir ifade satır içi öğeleri geçtiğinde ve birkaç satıra sarıldığında bir notun bağlı kalmasını sağlayan küçük bir Mac masaüstü tarayıcı demosunu gösterir.
Görev spesifiktir: temel bir web sayfası düzenindeki bir ifadeye, metnini yeniden oluşturmadan bir görünür açıklama ekleyin. İfadeyi belirleyeceksiniz, etiket için bir parça seçeceksiniz ve düzen değişikliklerinden sonra yeniden ölçüleceksiniz. MDN, bir range'in işgali altındaki dikdörtgenleri almak için tarayıcı API'si olarak Range.getClientRects()'yi belgelemektedir. Range: getClientRects() yöntemi.
Neden etiket sarılmış metinden uzaklaşıyor?
Bir ifadenin iki ilgili gösterimi vardır: belgedeki konumu ve ekrandaki mevcut geometrisi. WHATWG, bir range'i başlangıç ve bitiş sınır noktaları üzerinden tanımlar; her biri bir düğüm ve bir ofsetten oluşur. Bu uç noktalar farklı metin düğümlerinde olabilir, bu da tek bir range'in mevcut bir vurgulama öğesini geçmesine olanak tanır. DOM Standardı.
Tek bir sınırlayıcı dikdörtgen detayı kaybeder. Bir ifadenin bir satırın ortasından başlayıp bir sonraki satırın başlangıcına yakın yerde bittiğini hayal edin. Kapsayan dikdörtgen her iki satırın yanındaki boşluğu da içerir. Bu dikdörtgenin ortasında merkezlenen bir etiket bu nedenle kelimelerden ayrık görünebilir. Floating UI bu sorunu çok satırlı bağlantılar ve range seçimleri için açıkça belirler. inline.
Tek tek dikdörtgenler de yorum gerektirir. W3C, bir range'in hem seçili öğe kenar dikdörtgenlerini hem de metin dikdörtgenlerini içerik sırasına göre döndürebileceğini belirtir. İç içe öğeler bu nedenle çakışan dikdörtgenler üretebilir; dikdörtgen sayısı güvenilir bir satır sayısı değildir. Metin sınırları tam boyanan glif ana hatları yerine yazı tipi metriklerini kullanır. CSSOM View Module.
Hangi parça açıklamaya sahip olmalı?
Konumlandırma kodunu yazmadan önce bir yerleştirme kuralı seçin. Aşağıdaki karar yardımı, açıklanan metni etiketin göründüğü noktadan ayırır.
| Kasten davranış | Referans geometrisi | Yerleştirme kuralı |
|---|---|---|
| Bir ifadeyi takip eden bir not | Tam range | Basit yatay, soldan sağa düzende son kullanılabilir parçasının altına ekleyin |
| Bir işaretçi etkileşimi yakınındaki bir etiket | Range'in parça dikdörtgenleri | Etkileşimin istemci koordinatındaki parçayı seçin |
| Merkezli bir genel bakış etiketi | Kapsayan dikdörtgen | Etiketin boş alan üzerinde oturabileceğini kabul edin |
| Tüm ifadeyi işaretleyen bir arka plan | Tam range | Etiket yerleşiminden bağımsız olarak bir metin vurgulaması kullanın |
Demo ilk kuralı kullanır. Son dikdörtgenin faydalı bir uç nokta referansı olmasını sağlamak için yazım modunu ve yönünü kasten sabitler. Bunu örneğin bir koşulu olarak değerlendirin. W3C bu dikdörtgenler için içerik sırasını belirtir; sol üst köşeden sağ alt köşeye evrensel bir sırayı vaat etmez. CSSOM View Module.
Floating UI ile işaretçi tabanlı yerleştirme için, onun inline() middleware'i görüntü alanına göreli x ve y koordinatlarını kabul eder. Belgeler bu koordinat tabanlı seçimi ayrık dikdörtgenlerle sınırlandırır, bu nedenle bunu kısıtlamsız bir karakter seçici olarak tanımlamamalıdır. inline.
Mac'te Tam Bir Sarılmış Metin Demosu Oluşturun
Güncel bir masaüstü Firefox sürümünü kullanın ve about:blank adresinde boş bir sekme açın. Sayfanın Web Konsolunu açmak için Command + Option + K tuşlarına basın. Mozilla bu kısayolu belgelemekte ve konsolun sayfa bağlamında JavaScript çalıştırdığını onaylamaktadır. Web Konsolu.
Aşağıdaki tam programı o konsolda çalıştırın. Örnek içeriğini, stillerini ve kontrollerini kendisi oluşturur, bu nedenle proje, import veya harici kütüphane gerekmez. Sentetik ifade mevcut bir vurgulama öğesini geçer. Açıklama sadece görüntüleme amaçlı bir etikettir; düzenleme kontrolü yoktur.
(() => {
window.fragmentDemo?.destroy();
const host = document.createElement('section');
host.style.cssText = 'margin:48px 24px;padding-bottom:900px';
const p = document.createElement('p');
p.dir = 'ltr';
p.style.cssText = 'font:20px/1.6 Georgia,serif;width:24rem;max-width:80vw;writing-mode:horizontal-tb';
const a = document.createTextNode('Sample A: amber tiles follow the curved path ');
const em = document.createElement('em');
em.textContent = 'past the small blue circle and ';
const c = document.createTextNode('toward the final silver square. End.');
p.append(a, em, c);
const button = document.createElement('button');
button.textContent = 'Change text width';
const status = document.createElement('p');
host.append(button, p, status);
document.body.append(host);
const range = document.createRange();
range.setStart(a, 'Sample A: '.length);
range.setEnd(c, c.data.indexOf(' End.'));
const note = document.createElement('div');
note.textContent = 'Note A: review this phrase';
note.style.cssText = 'position:fixed;z-index:1000;background:#fff2ad;color:#222;padding:6px 10px;font:14px/1.4 system-ui;pointer-events:none';
document.body.append(note);
const style = document.createElement('style');
style.textContent = '::highlight(fragment-demo) {background-color:#dbeafe;}';
document.head.append(style);
const canHighlight = typeof Highlight !== 'undefined' && !!window.CSS?.highlights;
if (canHighlight) CSS.highlights.set('fragment-demo', new Highlight(range));
status.textContent = canHighlight ? 'The blue phrase owns Note A.' : 'Text highlighting unavailable; Note A still uses the range.';
function update() {
if (!host.isConnected) return;
const rects = Array.from(range.getClientRects()).filter(r => r.width > 0 && r.height > 0);
const r = rects.at(-1);
note.hidden = !r || range.collapsed;
if (note.hidden) return;
const maxLeft = Math.max(8, innerWidth - note.offsetWidth - 8);
note.style.left = Math.max(8, Math.min(r.left, maxLeft)) + 'px';
note.style.top = (r.bottom + 8) + 'px';
}
let narrow = false;
button.onclick = () => {
narrow = !narrow;
p.style.width = narrow ? '16rem' : '24rem';
update();
};
const observer = new ResizeObserver(update);
observer.observe(p);
window.addEventListener('resize', update);
window.addEventListener('scroll', update, true);
document.fonts.ready.then(update);
update();
window.fragmentDemo = {destroy() {
observer.disconnect();
window.removeEventListener('resize', update);
window.removeEventListener('scroll', update, true);
if (canHighlight) CSS.highlights.delete('fragment-demo');
host.remove(); note.remove(); style.remove();
}};
})();
Uç noktalar Sample A: önekini ve End. son ekini hariç tutar. Paragraf genişliğini iki değer arasında değiştirmek için Change text width (Metin genişliğini değiştir) butonuna tıklayın. Aynı range açıklama hedefi olarak kalırken etiket son kullanılabilir parçasının altına yeniden konumlandırılır. Pencereyi kaydırın ve yeniden boyutlandırarak davranışı inceleyin. Demo'yu window.fragmentDemo.destroy() ile kaldırın.
Program bir JavaScript sözdizimi kontrolünden geçti, örnek ofsetleri ve parça konumu aritmetiği deterministik kontrollerden geçti. Tarayıcı renderlaması burada çalıştırılmadı; yukarıdaki adımlar görsel doğrulamayı sağlar.
Demo Metin Akışını ve Şekillendirmeyi Nasıl Korur?
Range mevcut metni ölçer. Program ifadenin metin düğümlerini ve vurgulama öğesini örneği oluşturduktan sonra yerinde bırakır. Etiketi ayrı ekler ve isteğe bağlı olarak ifadeyi CSS Custom Highlight API'si üzerinden boyar; MDN bunu altta yatan DOM yapısını değiştirmeden çalıştığı olarak belgelemiştir. CSS custom highlight API.
Bu ayrım yaklaşımı uyarlarken önemlidir. Karakter birimleri arasına satır içi padding, kenarlık veya kenar boşluğu eklemek şekillendirme sınırları oluşturabilir. W3C, başlangıçta olmayan vertical-align ve çift yönlü izolasyon sınırlarını da şekillendirmeyi bozan koşullar olarak belirtir. Koordinat almak için sadece bireysel konumlandırılmış karakter sarmalayıcıları eklemeyin; tarayıcının mevcut sonucunu ölçün. CSS Text Module Level 3, Shaping Across Element Boundaries.
Gerçek uygulama metni için, range uç noktalarını kasten metin sınırlarından türetin. DOM metin ofsetleri kod birimlerini kullanır; bunlar görünür glif sayısı değildir. Uç noktaları, görüntülenen her karakterin bir ofset işgal ettiğini varsaymak yerine, tam kasten karakterlerle hizalı tutun. DOM Standardı.
Vurgulama isteğe bağlıdır. MDN Highlight'ı Baseline 2025 olarak listeler ve eski tarayıcıların buna sahip olmayabileceğini belirtir. Demo'daki özellik kontrolü vurgulama kullanılamadığında açıklama yerleşimini korur ve açıklayıcı bir durum mesajı görüntüler. Highlight.
Sayfa Yeniden Akış Yaptığında Ne Güncellenmeli?
Demo paragraf yeniden boyutlandırmada, pencere yeniden boyutlandırmada ve kaydırmada yeniden ölçer. ResizeObserver, tarayıcı penceresini yeniden boyutlandırmayla ilgili olmayan değişiklikler de dahil olmak üzere öğe boyutlarındaki değişiklikleri algılar. Buradaki geri çağrısı sadece ayrı etiketi taşır, ölçülen paragrafın boyutunu tekrar tekrar değiştiren bir geri bildirim döngüsünden kaçınır. ResizeObserver.
Yazı tipi yüklenmesi kendi güncellemesini hak eder. Program hemen ölçer, ardından document.fonts.ready çözüldüğünde tekrar ölçer. MDN, bu vaadin belgenin yazı tipi yüklemesi ve ilişkili düzen işlemleri tamamlandıktan sonra çözüldüğünü belirtir. Daha sonraki uygulama odaklı yazı tipi veya içerik değişiklikleri açıkça başka bir ölçüm istemelidir. FontFaceSet: ready özelliği.
Koordinatları tek bir sistemde tutun. Demo boş bir sayfada sabit konumlu bir etiket kullanır ve istemci koordinatlarını doğrudan uygular. Belge kaydırma ofsetleri eklenirse bunlar belge koordinatlarına dönüşür. MDN, istemci dikdörtgenlerinin görüntü alanı göreli olduğunu ve belge göreli koordinatlar istendiğinde kaydırma ofsetlerinin eklenmesinin dönüşüm olduğunu açıklar. Element: getBoundingClientRect() yöntemi.
Bu küçük örnek yatay yerleşimi kısıtlar ancak dikey çarpışma önleme, ekran dışı gizleme veya rastgele dönüştürülmüş kapsayıcıları uygulamaz. Açıklamayı yeniden yükleme veya metnin tamamen değiştirilmesinden sonra geri getirmez de. Bir uygulama için, kasten geçişi belirlemenin bir yolunu saklayın, render sonrası range'ini yeniden oluşturun ve sonra tekrar ölçün.
Floating UI Ne Zaman Kullanmalısınız?
Etiket kırpma farkında yerleşim ve yönetilen bir güncelleme yaşam döngüsü gerektirdiğinde Floating UI kullanın. Belgelenen sanal referans arayüzü iki yöntemle bir range'i destekler: getBoundingClientRect() range'in kapsayan dikdörtgenini döndürür ve getClientRects() parça dikdörtgenlerini döndürür. Kırpma ve güncelleme algılamasının bir öğe bağlamı olmasını sağlamak için metni içeren gerçek bir öğeyle contextElement sağlayın. Sanal Öğeler.
Bu sanal referansı computePosition()'e iletin ve middleware dizisinin başlangıcına yakın, flip()'dan önce inline()'yi ekleyın. Yüzen öğenin CSS konumlandırmasını seçilen stratejiyla eşleştirin. Belgeler, alan yetersiz olduğunda kenar değiştirmek için flip()'i ve bir etiketi kırpma sınırları içinde taşımak için shift()'ü açıklar. inline, computePosition.
autoUpdate()'ü açıklama monteyken başlatın ve etiketi kaldırırken dönen temizleme fonksiyonunu çağırın. Belgelenen güncelleme mekanizmaları atalarak kaydırma, yeniden boyutlandırma ve düzen kaymalarını kapsar. Hala, gözlemlenen kapsayıcının boyutlarını değiştirmeden seçili metnin parçalarını değiştiren bir uygulama değişikliğinden sonra bir güncelleme tetikleyin. autoUpdate.
Hala Yanlış Gösteren Bir Açıklamayı Tanılayın
Bir geometri sorununun bir metin hedefleme sorununu gizlemesini önlemek için bu kontrolleri sırayla gerçekleştirin.
- İfadeyi onaylayın.
range.toString()'yi inceleyin ve koordinatları ayarlamadan önce her iki uç noktayı da kontrol edin. - Parçaları inceleyin.
Array.from(range.getClientRects())'i günlüğe kaydedin. Çoklu çakışan girişler iç içe satır içi içeriği yansıtabilir; sayacı satır sayısı olarak yorumlamayın. - Yerleştirme kuralını kontrol edin. Kapsayan dikdörtgen, son parça ve işaretçi ile seçilen parça farklı açıklama davranışlarına hizmet eder.
- Koordinat sistemini kontrol edin. Boş sayfa demosu için, sabit etiketi istemci koordinatlarında tutun.
- Düzen değişikliklerinden sonra tekrar edin. Genişliği değiştirin, kaydırın, yeniden boyutlandırın ve yazı tiplerinin yüklenmesini bekleyin. Uygulama metin düğümlerini değiştirirse range'i yeniden oluşturun.
- Düzen varsayımlarını kontrol edin. Dikey yazım, karışık yönler, sütunlar veya dönüştürülmüş kapsayıcılar için, demo'nun son parça kısayolunu yeniden kullanmadan önce uygun bir parça seçimi kuralı tanımlayın ve test edin.
Güvenilir bir açıklama doğru bir metin range'i ve açık bir yerleştirme kuralıyla başlar. Tarayıcıyı sarma ve şekillendirme konusunda sorumlu tutun, ardından ilgili düzen her değiştiğinde işlenmiş parçalarını yeniden ölçün. Bu, kelimelerin doğal akmasına izin verirken nota ifadeyle kararlı bir ilişki verir.
Kaynaklar
- DOM Standardı — WHATWG
- CSSOM View Module — W3C
- CSS Text Module Level 3 — W3C
- Range: getClientRects() yöntemi — MDN
- Element: getBoundingClientRect() yöntemi — MDN
- CSS custom highlight API — MDN
- Highlight — MDN
- ResizeObserver — MDN
- FontFaceSet: ready özelliği — MDN
- Web Konsolu — Mozilla
- inline — Floating UI
- Sanal Öğeler — Floating UI
- computePosition — Floating UI
- autoUpdate — Floating UI