JavaScript und CSS/CSS-Eigenschaften auslesen

Aus SELFHTML-Wiki
< JavaScript und CSS(Weitergeleitet von ResizeObserver)
Wechseln zu: Navigation, Suche

Oft wird gefragt, wie man Abmessungen von Elementen mit JavaScript ermitteln kann. Häufig liegt hier bereits der Denkfehler - modernes CSS überlässt solche Berechnungen dem Browser.

Empfehlung: Verzichte auf das Setzen fester Größenangaben!

Meist ist es nicht nötig, diese Werte zu ermitteln, da sich ein responsives Layout unabhängig von genauen Pixelangaben an den Viewport anpasst.

ermöglichen es, dass der Browser Seitenelemente automatisch ohne weiteres Zutun des Entwicklers passend positioniert.

Wenn du aktuelle Pixelwerte benötigst, um dein Layout mit JavaScript zu steuern, hast du schon vorher konzeptuelle Fehler eingebaut!

Und trotzdem kann es sinnvoll sein, mit JavaScript CSS-Eigenschaften, bzw. die Abmessungen von HTMLElement-Objekten auszulesen, um diese Informationen anderswo zu verarbeiten. Dieses Kapitel stellt die verschiedenen Möglichkeiten, Anwendungsfälle und Best practices vor.

CSS-Eigenschaften auslesen

Das style-Objekt wird immer wieder missverstanden: Man kann damit lediglich Inline-Styles setzen und die bereits gesetzten auslesen, aber nicht den berechneten Wert einer CSS-Eigenschaft ermitteln.

Die besagten Objekteigenschaften (.style.cssEigenschaft) sind allesamt leer, wenn sie nicht im betreffenden HTML-Element über ein style-Attribut oder wie beschrieben mit JavaScript gesetzt wurden:

auslesen von Inline-Styles
<p id="ohne-inline-styles">Element ohne Inline-Styles</p>
<p id="mit-inline-styles" style="color: red">Element mit Inline-Styles</p>
// Gibt einen leeren String aus:
console.log(
    document.getElementById('ohne-inline-styles').style.backgroundColor
);
// Gibt »red« aus, weil Inline-Style gesetzt wurde:
console.log(
    document.getElementById('mit-inline-styles').style.backgroundColor
);

getComputedStyle

Das Auslesen des gegenwärtigen Werts von CSS-Eigenschaften eines Elements gestaltet sich als schwierig. Wenn man in Erfahrung bringen will, welche tatsächliche Textfarbe oder welche Pixel-Breite ein Element hat, dann ist nach den sogenannten berechneten Werten (englisch: computed values) gefragt, wie sie in der CSS-Fachsprache genannt werden.

Im Allgemeinen besteht der aktuelle CSS-Eigenschaftswert eines Elements aus einer Kombination von …

  • Standardformatierungen des Browsers
  • (oft mehreren) im CSS festgelegten Regelsätzen, die über Selektoren ausgewählt werden und dann nach den Regeln der Kaskade kombiniert und überschrieben werden.

Die Methode Window.getComputedStyle() enthält für jede CSS-Eigenschaft eine entsprechende Objekteigenschaft mit dem aktuellen berechneten Wert.

Ausgabe der berechneten Werte von CSS-Eigenschaften ansehen …
page.addEventListener('mouseover',getCSSValues);		

  function getCSSValues(event) { 
    const element = event.target;
		const compStyles = window.getComputedStyle(element);
		let textsize  = compStyles.getPropertyValue('font-size');
		let textcolor = compStyles.getPropertyValue('color');		
 
		document.getElementById('textsize').textContent = textsize;
		document.getElementById('textcolor').textContent = textcolor;		
  }

Im Beispiel wird ein addEventListener gesetzt, der beim Überfahren mit der Maus die Funktion getCSSValues() aufruft. Diese ermittelt mit getComputedStyle() die berechneten Werte. Wie man in der Konsole sehen kann, werden alle Werte (Stand Februar 2024: 367!) angezeigt.

Um das Beispiel übersichtlich zu halten, werden mit getPropertyValue() nur die Werte für font-size und color ausgelesen.

Die berechneten Werte (computed values), die getComputedStyle zurückgibt, werden letztlich in Pixelwerte umgerechnet, sodass getComputedStyle Werte mit der Einheit px zurückgibt. Gerade bei der verschachtelten ul sieht man, dass der Standardschriftwert in jeder Ebene mit 0.8 multipliziert und so immer kleiner wird.

Beachte, dass die Werte prinzipiell in Pixel ausgegeben werden, CSS arbeitet aber oft mit anderen Werten (CSS-Pixel != reelle Pixel)

