Um DOM-Text zuverlässig zu messen, warten Sie auf das relevante Stylesheet, bringen Sie den Text in das vorgesehene Layout und warten Sie dann auf document.fonts.ready, bevor Sie die Geometrie auslesen. Diese Anleitung zeigt die Abfolge in einem Desktopbrowser auf dem Mac und erklärt, wann Sie sie wiederholen sollten.
Hier geht es darum, ein synthetisches Label oder einen umbrochenen Absatz auf einer Browserseite zu messen – zum Beispiel, um zu bestimmen, wie viel Platz eine Karte benötigt. Der Browser sollte das Textlayout übernehmen; Ihr Code sollte die Bereitschaft sicherstellen und die resultierenden Begrenzungsrahmen auslesen.
Auf welches Bereitschaftssignal sollten Sie warten?
Für den Dokumentaufbau, das Laden von Stylesheets und das Schriftlayout gibt es getrennte Meilensteine. Wählen Sie den Meilenstein, der zu den von Ihnen kontrollierten Vorgängen passt, und messen Sie anschließend, wenn Inhalt und Formatierung bereitstehen.
| Signal | Was dadurch sichergestellt wird | Was weiterhin beachtet werden muss |
|---|---|---|
DOMContentLoaded |
Das Parsen und die Ausführung aufgeschobener Skripte sind abgeschlossen | Es wartet nicht direkt auf Stylesheets |
Das load-Ereignis eines Stylesheets |
Das Stylesheet und seine importierten Inhalte wurden geladen und geparst | Das Laden der Schriftarten und das daraus resultierende Layout des Ziels |
Das load-Ereignis des Fensters |
Die anfänglichen Seitenressourcen einschließlich Stylesheets wurden geladen, ausgenommen verzögert geladene Ressourcen | Spätere Änderungen durch die Anwendung und die Bereitschaft der Schriftarten |
document.fonts.ready |
Das Laden und Layouten mit den im Dokument verwendeten Schriftarten ist abgeschlossen | Nachfolgende Änderungen an Inhalt, Formatierung oder Größe |
MDN erläutert die Besonderheit im Zusammenhang mit Stylesheets: DOMContentLoaded selbst wartet nicht auf Stylesheets, aufgeschobene Skripte warten jedoch darauf und können das Ereignis deshalb indirekt verzögern. Das Ereignis eignet sich zum Auffinden von Elementen, bildet aber eine unvollständige Grundlage für Messungen. Document: DOMContentLoaded event.
Wenn Sie ein Stylesheet selbst einfügen, registrieren Sie dessen Handler für load und error, bevor Sie den Link einfügen. Laut MDN tritt das load-Ereignis des Stylesheets nach dem Laden und Parsen auf, unmittelbar bevor die Formatierungen wirksam werden. Auch die Stylesheet-Verarbeitung im WHATWG-Standard unterscheidet zwischen erfolgreichem Laden und einem error-Ereignis. Stylesheet load events, HTML Standard: Stylesheet links.
Für das Laden der anfänglichen Seitenressourcen ist das load-Ereignis des Fensters ein umfassenderer Prüfpunkt. Ein Skript, das danach startet, muss document.readyState prüfen, bevor es sich für das Ereignis registriert, denn ein bereits ausgelöstes Ereignis ruft einen neu registrierten Handler nicht auf. Anwendungscode, der später ein Stylesheet einfügt, muss diesen Vorgang weiterhin explizit erfassen. Window: load event, Checking document readiness.
Was gewährleistet die Bereitschaft der Schriftarten tatsächlich?
Die W3C-Spezifikation CSS Font Loading erklärt, dass ready erfüllt wird, nachdem die relevanten Layoutvorgänge abgeschlossen sind und für diese Arbeit keine weiteren Schriftarten geladen werden müssen. Das schließt Fälle ein, in denen durch die Ersatzschrift weitere Ladevorgänge erforderlich werden. Dasselbe Promise wird nur einmal erfüllt, spätere Änderungen können jedoch weitere Ladevorgänge von Schriftarten auslösen. Werten Sie document.fonts.ready nach den für die jeweilige Messung relevanten Änderungen aus, statt ein einziges Promise vom Start dauerhaft wiederzuverwenden. CSS Font Loading Module Level 3: The ready attribute.
Die Bereitschaft bezieht sich auf die im Dokument verwendeten Schriftarten. Sie bedeutet nicht, dass alle deklarierten Schriftarten heruntergeladen wurden. MDN weist ausdrücklich darauf hin, dass sich die verwendeten Schriftarten bei font-display: optional von den deklarierten unterscheiden können. Eine abgeschlossene Messung kann daher den Text mit Ersatzschrift korrekt wiedergeben. Document: fonts property.
Entscheiden Sie vor dem Schreiben des Wartens, welches Ergebnis Sie benötigen:
- Das aktuell gerenderte Ergebnis messen: Warten Sie auf die Bereitschaft der Schriftarten und lesen Sie die DOM-Geometrie aus; dabei werden die Entscheidungen des Browsers zur Ersatzschrift akzeptiert.
- Eine deklarierte, herunterladbare Schriftart voraussetzen: Laden Sie die passende Schriftart explizit, behandeln Sie Fehler und warten Sie anschließend vor dem Messen auf die Bereitschaft der Dokument-Schriftarten.
- Sich ändernde Inhalte messen: Wiederholen Sie die Abfolge nach jeder relevanten Aktualisierung und verwerfen Sie Ergebnisse, die durch eine weitere Aktualisierung überholt wurden.
Für das zweite Ergebnis akzeptiert document.fonts.load() eine CSS-Schriftangabe und optional einen Beispieltext. Bei einer synthetischen deklarierten Schriftfamilie namens SampleFace könnte die Angabe 400 18px \"SampleFace\" lauten, der Text etwa Panel Alpha. Das zurückgegebene Promise wird abgewiesen, wenn eine passende Schriftart nicht geladen werden kann. Das Textargument wählt Schriftarten anhand ihrer deklarierten Unicode-Bereiche aus; es überprüft nicht, ob einzelne Glyphen enthalten sind. FontFaceSet: load() method.
Ersetzen Sie diese Anforderung nicht durch document.fonts.check(). MDN erläutert, dass die Methode true zurückgeben kann, selbst wenn die angeforderte Schriftart nicht verfügbar ist. Ebenso legt die W3C fest, dass ready niemals abgewiesen wird. Ein erfolgreicher Abschluss allein beweist daher nicht, dass das Laden der Schriftart erfolgreich war. FontFaceSet: check() method, CSS Font Loading: The ready attribute.
Ein vollständiges Beispiel vom Stylesheet bis zur Messung ausführen
Öffnen Sie auf Ihrem Mac about:blank in einem aktuellen Desktopbrowser, öffnen Sie die Entwicklertools und wählen Sie Console aus. Führen Sie das folgende vollständige JavaScript-Programm einmal aus. Es erstellt einen neutralen Beispieltext und ein Stylesheet selbst, sodass weder Server noch Bibliothek oder Schriftdatei erforderlich sind.
Das Beispiel verwendet bewusst system-ui. Es veranschaulicht die Abfolge des Wartens, ohne von einem herunterladbaren Asset abhängig zu sein. Auf einer Seite mit Webfonts behalten Sie die Abfolge bei und verwenden die tatsächlichen Schriftdeklarationen dieser Seite als Ressourcen. Das Browserbeispiel wurde geprüft, aber für dieses Manuskript nicht ausgeführt.
(async () => {
if (!document.fonts || !document.body) {
throw new Error("A document body and CSS Font Loading support are required.");
}
if (document.getElementById("text-measure-demo")) {
throw new Error("Reload the blank tab before running this example again.");
}
async function withDeadline(promise, label, milliseconds = 10000) {
let timer;
try {
return await Promise.race([
promise,
new Promise((_, reject) => {
timer = setTimeout(() => {
reject(new Error(`${label} did not finish within the deadline.`));
}, milliseconds);
})
]);
} finally {
clearTimeout(timer);
}
}
const paragraph = document.createElement("p");
paragraph.id = "text-measure-demo";
paragraph.textContent =
"Panel Alpha contains a compact label. Panel Beta contains another label.";
document.body.append(paragraph);
const css = `
#text-measure-demo {
box-sizing: border-box;
width: 280px;
margin: 24px;
padding: 12px;
border: 1px solid #777;
font: 400 18px/1.5 system-ui, sans-serif;
letter-spacing: normal;
white-space: normal;
overflow-wrap: normal;
}
`;
const stylesheetURL = URL.createObjectURL(
new Blob([css], { type: "text/css" })
);
const link = document.createElement("link");
link.rel = "stylesheet";
link.href = stylesheetURL;
const stylesheetLoaded = new Promise((resolve, reject) => {
link.onload = resolve;
link.onerror = () => reject(new Error("The sample stylesheet failed."));
});
try {
document.head.append(link);
await withDeadline(stylesheetLoaded, "Stylesheet loading");
// Establish the target's styled layout before reading font readiness.
paragraph.getBoundingClientRect();
await withDeadline(document.fonts.ready, "Font readiness");
const style = getComputedStyle(paragraph);
const range = document.createRange();
range.selectNodeContents(paragraph);
const copyRect = rect => ({
x: rect.x, y: rect.y, width: rect.width, height: rect.height
});
console.log({
text: paragraph.textContent,
fontFamily: style.fontFamily,
fontSize: style.fontSize,
lineHeight: style.lineHeight,
elementBox: copyRect(paragraph.getBoundingClientRect()),
textFragments: Array.from(range.getClientRects(), copyRect)
});
} catch (error) {
link.remove();
paragraph.remove();
throw error;
} finally {
link.onload = null;
link.onerror = null;
URL.revokeObjectURL(stylesheetURL);
}
})().catch(error => console.error(error));
Entscheidend ist die Reihenfolge: Abschluss des Stylesheets, festgelegtes Layout des Ziels, Bereitschaft der Schriftarten und schließlich das Auslesen der Geometrie. Das erste Auslesen der Geometrie legt das aktuelle Layout fest; sein Wert wird absichtlich verworfen. Die abschließenden Auslesevorgänge beschreiben das Ergebnis nach Eintritt der Schriftartenbereitschaft. Die Frist von zehn Sekunden ist eine beispielhafte operative Grenze: Bei Ablauf wird ein unvollständiger Versuch gemeldet und keine Messung erstellt.
Prüfen Sie beim Anpassen des Beispiels fontSize und lineHeight zusammen mit den Begrenzungsrahmen. getComputedStyle() liefert aufgelöste Eigenschaftswerte zurück, nachdem aktive Stylesheets angewendet wurden. So lassen sich Abweichungen durch Selektoren oder die Kaskade erkennen. Der Wert fontFamily beschreibt die CSS-Schriftfamilienliste; er beweist nicht, welche Schriftart tatsächlich jedes Glyph geliefert hat. Window: getComputedStyle() method.
Welche DOM-Messung beantwortet Ihre Frage?
Verwenden Sie element.getBoundingClientRect(), wenn Sie den äußeren Platzbedarf einer Karte oder eines Absatzes benötigen. Das Rechteck umfasst Innenabstände und Rahmen; seine Position ist relativ zum Ansichtsfenster. Im Beispiel beträgt die deklarierte Border-Box-Breite 280px; die verfügbare Inhaltsbreite des Textes beträgt 254px, berechnet als 280 − 24 − 2. Diese Berechnung beschreibt das vorgegebene CSS, kein beobachtetes Browserergebnis. Element: getBoundingClientRect() method.
Verwenden Sie einen Range über dem Text, wenn Sie dessen Layoutfragmente benötigen. Range.getClientRects() gibt Rechtecke für den Bereich zurück, nicht nur für den umschließenden Absatz. Behalten Sie die Sammlung bei: Wenn Sie den umbrochenen Text zu einer einzigen Breite zusammenfassen, gehen nützliche Informationen über die belegten Bereiche verloren. Nennen Sie die Array-Länge in einer allgemeinen Implementierung nicht lineCount; komplexe Inline-Inhalte können mehrere Rechtecke beitragen. Range: getClientRects() method.
Lassen Sie den ursprünglichen Textlauf während der Messung unverändert. Ein Wrapper um jedes einzelne Zeichen kann Formatierungsgrenzen einführen, die das Text-Shaping beeinflussen. Der CSS-Text-Entwurf der W3C legt Bedingungen fest, unter denen Inline-Grenzen das Text-Shaping beibehalten oder unterbrechen. Wenn Sie das bestehende DOM mit einem Range messen, vermeiden Sie solche Wrapper. CSS Text Module Level 3: Shaping Across Element Boundaries.
Canvas hat eine andere Funktion. measureText() liefert Textmetriken für eine angegebene Zeichenfolge auf einem Canvas zurück. Bei dieser Aufgabe – den tatsächlichen Zeilenumbruch von Text in einer DOM-Karte zu ermitteln – lesen Sie nach Eintritt der Bereitschaft das DOM aus. Behandeln Sie eine Canvas-Messung als separate Berechnung mit eigenen Texteinstellungen. CanvasRenderingContext2D: measureText() method.
Was sollten Sie tun, wenn sich die Messwerte weiterhin ändern?
Verwenden Sie diese Diagnoseschritte, bevor Sie eine weitere Verzögerung hinzufügen:
- Vergewissern Sie sich, dass der Abschluss des Stylesheets, auf den Sie gewartet haben, zu dem Stylesheet gehört, das das Ziel formatiert. Speichern Sie bei dynamisch eingefügten Stylesheets deren Lade-Promises zusammen mit dem Einfügecode.
- Prüfen Sie die aufgelöste Typografie und Breite des Ziels. Auch ein geladenes Stylesheet kann in der Kaskade unterliegen.
- Vergewissern Sie sich, dass der Zieltext und sein vorgesehener Container vorhanden sind, bevor Sie auf die Bereitschaft der Schriftarten warten.
- Entscheiden Sie, ob eine Ersatzschrift akzeptabel ist. Wenn eine bestimmte deklarierte Schriftart erforderlich ist, laden Sie sie explizit und melden Sie Fehler.
- Prüfen Sie, ob eine weitere Anwendungsaktualisierung nach dem Warten den Text, die Klassen oder den Container geändert hat. Starten Sie die Messung für diesen neuen Zustand erneut.
Ein Frame-Callback ist keine Barriere für das Laden von Ressourcen. requestAnimationFrame() plant einen Callback vor dem nächsten Neuzeichnen ein; in Hintergrundtabs setzen Browser ihn häufig aus. Verwenden Sie ihn, wenn die Frame-Planung für Ihre Anwendung sinnvoll ist, aber ersetzen Sie Promises für Stylesheets oder Schriftarten nicht durch einen oder zwei Frame-Callbacks. Window: requestAnimationFrame() method.
Beobachten Sie bei einem Layout mit veränderbarer Größe den relevanten Container mit ResizeObserver und planen Sie neue Messungen ein, wenn sich seine Abmessungen ändern. Der Observer meldet Änderungen an der Content-Box- oder Border-Box-Größe; er ist keine universelle Benachrichtigung über Textänderungen. Lösen Sie Messungen außerdem über Anwendungsvorgänge aus, die Text ersetzen oder die Typografie ändern. ResizeObserver.
Wenn Sie bereits Playwright verwenden, führen Sie das Warten auf die browserseitige Bereitschaft und das Auslesen der Geometrie innerhalb eines asynchronen page.evaluate()-Callbacks aus. Playwright wartet auf das zurückgegebene Promise und übermittelt das Ergebnis an die aufrufende Umgebung. Geben Sie einfache numerische Rechteckfelder zurück, wie im Beispiel, und übergeben Sie alle Werte der aufrufenden Seite explizit an den Callback. Evaluating JavaScript.
Eine nützliche Messung gehört zu einem bestimmten Text, Stylesheet-Zustand, Schriftartenergebnis und einer bestimmten Containergröße. Legen Sie diese Eingaben fest, warten Sie auf die relevanten Ladevorgänge und messen Sie das unveränderte DOM. Wiederholen Sie die Messung, wenn sich diese Eingaben ändern, damit die erfasste Geometrie weiterhin die tatsächliche Layoutfrage beantwortet.
Quellen
- CSS Font Loading Module Level 3 — W3C.
- HTML Standard: Link type stylesheet — WHATWG.
- Document: DOMContentLoaded event — MDN Web Docs.
<link>: The HTML External Resource Link element — MDN Web Docs.- Window: load event — MDN Web Docs.
- Document: fonts property — MDN Web Docs.
- FontFaceSet: load() method — MDN Web Docs.
- FontFaceSet: check() method — MDN Web Docs.
- Window: getComputedStyle() method — MDN Web Docs.
- Element: getBoundingClientRect() method — MDN Web Docs.
- Range: getClientRects() method — MDN Web Docs.
- CSS Text Module Level 3 — W3C.
- CanvasRenderingContext2D: measureText() method — MDN Web Docs.
- Window: requestAnimationFrame() method — MDN Web Docs.
- ResizeObserver — MDN Web Docs.
- Evaluating JavaScript — Playwright.