Mestr ResizeObserver API'et for præcist at spore ændringer i elementstørrelser og bygge robuste, responsive weblayouts. Dyk ned i fordele, brugsscenarier og bedste praksis for moderne webudvikling.
ResizeObserver API'et: Præcis sporing af elementstørrelse for dynamiske, responsive layouts
I det store og evigt udviklende landskab inden for webudvikling er det fortsat en afgørende udfordring at skabe ægte responsive og adaptive brugergrænseflader. Mens media queries længe har fungeret som hjørnestenen for tilpasning af layouts til forskellige viewport-størrelser, kræver det moderne web en mere detaljeret tilgang: responsivitet på komponentniveau. Det er her, det kraftfulde ResizeObserver API træder til og revolutionerer, hvordan udviklere sporer og reagerer på ændringer i et elements størrelse, uafhængigt af viewporten.
Denne omfattende guide vil dykke dybt ned i ResizeObserver API'et og udforske dets mekanismer, forskellige anvendelsesmuligheder, bedste praksis, og hvordan det giver udviklere mulighed for at bygge yderst dynamiske og robuste weboplevelser for et globalt publikum.
Forståelse af kerneproblemet: Hvorfor window.resize kommer til kort
I mange år var den primære mekanisme til at reagere på layoutændringer i browseren window.resize-eventet. Udviklere ville tilknytte event listeners til window-objektet for at registrere, når browserens viewport ændrede dimensioner. Denne tilgang har dog betydelige begrænsninger i nutidens komponentdrevne verden:
- Kun viewport-centreret:
window.resize-eventet udløses kun, når selve browservinduet ændres i størrelse. Det giver ingen information om individuelle elementer i dokumentet, der ændrer størrelse på grund af andre faktorer. - Begrænset rækkevidde: En komponent kan have brug for at justere sit interne layout, hvis dens overordnede container skrumper eller udvides, selvom den samlede viewport-størrelse forbliver konstant. Tænk på en sidebar, der klappes sammen, eller et fanepanel, der afslører nyt indhold.
window.resizegiver ingen indsigt i disse lokale ændringer. - Ineffektiv polling: For at spore ændringer på elementniveau uden
ResizeObserver, tyede udviklere ofte til ineffektive og performance-krævende polling-mekanismer ved hjælp afsetInterval, hvor de gentagne gange tjekkedeelement.offsetWidthellerelement.offsetHeight. Dette fører til unødvendige beregninger og potentiel 'jank'. - Kompleks kommunikation på tværs af komponenter: At orkestrere størrelsesændringer mellem dybt indlejrede eller uafhængige komponenter bliver et rodet virvar uden en direkte måde for en komponent at kende sin egen tildelte plads.
Overvej et scenarie, hvor et datavisualiseringsdiagram dynamisk skal ændre størrelse, når dets indeholdende <div>-element justeres af en bruger, måske via en trækbar splitter. window.resize ville være ubrugelig her. Det er præcis denne type udfordring, ResizeObserver blev designet til at løse.
Introduktion til ResizeObserver API'et
ResizeObserver API'et giver en performant og effektiv måde at observere ændringer i størrelsen på et elements content box eller border box. I modsætning til window.resize, som overvåger viewporten, fokuserer ResizeObserver på de specifikke dimensioner af et eller flere mål-DOM-elementer.
Det er en kraftfuld tilføjelse til suiten af web-API'er, der giver udviklere mulighed for at:
- Reagere på elementspecifikke størrelsesændringer: Få besked, når et observeret elements størrelse ændres, uanset om vinduet ændrede størrelse eller ej. Dette inkluderer ændringer forårsaget af CSS-layouts (flexbox, grid), dynamisk indholdsinjektion eller brugerinteraktioner.
- Undgå uendelige resize-løkker: API'et er designet til at forhindre uendelige løkker, der kan opstå, hvis en resize-event handler direkte ændrer det observerede elements størrelse, hvilket udløser endnu et resize-event. ResizeObserver samler ændringer og behandler dem effektivt.
- Forbedre performance: Ved at levere en deklarativ, event-drevet mekanisme eliminerer det behovet for dyre polling-metoder eller komplekse intersection observer-hacks til størrelsessporing.
- Muliggøre ægte responsivitet på komponentniveau: Komponenter kan blive virkelig selvbevidste om deres tildelte plads, hvilket fører til mere modulære, genanvendelige og robuste UI-elementer.
Sådan virker ResizeObserver: Et praktisk dyk ned i detaljerne
Brugen af ResizeObserver API'et involverer et par enkle trin: at instantiere en observer, fortælle den, hvilke elementer den skal overvåge, og derefter håndtere ændringerne i en callback-funktion.
Instantiering og observation
Først opretter du en ny instans af ResizeObserver og giver den en callback-funktion, der vil blive eksekveret, hver gang et overvåget elements størrelse ændres.
// Opret en ny ResizeObserver-instans
const myObserver = new ResizeObserver(entries => {
// Denne callback vil blive eksekveret, når det observerede elements størrelse ændres
for (let entry of entries) {
const targetElement = entry.target;
const newWidth = entry.contentRect.width;
const newHeight = entry.contentRect.height;
console.log(`Element ${targetElement.id || targetElement.tagName} ændrede størrelse til ${newWidth}px x ${newHeight}px.`);
// Udfør handlinger baseret på den nye størrelse
}
});
Når du har en observer-instans, kan du fortælle den, hvilke DOM-elementer den skal observere ved hjælp af observe()-metoden:
// Hent det element, du vil observere
const myElement = document.getElementById('myResizableDiv');
// Start observationen af elementet
if (myElement) {
myObserver.observe(myElement);
console.log('Observation startet for myResizableDiv.');
} else {
console.error('Elementet #myResizableDiv blev ikke fundet.');
}
Du kan observere flere elementer med den samme observer-instans:
const element1 = document.getElementById('chartContainer');
const element2 = document.querySelector('.responsive-sidebar');
if (element1) myObserver.observe(element1);
if (element2) myObserver.observe(element2);
For at stoppe med at observere et specifikt element, skal du bruge unobserve():
// Stop observationen af et enkelt element
if (myElement) {
myObserver.unobserve(myElement);
console.log('Observation stoppet for myResizableDiv.');
}
For at stoppe med at observere alle elementer og afkoble observeren helt, skal du bruge disconnect():
// Afkobl observeren fra alle observerede elementer
myObserver.disconnect();
console.log('ResizeObserver afkoblet.');
Callback-funktionen og ResizeObserverEntry
Callback-funktionen, der sendes til ResizeObserver, modtager et array af ResizeObserverEntry-objekter. Hver entry svarer til et element, hvis størrelse har ændret sig siden sidste notifikation.
Et ResizeObserverEntry-objekt giver afgørende information om størrelsesændringen:
target: En reference til det DOM-element, der er blevet ændret i størrelse.contentRect: EtDOMRectReadOnly-objekt, der repræsenterer størrelsen på elementets content box (området inden for padding og border). Dette er ofte den mest anvendte egenskab til generel indholdsstørrelse.borderBoxSize: Et array afResizeObserverSize-objekter. Dette giver dimensionerne af elementets border box, inklusive padding og border. Nyttigt, når du skal tage højde for disse i dine layoutberegninger. Hvert objekt i arrayet indeholderinlineSizeogblockSize.contentBoxSize: Et array afResizeObserverSize-objekter, der lignerborderBoxSize, men repræsenterer content box. Dette betragtes som mere moderne og præcist endcontentRectfor indholdsdimensioner, især i layouts med flere kolonner eller ved håndtering af skriftretninger.devicePixelContentBoxSize: Et array afResizeObserverSize-objekter, der angiver content box-dimensionerne i enhedspixels, hvilket er nyttigt for pixel-perfekt rendering, især på skærme med høj DPI.
Lad os se på et eksempel, der bruger disse egenskaber:
const detailedObserver = new ResizeObserver(entries => {
for (let entry of entries) {
console.log(`--- Ændret element: ${entry.target.id || entry.target.tagName} ---`);
// Gammel contentRect (DOMRectReadOnly)
console.log('ContentRect (legacy):');
console.log(` Bredde: ${entry.contentRect.width}px`);
console.log(` Højde: ${entry.contentRect.height}px`);
console.log(` X: ${entry.contentRect.x}px`);
console.log(` Y: ${entry.contentRect.y}px`);
// Moderne contentBoxSize (array af ResizeObserverSize)
if (entry.contentBoxSize && entry.contentBoxSize.length > 0) {
const contentBox = entry.contentBoxSize[0];
console.log('ContentBoxSize (moderne):');
console.log(` Inline Size (bredde): ${contentBox.inlineSize}px`);
console.log(` Block Size (højde): ${contentBox.blockSize}px`);
}
// BorderBoxSize (array af ResizeObserverSize)
if (entry.borderBoxSize && entry.borderBoxSize.length > 0) {
const borderBox = entry.borderBoxSize[0];
console.log('BorderBoxSize:');
console.log(` Inline Size (bredde inkl. padding/border): ${borderBox.inlineSize}px`);
console.log(` Block Size (højde inkl. padding/border): ${borderBox.blockSize}px`);
}
// DevicePixelContentBoxSize (array af ResizeObserverSize)
if (entry.devicePixelContentBoxSize && entry.devicePixelContentBoxSize.length > 0) {
const devicePixelBox = entry.devicePixelContentBoxSize[0];
console.log('DevicePixelContentBoxSize:');
console.log(` Inline Size (enhedspixels): ${devicePixelBox.inlineSize}px`);
console.log(` Block Size (enhedspixels): ${devicePixelBox.blockSize}px`);
}
}
});
const observeMe = document.getElementById('observeThisDiv');
if (observeMe) {
detailedObserver.observe(observeMe);
}
En note om contentRect vs. contentBoxSize: Selvom contentRect er bredt understøttet og intuitivt, er contentBoxSize og borderBoxSize nyere tilføjelser til specifikationen. De giver et array af ResizeObserverSize-objekter, fordi et element kan have flere fragmenter, hvis det er i et layout med flere kolonner. I de fleste almindelige scenarier med et enkelt fragment vil du tilgå det første element i arrayet (f.eks. entry.contentBoxSize[0].inlineSize).
Anvendelsesscenarier fra den virkelige verden til responsiv layoutstyring
Anvendelserne af ResizeObserver er utroligt forskellige og giver udviklere mulighed for at bygge mere fleksible og robuste brugergrænseflader. Her er nogle overbevisende scenarier fra den virkelige verden:
Dynamiske diagrammer og datavisualiseringer
Diagrambiblioteker (som Chart.js, D3.js, Highcharts osv.) skal ofte gentegne eller justere deres skalaer, når deres container ændrer størrelse. Traditionelt involverede dette at lytte til window.resize og derefter manuelt kontrollere, om diagrammets forældreelement havde ændret sig. Med ResizeObserver kan diagrammer simpelthen observere deres egen container og reagere direkte.
Eksempel: Et dashboard med flere diagrammer arrangeret i et gitter. Når en bruger ændrer størrelsen på et panel eller ændrer layoutet, gentegner hvert diagram automatisk for at passe perfekt til sine nye dimensioner, uden flimmer eller manuel indgriben.
Adaptive gridsystemer og tabeller
Responsive tabeller er notorisk vanskelige. Du vil måske skjule visse kolonner, omdanne en tabel til en liste-lignende struktur eller justere kolonnebredder baseret på den tilgængelige plads. I stedet for at stole på media queries, der gælder for hele viewporten, giver ResizeObserver en tabelkomponent mulighed for at bestemme sin egen responsivitet baseret på sin egen bredde.
Eksempel: En e-handels-produkttabel. Når dens container bliver smal, kan specifikke kolonner som "produkt-ID" eller "lagerniveau" blive skjult, og de resterende kolonner kan udvides for at fylde pladsen. Hvis containeren bliver meget smal, kan tabellen endda transformeres til et kortbaseret layout.
Brugerdefinerede UI-komponenter og widgets
Mange webapplikationer har komplekse, genanvendelige UI-komponenter: sidebars, modals, trækbare paneler eller indlejrede widgets. Disse komponenter skal ofte tilpasse deres interne layout baseret på den plads, der er tildelt dem af deres forælder. ResizeObserver gør denne selv-adaptive adfærd ligetil.
Eksempel: En brugerdefineret rich text editor-komponent. Den kan vise en fuld værktøjslinje, når den har rigelig vandret plads, men automatisk skifte til en mere kompakt pop-over-menu for formateringsmuligheder, når dens container skrumper. Et andet eksempel er en brugerdefineret medieafspiller, der justerer størrelsen og placeringen af sine kontroller baseret på videoens containerstørrelse.
Responsiv typografi og billedskalering
Ud over simple viewport-baserede justeringer kan ResizeObserver muliggøre virkelig flydende typografi og billedhåndtering. Du kan dynamisk justere skriftstørrelser, linjehøjder eller billedkilder (f.eks. indlæse et billede med højere opløsning til større containere) baseret på den faktiske størrelse af tekstblokken eller billedcontaineren, snarere end blot vinduet.
Eksempel: Hovedindholdsområdet i et blogindlæg. Skriftstørrelsen på overskrifter og afsnit kunne subtilt øges eller mindskes for at optimere læsbarheden inden for den specifikke bredde af indholdskolonnen, uafhængigt af sidebaren eller footeren.
Tredjepartsindlejringer og Iframes
Iframes er notorisk svære at gøre responsive, især når deres indhold skal kommunikere sin ønskede højde til forældresiden. Selvom postMessage kan bruges, er det ofte besværligt. I simplere scenarier, hvor iframe'ens forælder skal reagere på iframe'ens eksterne størrelsesændringer (f.eks. hvis iframe'en har en dynamisk højde baseret på sit interne indhold), kan ResizeObserver underrette forældre-wrapperen.
Eksempel: Indlejring af en tredjepartsformular eller et undersøgelsesværktøj. Hvis formularen dynamisk udvides eller skjuler sektioner, kan dens indeholdende <div> på din side lytte efter disse størrelsesændringer via ResizeObserver og justere sin egen styling eller scroll-adfærd i overensstemmelse hermed.
"Container Query"-lignende adfærd i dag
Før native CSS Container Queries blev bredt understøttet, var ResizeObserver den primære måde at opnå lignende logik i JavaScript. Udviklere kunne observere et elements størrelse og derefter programmatisk anvende CSS-klasser eller ændre stilarter baseret på elementets bredde- eller højdetærskler.
Eksempel: En produktkort-komponent. Hvis dens bredde er mindre end 300px, kan den stable sit billede og tekst lodret. Hvis dens bredde er mellem 300px og 600px, kan den placere dem side om side. Over 600px kunne den vise flere detaljer. ResizeObserver giver udløseren for disse betingede stilanvendelser.
ResizeObserver vs. andre DOM-observationsteknikker
Det er afgørende at forstå, hvor ResizeObserver passer ind i økosystemet af DOM-API'er. Det supplerer snarere end erstatter andre observationsteknikker.
window.resize: Stadig relevant for globale layouts
Som diskuteret er window.resize nyttig for ændringer, der påvirker hele viewporten, såsom omarrangering af store layoutblokke (f.eks. at flytte en sidebar til bunden på mobil). Det er dog ineffektivt og utilstrækkeligt til justeringer på komponentniveau. Brug window.resize, når du skal reagere på den samlede browservinduesstørrelse; brug ResizeObserver for specifikke elementdimensioner.
MutationObserver: Til ændringer i DOM-struktur og attributter
MutationObserver er designet til at observere ændringer i selve DOM-træet, såsom tilføjelse/fjernelse af noder, ændringer i tekstindhold eller attributmodifikationer. Det rapporterer ikke direkte ændringer i elementstørrelse. Selvom en ændring i DOM-strukturen indirekte kan få et element til at ændre størrelse, vil MutationObserver ikke fortælle dig de nye dimensioner direkte; du bliver nødt til selv at beregne dem efter mutationen. Til eksplicit størrelsessporing er ResizeObserver det korrekte værktøj.
Polling (setInterval): Et anti-mønster til størrelsessporing
Før ResizeObserver var en almindelig, men ineffektiv metode at gentagne gange tjekke et elements offsetWidth eller offsetHeight ved hjælp af setInterval. Dette er generelt et anti-mønster, fordi:
- Det bruger unødvendigt CPU-cykler, selv når der ikke er sket nogen størrelsesændring.
- Polling-intervallet er en afvejning: for hyppigt, og det er en performance-synder; for sjældent, og UI'en reagerer trægt.
- Det udnytter ikke browserens optimerede renderingspipeline for layoutændringer.
ResizeObserver tilbyder et deklarativt, performant og browser-optimeret alternativ.
element.getBoundingClientRect() / element.offsetWidth: Statiske målinger
Metoder som getBoundingClientRect(), offsetWidth og offsetHeight giver øjeblikkelige, statiske målinger af et elements størrelse og position i det øjeblik, de kaldes. De er nyttige til engangsmålinger, men tilbyder ingen reaktivitet. Du ville skulle kalde dem gentagne gange (f.eks. inde i en window.resize-handler eller en polling-løkke) for at opdage ændringer, hvilket bringer os tilbage til de ineffektiviteter, som ResizeObserver løser.
Bedste praksis og avancerede overvejelser
Selvom det er kraftfuldt, kræver effektiv brug af ResizeObserver en forståelse af dets nuancer og potentielle faldgruber.
Undgåelse af ResizeObserverLoopError
En almindelig fejl, når man først bruger ResizeObserver, er direkte at ændre layout-egenskaber (f.eks. width, height, padding, margins) for et observeret element inden i sin egen callback-funktion. Dette kan føre til en uendelig løkke: en størrelsesændring registreres, callback'en ændrer elementets størrelse, hvilket udløser en ny størrelsesændring, og så videre. Browseren vil til sidst kaste en ResizeObserverLoopError for at forhindre siden i at blive ureagerende.
Løsning: Udskyd layoutændringer med requestAnimationFrame.
For sikkert at ændre det observerede elements layout, skal du udskyde disse ændringer til næste animationsramme. Dette giver browseren mulighed for at fuldføre den nuværende layout-gennemgang, før du introducerer nye ændringer, der kan udløse en ny størrelsesændring.
const saferObserver = new ResizeObserver(entries => {
for (let entry of entries) {
// Sørg for, at vi ikke direkte ændrer det observerede elements størrelse her
// Hvis vi har brug for det, skal vi udskyde det.
const target = entry.target;
const newWidth = entry.contentRect.width;
// Eksempel: Hvis vi justerede skriftstørrelsen på målet baseret på dets bredde
// DÅRLIGT: target.style.fontSize = `${newWidth / 20}px`; // Kan forårsage en løkke
// GODT: Udskyd stilændringen
requestAnimationFrame(() => {
// Anvend kun ændringer, hvis elementet stadig er forbundet til DOM'en
// (vigtigt, hvis elementer kan fjernes under en animationsramme)
if (document.body.contains(target)) {
target.style.fontSize = `${newWidth / 20}px`;
console.log(`Justeret skriftstørrelse for ${target.id || target.tagName} til ${target.style.fontSize}.`);
}
});
}
});
const fontResizer = document.getElementById('fontResizerDiv');
if (fontResizer) {
saferObserver.observe(fontResizer);
}
Det er vigtigt at bemærke, at denne fejl typisk opstår, når du ændrer det observerede element selv. At ændre et barnelement eller et urelateret element inden i callback'en er generelt sikkert, da det ikke vil udløse et nyt resize-event på det oprindeligt observerede element.
Performance-implikationer
ResizeObserver er designet til at være yderst performant. Browseren samler resize-notifikationer, hvilket betyder, at callback'en kun kaldes én gang pr. frame, selvom flere observerede elementer ændrer størrelse, eller et enkelt element ændrer størrelse flere gange inden for den frame. Denne indbyggede throttling forhindrer overdreven eksekvering af callback'en.
Du bør dog stadig være opmærksom på det arbejde, der udføres inde i din callback:
- Dyre beregninger: Undgå tunge DOM-manipulationer eller komplekse beregninger inden i callback'en, hvis de ikke er strengt nødvendige.
- Mange observers: Selvom det er effektivt, kan observation af et meget stort antal elementer (f.eks. hundreder eller tusinder) stadig have en performance-omkostning, især hvis hver callback udfører betydeligt arbejde.
- Tidlige exits: Hvis en størrelsesændring ikke berettiger en handling, skal du tilføje en tidlig exit-betingelse i din callback.
For handlinger, der er beregningsmæssigt dyre og ikke behøver at ske ved hver eneste resize-event (f.eks. netværksanmodninger, komplekse gentegninger), bør du overveje at debounc'e eller throttle de handlinger, der udløses af ResizeObserver-callback'en, snarere end selve callback'en. For de fleste UI-opdateringer er den indbyggede throttling dog tilstrækkelig.
Tilgængelighedsovervejelser
Når du implementerer dynamiske layouts med ResizeObserver, skal du altid overveje indvirkningen på tilgængelighed. Sørg for, at layoutændringer:
- Er forudsigelige: Undgå pludselige, desorienterende skift i indhold uden brugerinitiering eller klar kontekst.
- Opretholder læsbarhed: Tekst skal forblive læsbar, og interaktive elementer skal forblive tilgængelige, uanset containerens størrelse.
- Understøtter tastaturnavigation: Responsive ændringer bør ikke bryde tastaturfokus-rækkefølgen eller gøre elementer utilgængelige.
- Tilbyder alternativer: For kritisk information eller funktionalitet skal du sikre, at der er alternative måder at tilgå det på, hvis dynamisk størrelsesændring får det til at blive skjult eller mindre fremtrædende.
Browser-support og polyfills
ResizeObserver har fremragende browser-support på tværs af alle moderne browsere, herunder Chrome, Firefox, Edge, Safari og Opera. Dette gør det til et pålideligt valg for nutidig webudvikling.
For projekter, der kræver kompatibilitet med ældre browsere (f.eks. Internet Explorer), kan en polyfill bruges. Biblioteker som resize-observer-polyfill kan levere den nødvendige funktionalitet, så du kan bruge API'et konsekvent på tværs af et bredere udvalg af miljøer.
Du kan tjekke den seneste kompatibilitetsstatus på Can I use... ResizeObserver.
Arbejde med CSS-layouts (Flexbox, Grid, calc())
ResizeObserver fungerer problemfrit med moderne CSS-layoutteknikker som Flexbox og Grid. Når et elements størrelse ændres på grund af dets forælders flex- eller grid-layoutregler, vil ResizeObserver korrekt udløse sin callback. Denne integration er kraftfuld:
- CSS håndterer den primære layoutlogik (f.eks. elementer, der fordeler plads).
- JavaScript (via ResizeObserver) håndterer eventuelle sekundære, indholdsspecifikke justeringer, som CSS alene ikke kan klare (f.eks. gentegning af et diagram, dynamisk justering af brugerdefinerede scrollbar-størrelser).
På samme måde vil elementer, hvis størrelser er defineret ved hjælp af CSS-funktioner som calc() eller relative enheder (em, rem, vw, vh, %), også udløse ResizeObserver, når deres beregnede pixeldimensioner ændres. Dette sikrer, at API'et er reaktivt over for stort set enhver mekanisme, der påvirker et elements renderede størrelse.
Et trin-for-trin-eksempel: Oprettelse af et selvjusterende tekstområde
Lad os gennemgå et praktisk eksempel: et tekstområde, der automatisk justerer sin højde for at passe til sit indhold, og som yderligere reagerer, hvis dets forældrecontainer ændres i størrelse.
Målet er at skabe et <textarea>, der udvides lodret, efterhånden som mere indhold skrives i det, men som også sikrer, at dets indeholdende <div> kan påvirke dets maksimale tilgængelige højde, hvis containeren selv ændrer størrelse.
HTML-struktur
Vi vil opsætte en simpel HTML-struktur med en forældrecontainer og et tekstområde indeni.
<div class="container" id="textContainer">
<h3>Justerbart indholdsområde</h3>
<p>Skriv her og se tekstområdet justere sig.</p>
<textarea id="autoResizeTextarea" placeholder="Begynd at skrive..."></textarea>
<div class="resize-handle"></div>
</div>
CSS-styling
Lidt CSS for at gøre det visuelt klart og tillade, at containeren kan ændres manuelt i størrelse (til demonstration).
.container {
width: 100%;
max-width: 600px;
min-width: 300px;
min-height: 200px;
background-color: #f0f0f0;
border: 1px solid #ccc;
padding: 15px;
margin: 20px auto;
position: relative;
/* Tillad manuel størrelsesændring til demoformål */
resize: both;
overflow: auto;
box-shadow: 0 4px 8px rgba(0,0,0,0.1);
border-radius: 8px;
}
.container h3 {
color: #333;
margin-top: 0;
margin-bottom: 10px;
}
.container p {
color: #666;
font-size: 0.9em;
margin-bottom: 15px;
}
#autoResizeTextarea {
width: 100%;
min-height: 50px;
box-sizing: border-box; /* Inkluder padding/border i bredde/højde */
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1em;
line-height: 1.5;
font-family: sans-serif;
overflow-y: hidden; /* Skjul scrollbar, vi styrer højden */
resize: none; /* Deaktiver standard textarea-resize-håndtag */
}
JavaScript-implementering
Lad os nu tilføje JavaScript for at få tekstområdet til at ændre størrelse dynamisk.
document.addEventListener('DOMContentLoaded', () => {
const textarea = document.getElementById('autoResizeTextarea');
const container = document.getElementById('textContainer');
if (!textarea || !container) {
console.error('Nødvendige elementer blev ikke fundet. Tjek HTML-ID\'er.');
return;
}
// Funktion til at justere tekstområdets højde baseret på indhold
const adjustTextareaHeight = () => {
// Nulstil højden for at beregne scrollHeight præcist
textarea.style.height = 'auto';
// Sæt højden til scrollHeight for at sikre, at den passer til indholdet
textarea.style.height = `${textarea.scrollHeight}px`;
// VALGFRIT: Begræns tekstområdets højde til dets forældrecontainers indholdshøjde
// Dette forhindrer tekstområdet i at vokse ud over det synlige containerområde.
const containerContentHeight = container.clientHeight -
(parseFloat(getComputedStyle(container).paddingTop) || 0) -
(parseFloat(getComputedStyle(container).paddingBottom) || 0);
const currentTextareaHeight = textarea.scrollHeight;
const spaceAboveTextarea = textarea.offsetTop - container.offsetTop;
const maxHeightAllowed = containerContentHeight - spaceAboveTextarea;
if (currentTextareaHeight > maxHeightAllowed && maxHeightAllowed > 0) {
textarea.style.height = `${maxHeightAllowed}px`;
textarea.style.overflowY = 'auto'; // Genaktiver scroll, hvis den er begrænset
} else {
textarea.style.overflowY = 'hidden'; // Skjul scroll, hvis indholdet passer
}
};
// 1. Lyt efter input-events på tekstområdet for at justere højden, mens brugeren skriver
textarea.addEventListener('input', adjustTextareaHeight);
// 2. Brug ResizeObserver til at reagere på containerens størrelsesændringer
const containerResizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
if (entry.target === container) {
console.log(`Container ændrede størrelse til: ${entry.contentRect.width}px x ${entry.contentRect.height}px`);
// Når containeren ændrer størrelse, skal vi gen-evaluere tekstområdets højde,
// især hvis den var begrænset af forælderens højde.
// Udskyd dette for at undgå ResizeObserverLoopError, hvis containerens børn påvirker dens størrelse.
requestAnimationFrame(() => {
if (document.body.contains(container)) {
adjustTextareaHeight();
}
});
}
}
});
// Start observation af containeren
containerResizeObserver.observe(container);
// Indledende justering, når siden indlæses
adjustTextareaHeight();
});
I dette eksempel:
- Vi har et
textarea, der udvider sin højde baseret på sinscrollHeight, når brugeren skriver. - En
ResizeObserverer tilknyttet forældrecontaineren (#textContainer). - Når containeren manuelt ændres i størrelse (ved hjælp af CSS-egenskaben
resize: both;), udløses observerens callback. - Inde i callback'en genkører vi
adjustTextareaHeight(). Dette sikrer, at hvis containeren skrumper, bliver tekstområdets højdebegrænsning gen-evalueret, hvilket potentielt aktiverer dens scrollbar, hvis indholdet ikke længere passer. - Vi bruger
requestAnimationFrametil kaldet afadjustTextareaHeight()inde i observerens callback for at forhindre potentielResizeObserverLoopError, især hvis tekstområdets størrelse på en eller anden måde påvirkede containerens størrelse i et mere komplekst layout.
Dette demonstrerer, hvordan ResizeObserver gør det muligt for en komponent (tekstområdet) at være virkelig responsiv, ikke kun over for sit eget indhold, men også over for den dynamiske plads, som dens forælder stiller til rådighed, hvilket skaber en flydende og brugervenlig oplevelse.
Fremtiden: ResizeObserver og native Container Queries
Med fremkomsten af native CSS Container Queries (f.eks. @container-regler), som er ved at opnå bred browser-support, opstår et almindeligt spørgsmål: Har ResizeObserver stadig en rolle?
Svaret er et rungende ja.
- Container Queries: Fokuserer primært på CSS-drevet styling baseret på en forældrecontainers størrelse. De giver dig mulighed for at anvende stilarter (som at ændre
display,font-size,grid-template-columns) direkte i CSS-regler uden JavaScript. Dette er ideelt til rent præsentations- og layoutrelateret responsivitet. - ResizeObserver: Excels, når du har brug for JavaScript til at reagere på størrelsesændringer. Dette inkluderer:
- Programmatisk gentegning af et canvas (f.eks. diagrammer, spil).
- Justering af kompleks JavaScript-drevet UI-logik (f.eks. gen-initialisering af et tredjepartsbibliotek, beregning af nye positioner for trækbare elementer).
- Interaktion med andre JavaScript-API'er baseret på størrelse (f.eks. dynamisk indlæsning af forskellige billedstørrelser, styring af videoafspilning).
- Når du har brug for præcise pixeldimensioner til beregninger, som CSS alene ikke kan levere eller udføre effektivt.
I bund og grund håndterer Container Queries den deklarative styling, mens ResizeObserver håndterer den imperative, programmatiske logik. De er komplementære værktøjer, der tilsammen skaber det ultimative værktøjssæt til virkelig responsive webapplikationer.
Konklusion
ResizeObserver API'et er et uundværligt værktøj for moderne webudviklere, der stræber efter at bygge virkelig dynamiske og responsive brugergrænseflader. Ved at levere en effektiv, event-drevet mekanisme til at observere størrelsesændringer af ethvert DOM-element, bevæger det os ud over begrænsningerne ved viewport-centreret responsivitet og ind i en verden af robust, komponentniveau-tilpasningsevne.
Fra at få datavisualiseringer til problemfrit at tilpasse sig deres containere til at muliggøre selvbevidste UI-komponenter, giver ResizeObserver dig mulighed for at skabe mere robuste, performante og brugervenlige weboplevelser. Omfavn dette kraftfulde API for at løfte din front-end-udvikling, skabe layouts, der elegant tilpasser sig enhver kontekst, og levere exceptionelle digitale produkter til et globalt publikum.
Begynd at integrere ResizeObserver i dine projekter i dag og lås op for et nyt niveau af kontrol over dine responsive webdesigns!