Auch beispielsweise bei Farbwerten ist das ähnlich:

Bei Farbangaben wie beispielsweise color: red, oder #ff0000 liefert getComputedStyle für color den Wert "rgb(255 0 0)". Dies ist derselbe Wert in einer alternativen Schreibweise, wie das Farbfeld zeigt.

Beachte: Besuche einen der Links auf der Seite und beobachte was passiert!
Spoiler: Die Linkfarbe ändert sich, da die Pseudoklasse :visited aktiviert wird.
Allerdings gibt getComputedStyle die Farbe weiter wie bei :unvisited-Links an, damit man nicht per JS herausfinden kann, welche Seiten besucht wurden.[1]
Beachte: Vereinzelt findet man noch eine getDefaultComputedStyle-Methode - sie ist nicht-standardisiert und sollte nicht verwendet werden!

Elementbox-Größen auslesen

Um die aktuelle Höhe und Breite einer Element-Box zu ermitteln, müssen wir erst einmal überlegen, welche Werte wir dafür benötigen. Neben den Eigenschaften width und height bestimmen ja auch noch Rahmen und Innen- und Außenabstände die Dimensionen von Element-Boxen. So gibt es mehrere Möglichkeiten, die Größe zu ermitteln:

  • clientWidth und clientHeight: liefern Breite bzw. Höhe der Innenabstand-Box des Elements. Das bedeutet, dass padding inbegriffen ist, während border und margin nicht eingerechnet werden. Ebenso wird die Größe einer möglicherweise angezeigte Bildlaufleiste (Scrollbar) nicht einberechnet.
  • offsetWidth und offsetHeight: liefern die Breite bzw. Höhe der Rahmen-Box des Elements. Das bedeutet, dass der Innenabstand (padding) und der Rahmen (border) inbegriffen sind, der Außenrahmen hingegen (margin) nicht.
  • scrollWidth und scrollHeight: geben die tatsächlich angezeigte Breite bzw. Höhe des Inhalts wieder. Wenn das Element kleiner ist, als der Inhalt es erfordert, also Bildlaufleisten angezeigt werden, so geben diese Eigenschaften die Größe des aktuell sichtbaren Ausschnittes wieder.[2]

In den meisten Fällen benötigt man die äußere Größe eines Elements, also offsetWidth und offsetHeight.

Elementgröße berechnen ansehen …
var element = document.getElementById('beispielID');
console.log('Breite: ' + element.offsetWidth + '\nHöhe: ' + element.offsetHeight);

Im Gegensatz zu getComputedStyle geben sie keine String-Werte samt Einheiten zurück, sondern direkt JavaScript-Zahlen (Number-Werte) in der Einheit Pixel.

Falls du die innere Größe benötigst, so kannst du zunächst die aktuellen Werte der jeweiligen padding-Eigenschaften auslesen. Das sind padding-left und padding-right für die Breite bzw. padding-top und padding-bottom für die Höhe.

let Innenbreite = offsetWidth - (padding-left + padding-right);
let Innenhoehe = offsetHeight - (padding-top + padding-bottom);
Beachte: Die clientwidth-Eigenschaft berechnet einen ganzzahligen Wert. Verwende getBoundingClientRect(), wenn du genauere Angaben benötigst.

getBoundingClientRect

Die Element.getBoundingClientRect()-Methode gibt die Größe und Position eines Elements relativ zur Größe des Viewports zurück.


Syntax

rectObject = element.getBoundingClientRect(node);

  • rectObject: Rückgabewert ist ein DOMRect-Objekt, das left, top, right, bottom, x, y, width, height-Eigenschaften besitzt, die ausgelesen werden können.
    • Alle Eigenschaften außer width and height sind relativ zur oberen linken Ecke (top-left) des Viewports.
    • width und height geben die tatsächliche Breite, bzw. Höhe des Elements zurück, also
      Höhe + padding + border-width, nicht nur die Größe der content-box.

Mit der getBoundingClientRect-Methode kannst du einen einfachen Seiteninspektor erstellen:

Beispiel ansehen …
  function getCSSValues(event) { 
		const element = event.target;
		const name = element.nodeName;
		const rect = element.getBoundingClientRect();		
		let result = `
			Elementname: ${name} \n
			x₁, y₁ :    ${rect.left}px, ${rect.top}px  (links oben) \n
			x₂, y₂:    ${rect.right}px, ${rect.bottom}px  (rechts unten) \n			
			Höhe:   ${rect.height}px \n
			Breite: ${rect.width}px 		
			`; 
		console.log(result);		
		result = result.replaceAll("\n", "<br>");
		document.getElementById('info').innerHTML = result;
  }

An das body-Element werden mit addEventListener zwei Eventhandler angefügt, sodass beim Überfahren oder beim Klick mit der Maus die Funktion getCSSValues() aufgerufen wird.

Diese Funktion ermittelt mit event.target das Element, durch das das Ereignis ausgelöst wurde. Von diesem Element werden jetzt mit getBoundingClientRect() die einzelnen Eigenschaften abgefragt, in einer Variaben result zusammengefügt und anschließend in der Konsole ausgegeben.

Die Variable result enthält mehrere line breaks \n. Da der string auch im HTML sichtbar gemacht werden soll, werden mit String.replaceAll alle Vorkommen mit einem br ersetzt und es anschließend im output-Element ausgegeben.

Beachte, dass die Werte im CSS in em festgelegt wurden - mit getBoundingClientRect() aber in px ermittelt werden.
Es wäre schöner, wenn die ausgegebenen Werte mit toFixed nur 2 oder 3 Nachkommastellen erhielten; es sollte in diesem Beispiel aber die Genauigkeit demonstriert werden.


Siehe auch

  • Infobox/Akkordeon mit details
    Um das Auf- und Zuklappen eines details-Elements zu animieren, wird zuerst mit getBoundingClientRect() die Höhe der Box einmal im geöffneten und einmal im geschlossenen Zustand ermittelt.

computedStyleMap

Die computedStyleMap()-Methode der Element-Schnittstelle gibt ein StylePropertyMapReadOnly-Objekt zurück.

Dieses Objekt ist mit dem CSSStyleDeclaration-Objekt vergleichbar, das du in der style-Eigenschaft des Elements findest. Es enthält die Eigenschaftswerte jedoch in Form eines CSSStyleValue-Objekts, das einen einfacheren Zugriff auf Werte und Einheiten bietet. (Im MDN gibt es ein sehenswertes Beispiel, dass alle Eigenschaften des StylePropertyMapReadOnly auflistet.)

In der StylePropertyMapReadOnly gibt es auch keinen direkten Zugriff auf CSS-Eigenschaften über den Eigenschaftsnamen. Du musst für den Zugriff auf einen Eigenschaftswert die get-Methode verwenden, oder mit entries() einen Iterator für die Einträge erhalten (den du dann zum Beispiel mit einer for...of Schleife durchlaufen kannst).

Eigenschaften und Werte separat auslesen ansehen …
  function getCSSValues(event) { 
		const element = event.target;
		const compStyles = element.computedStyleMap();
		console.log(compStyles);
		let name = element.nodeName;
		let value = element.computedStyleMap().get('font-size').value;  
		let unit = element.computedStyleMap().get('font-size').unit; 
		let result = `
			Elementname: ${name} \n
			Schriftgröße:    \n
			- Wert:    ${value} \n
			- Einheit: ${unit} \n			
			`; 
		console.log(result);				
  }

Jedes mit der Maus oder Klick gewählte Element wird analysiert.

  • computedStyleMap() erzeugt das StylePropertyMapReadOnly-Objekt , das (Stand: März 2024) in Chrome 380 Einträge hat.
  • element.computedStyleMap().get('font-size').value; liest mit der get-Methode den Wert der font-size-Eigenschaft aus. anders als in den oben vorgestellten Methoden ist dies kein String sondern eine number - so könnte man den erhaltenen Wert einfach berechnen.
  • element.computedStyleMap().get('font-size').unit; gibt die dazugehörige Einheit aus. Anders als im CSS notiert, wird der berechnete Wert in px angezeigt.

Hauptartikel: CSS Typed OM

Beachte: Dieses Beispiel funktioniert in allen Browsern außer dem Firefox. Es ist nicht absehbar, wann das CSS Typed OM implementiert wird.

auf Veränderungen reagieren

resizes mit resizeObserver

Oft möchte man mit Javascript die Größe von Fenstern und Boxen überprüfen. Der typische Weg ist die Verwendung eines Event-Listeners auf das resize-Event des Fensters:[3]


el.addEventListener('resize', () => {
  if (document.body.clientWidth >= 750) {
    // do something
  }
});

Allerdings wird das Event jedes Mal gefeuert, wenn das Fenster in der Größe verändert wird, und zwar bei jeder Pixelvergrößerung/-verkleinerung! Dies kann zu enormen Leistungsproblemen führen, wenn die Funktion des Ereignis-Listeners zu langsam ist.

Und eigentlich wollten wir doch nur wissen, ob die Größe des Bildes noch in das Layout passt.


In folgendem Beispiel soll der Platzbedarf für einen Sticky Header ermittelt und freigehalten werden. Auf großen Viewports passt die Navigation in eine Zeile, bei schmalen Viewports nimmt sie mehr Platz ein.[4]

Platzbedarf für Sticky Header ermitteln ansehen …
const resizeObserver = new ResizeObserver(entries => {
  document.documentElement.style.setProperty(
    '--header-height', `${entries[0].target.offsetHeight}px`
  );
});

resizeObserver.observe(document.querySelector('nav'));

Der resizeObserver ermittelt die offsetHeight des nav-Elements und weist sie der custom property --header-height zu.

Dieser Wert wird nun dem html-Element als oberer Scroll-Innenabstand zugewiesen:

html {
	scroll-padding-top: var(--header-height);
}


Weitere Observer

Es gibt noch weitere Observer, z.B. ...

  • Der Intersection Observer überwacht, ob ein Element gerade sichtbar ist.
  • Der MutationObserver achtet darauf, ob irgendwo im DOM etwas verändert wird.

Siehe auch

Medienabfragen mit matchMedia

Ist der verfügbare Platz noch ausreichend für das Element? Oft gibt es in solchen Fällen eine bessere Alternative, nämlich die Verwendung von media queries. Im Stylesheet wird ein Regelsatz in einer Medienabfrage notiert und der Browser entscheidet selbstständig, ob die Bedingung zutrifft. Dafür benötigt man kein JavaScript.

Und doch gibt es Spezialfälle, bei denen man Berechnungen in JavaScript anstellen muss, wenn man …

  • eben nicht genau weiß, wie lang oder breit ein Element ist. Eine Navigation soll die Links horizontal anordnen. Abhängig von Schriftgröße und verwendetem Font ->ich messe bei meinem Navigationsmenü, ob es noch einzeilig auf die Seite passt, oder ob ich das Hamburgericon zeige.

Deshalb kann man die Medienabfrage mit Window.matchMedia() durch JavaScript durchführen lassen:

const mediaQueryList = window.matchMedia('(min-width: 35em)');

mediaQueryList.addEventListener('change', event => {
    console.log(window.innerWidth);
  if (event.matches) {
    console.log('The window is now 35em or under');
  } else {
    console.log('The window is now over 35em');
  }
})

Bildern im Quer- oder sogar landscape-Format bieten ein Panorama. Je kleiner der Bildschirm wird, desto winziger wird die Grafik. Während man bei der Art Direction mit mehreren Bildern arbeitet, kann man bei SVG die viewBox und damit Seitenverhältnis und sichtbare Ausschnitte ändern. Da es sich bei viewBox aber (noch) nicht um ein Präsentationsattribut handelt, lässt sich das nicht mit CSS @media-Abfragen lösen. Mit JavaScript und matchMedia geht es:

viewBox-Anpassung ansehen …
const landscape = document.querySelector('#landscape'),
      mql       = window.matchMedia("(max-width:50em)");

changeViewBox(mql);

mql.addEventListener('change', changeViewBox);

function changeViewBox(mql) {
    if (mql.matches) {
        landscape.setAttribute('viewBox','100 66 120 100');
        console.log ('Kleines Browserfenster: max-width: 50em');
    } else {
        landscape.setAttribute('viewBox','0 66 250 100');
        console.log ('Großes Browserfenster max-width:50em ist false');
    }
}

Das SVG-viewBox-Attribut legt den sichtbaren Ausschnitt einer Grafik fest und kann nicht durch CSS angesprochen werden. changeViewBox(mql) ändert das viewBox-Attribut, wenn die Media query erfüllt ist, um das Bild auf kleinen Monitoren als Ausschnitt und auf großen Monitoren in voller Breite zu zeigen.

Siehe auch

  • Hintergrundvideo
    Nutzer, die keine Animation und Bewegung mögen, erhalten mit window.matchMedia('(prefers-reduced-motion: reduce)') nur ein Standbild
  • Fullscreen
    window.matchMedia('(display-mode: fullscreen)') überprüft, ob die Webseite im Fullscreen-Modus ist und fügt dann einen Schließen-Button hinzu, da es auf Tablets kein ESC gibt
  • Dark Mode
    kombinierte Abfrage von prefers-color-scheme und Nutzerauswahl

Wie man sehen kann, wird mit matchMedia() viel mehr als nur Längen ermittelt!

Weblinks

  1. :visited links have limited styling and getComputedStyle lies about their style(css-tricks.com)
  2. MDN: Determining the dimensions of elements
  3. Why you should use window.matchMedia when checking for window resizes in Javascript October 3, 2020 (webdevetc.com)
  4. sticky header, scroll-padding-top, ResizeObserver CodePen von Gunnar Bittersmann