Mestre ResizeObserver API-et for å nøyaktig spore endringer i elementstørrelser og bygge robuste, responsive weblayouts. Utforsk fordelene, bruksområdene og beste praksis.
ResizeObserver API: Presisjonssporing av elementstørrelse for dynamiske, responsive layouter
I det enorme og stadig utviklende landskapet av webutvikling, er det å skape virkelig responsive og adaptive brukergrensesnitt fortsatt en overordnet utfordring. Mens mediespørringer (media queries) lenge har fungert som hjørnesteinen for å tilpasse layouter til ulike skjermstørrelser, krever den moderne weben en mer granulær tilnærming: responsivitet på komponentnivå. Det er her det kraftige ResizeObserver API-et kommer inn i bildet og revolusjonerer hvordan utviklere sporer og reagerer på endringer i et elements størrelse, uavhengig av visningsområdet.
Denne omfattende guiden vil dykke dypt inn i ResizeObserver API-et, utforske dets mekanismer, ulike bruksområder, beste praksis, og hvordan det gir utviklere mulighet til å bygge svært dynamiske og robuste webopplevelser for et globalt publikum.
Forstå kjerneproblemet: Hvorfor window.resize kommer til kort
I mange år var den primære mekanismen for å reagere på layoutendringer i nettleseren window.resize-hendelsen. Utviklere festet hendelseslyttere til vindusobjektet for å oppdage når nettleserens visningsområde endret dimensjoner. Imidlertid har denne tilnærmingen betydelige begrensninger i dagens komponentdrevne verden:
- Kun sentrert rundt visningsområdet:
window.resize-hendelsen utløses kun når selve nettleservinduet endrer størrelse. Den gir ingen informasjon om individuelle elementer i dokumentet som endrer størrelse på grunn av andre faktorer. - Begrenset omfang: En komponent kan trenge å justere sin interne layout hvis dens overordnede container krymper eller utvides, selv om den totale visningsområdestørrelsen forblir konstant. Tenk på en sidekolonne som kollapser, eller et fanepanel som avslører nytt innhold.
window.resizegir ingen innsikt i disse lokale endringene. - Ineffektiv polling: For å spore endringer på elementnivå uten
ResizeObserver, tyr utviklere ofte til ineffektive og ytelseskrevende pollemekanismer ved hjelp avsetInterval, hvor de gjentatte ganger sjekkerelement.offsetWidthellerelement.offsetHeight. Dette fører til unødvendige beregninger og potensiell hakking (jank). - Kompleks kommunikasjon mellom komponenter: Å orkestrere størrelsesendringer mellom dypt nestede eller uavhengige komponenter blir et rot uten en direkte måte for en komponent å kjenne til sin egen tildelte plass.
Tenk deg et scenario der et datavisualiseringsdiagram må endre størrelse dynamisk når dets inneholdende <div>-element justeres av en bruker, kanskje gjennom en dra-bar skillelinje. window.resize ville vært ubrukelig her. Dette er nøyaktig den typen utfordring ResizeObserver ble designet for å løse.
Introduksjon til ResizeObserver API-et
ResizeObserver API-et gir en ytelseseffektiv og virkningsfull måte å observere endringer i størrelsen på et elements innholdsboks (content box) eller rammeboks (border box). I motsetning til window.resize, som overvåker visningsområdet, fokuserer ResizeObserver på de spesifikke dimensjonene til ett eller flere målrettede DOM-elementer.
Det er et kraftig tillegg til pakken av web-API-er, som lar utviklere:
- Reagere på elementsspesifikke størrelsesendringer: Bli varslet hver gang et observert elements størrelse endres, uavhengig av om vinduet endret størrelse eller ikke. Dette inkluderer endringer forårsaket av CSS-layouter (flexbox, grid), dynamisk innholdsinjeksjon eller brukerinteraksjoner.
- Unngå uendelige løkker med størrelsesendringer: API-et er designet for å forhindre uendelige løkker som kan oppstå hvis en hendelseshåndterer for størrelsesendring direkte modifiserte det observerte elementets størrelse, noe som ville utløse en ny hendelse. ResizeObserver samler endringer og behandler dem effektivt.
- Forbedre ytelsen: Ved å tilby en deklarativ, hendelsesdrevet mekanisme, eliminerer det behovet for kostbar polling eller komplekse Intersection Observer-hacks for størrelsessporing.
- Muliggjøre ekte responsivitet på komponentnivå: Komponenter kan bli virkelig selvbevisste om sin tildelte plass, noe som fører til mer modulære, gjenbrukbare og robuste UI-elementer.
Slik fungerer ResizeObserver: Et praktisk dypdykk
Å bruke ResizeObserver API-et innebærer noen få enkle trinn: instansiere en observatør, fortelle den hvilke elementer den skal overvåke, og deretter håndtere endringene i en callback-funksjon.
Instansiering og observasjon
Først oppretter du en ny instans av ResizeObserver, og sender inn en callback-funksjon som vil bli utført hver gang et overvåket elements størrelse endres.
// Opprett en ny ResizeObserver-instans
const myObserver = new ResizeObserver(entries => {
// Denne callback-funksjonen vil bli utført når det observerte elementets størrelse endres
for (let entry of entries) {
const targetElement = entry.target;
const newWidth = entry.contentRect.width;
const newHeight = entry.contentRect.height;
console.log(`Elementet ${targetElement.id || targetElement.tagName} endret størrelse til ${newWidth}px x ${newHeight}px.`);
// Utfør handlinger basert på den nye størrelsen
}
});
Når du har en observatørinstans, kan du fortelle den hvilke DOM-elementer den skal observere ved hjelp av observe()-metoden:
// Hent elementet du vil observere
const myElement = document.getElementById('myResizableDiv');
// Start observasjon av elementet
if (myElement) {
myObserver.observe(myElement);
console.log('Observasjon startet for myResizableDiv.');
} else {
console.error('Element #myResizableDiv ikke funnet.');
}
Du kan observere flere elementer med den samme observatørinstansen:
const element1 = document.getElementById('chartContainer');
const element2 = document.querySelector('.responsive-sidebar');
if (element1) myObserver.observe(element1);
if (element2) myObserver.observe(element2);
For å slutte å observere et spesifikt element, bruk unobserve():
// Stopp observasjon av et enkelt element
if (myElement) {
myObserver.unobserve(myElement);
console.log('Observasjon stoppet for myResizableDiv.');
}
For å slutte å observere alle elementer og koble fra observatøren helt, bruk disconnect():
// Koble fra observatøren fra alle observerte elementer
myObserver.disconnect();
console.log('ResizeObserver frakoblet.');
Callback-funksjonen og ResizeObserverEntry
Callback-funksjonen som sendes til ResizeObserver mottar en array av ResizeObserverEntry-objekter. Hver oppføring (entry) tilsvarer et element hvis størrelse har endret seg siden forrige varsel.
Et ResizeObserverEntry-objekt gir avgjørende informasjon om størrelsesendringen:
target: En referanse til DOM-elementet som har endret størrelse.contentRect: EtDOMRectReadOnly-objekt som representerer størrelsen på elementets innholdsboks (området innenfor padding og border). Dette er ofte den mest brukte egenskapen for generell innholdsstørrelse.borderBoxSize: En array avResizeObserverSize-objekter. Dette gir dimensjonene til elementets rammeboks, inkludert padding og border. Nyttig når du trenger å ta hensyn til disse i layoutberegningene dine. Hvert objekt i arrayen inneholderinlineSizeogblockSize.contentBoxSize: En array avResizeObserverSize-objekter, likborderBoxSize, men representerer innholdsboksen. Dette anses som mer moderne og nøyaktig enncontentRectfor innholdsdimensjoner, spesielt i flerkolonnelayouter eller ved bruk av ulike skrivemoduser.devicePixelContentBoxSize: En array avResizeObserverSize-objekter som gir innholdsboksens dimensjoner i enhetspiksler (device pixels), nyttig for pikselperfekt gjengivelse, spesielt på skjermer med høy DPI.
La oss se på et eksempel som bruker disse egenskapene:
const detailedObserver = new ResizeObserver(entries => {
for (let entry of entries) {
console.log(`--- Størrelsesendret element: ${entry.target.id || entry.target.tagName} ---`);
// Gammel contentRect (DOMRectReadOnly)
console.log('ContentRect (gammel):');
console.log(` Bredde: ${entry.contentRect.width}px`);
console.log(` Høyde: ${entry.contentRect.height}px`);
console.log(` X: ${entry.contentRect.x}px`);
console.log(` Y: ${entry.contentRect.y}px`);
// Moderne contentBoxSize (array av ResizeObserverSize)
if (entry.contentBoxSize && entry.contentBoxSize.length > 0) {
const contentBox = entry.contentBoxSize[0];
console.log('ContentBoxSize (moderne):');
console.log(` Inline-størrelse (bredde): ${contentBox.inlineSize}px`);
console.log(` Blokk-størrelse (høyde): ${contentBox.blockSize}px`);
}
// BorderBoxSize (array av ResizeObserverSize)
if (entry.borderBoxSize && entry.borderBoxSize.length > 0) {
const borderBox = entry.borderBoxSize[0];
console.log('BorderBoxSize:');
console.log(` Inline-størrelse (bredde inkl. padding/border): ${borderBox.inlineSize}px`);
console.log(` Blokk-størrelse (høyde inkl. padding/border): ${borderBox.blockSize}px`);
}
// DevicePixelContentBoxSize (array av ResizeObserverSize)
if (entry.devicePixelContentBoxSize && entry.devicePixelContentBoxSize.length > 0) {
const devicePixelBox = entry.devicePixelContentBoxSize[0];
console.log('DevicePixelContentBoxSize:');
console.log(` Inline-størrelse (enhetspiksler): ${devicePixelBox.inlineSize}px`);
console.log(` Blokk-størrelse (enhetspiksler): ${devicePixelBox.blockSize}px`);
}
}
});
const observeMe = document.getElementById('observeThisDiv');
if (observeMe) {
detailedObserver.observe(observeMe);
}
En merknad om contentRect vs. contentBoxSize: Selv om contentRect er bredt støttet og intuitiv, er contentBoxSize og borderBoxSize nyere tillegg til spesifikasjonen. De gir en array av ResizeObserverSize-objekter fordi et element kan ha flere fragmenter hvis det er i en flerkolonnelayout. For de fleste vanlige scenarioer med ett enkelt fragment, vil du få tilgang til det første elementet i arrayen (f.eks. entry.contentBoxSize[0].inlineSize).
Reelle bruksområder for administrasjon av responsiv layout
Bruksområdene for ResizeObserver er utrolig varierte, og gir utviklere mulighet til å bygge mer fleksible og robuste brukergrensesnitt. Her er noen overbevisende reelle scenarioer:
Dynamiske diagrammer og datavisualiseringer
Diagrambiblioteker (som Chart.js, D3.js, Highcharts, etc.) trenger ofte å tegne seg selv på nytt eller justere skalaene sine når containeren endrer størrelse. Tradisjonelt innebar dette å lytte til window.resize og deretter manuelt sjekke om diagrammets forelder hadde endret seg. Med ResizeObserver kan diagrammer enkelt observere sin egen container og respondere direkte.
Eksempel: Et dashbord med flere diagrammer arrangert i et rutenett. Når en bruker endrer størrelsen på et panel eller endrer layouten, tegner hvert diagram seg automatisk på nytt for å passe perfekt til sine nye dimensjoner, uten flimring eller manuell inngripen.
Adaptive rutenettsystemer og tabeller
Responsive tabeller er notorisk vanskelige. Du vil kanskje skjule visse kolonner, konvertere en tabell til en listelignende struktur, eller justere kolonnebredder basert på tilgjengelig plass. I stedet for å stole på mediespørringer som gjelder for hele visningsområdet, lar ResizeObserver en tabellkomponent bestemme sin egen responsivitet basert på sin egen bredde.
Eksempel: En e-handels tabell med produktlister. Når containeren blir smal, kan spesifikke kolonner som "produkt-ID" eller "lagernivå" skjules, og de gjenværende kolonnene kan utvides for å fylle plassen. Hvis containeren blir veldig smal, kan tabellen til og med transformeres til en kortbasert layout.
Egendefinerte UI-komponenter og widgets
Mange webapplikasjoner har komplekse, gjenbrukbare UI-komponenter: sidekolonner, modaler, dra-bare paneler eller innebygde widgets. Disse komponentene trenger ofte å tilpasse sin interne layout basert på plassen de er tildelt av sin forelder. ResizeObserver gjør denne selvtilpassende oppførselen enkel.
Eksempel: En egendefinert riktekst-editor-komponent. Den kan vise en full verktøylinje når den har rikelig med horisontal plass, men automatisk bytte til en mer kompakt, pop-over-meny for formateringsalternativer når containeren krymper. Et annet eksempel er en egendefinert mediespiller som justerer størrelsen og posisjoneringen av kontrollene sine basert på videoens containerstørrelse.
Responsiv typografi og bildeskalering
Utover enkle visningsområde-baserte justeringer, kan ResizeObserver muliggjøre virkelig flytende typografi og bildehåndtering. Du kan dynamisk justere skriftstørrelser, linjehøyder eller bildekilder (f.eks. laste inn et bilde med høyere oppløsning for større containere) basert på den faktiske størrelsen på tekstblokken eller bildecontaineren, i stedet for bare vinduet.
Eksempel: Hovedinnholdsområdet i et blogginnlegg. Skriftstørrelsen på overskrifter og avsnitt kan subtilt øke eller reduseres for å optimalisere lesbarheten innenfor den spesifikke bredden på innholdskolonnen, uavhengig av sidekolonnen eller bunnteksten.
Tredjeparts-innbygginger og Iframes
Iframes er notorisk vanskelige å gjøre responsive, spesielt når innholdet deres trenger å kommunisere sin ønskede høyde til foreldresiden. Selv om postMessage kan brukes, er det ofte tungvint. For enklere scenarioer der iframe-ens forelder trenger å reagere på iframe-ens eksterne størrelsesendringer (f.eks. hvis iframen har en dynamisk høyde basert på sitt interne innhold), kan ResizeObserver varsle den omsluttende forelderen.
Eksempel: Innebygging av et tredjeparts skjema eller et undersøkelsesverktøy. Hvis skjemaet dynamisk utvider eller kollapser seksjoner, kan dets inneholdende <div> på siden din lytte etter disse størrelsesendringene via ResizeObserver og justere sin egen styling eller rulleatferd tilsvarende.
"Container Query"-lignende oppførsel i dag
Før native CSS Container Queries ble bredt støttet, var ResizeObserver den primære måten å oppnå lignende logikk i JavaScript. Utviklere kunne observere et elements størrelse og deretter programmatisk anvende CSS-klasser eller modifisere stiler basert på elementets bredde- eller høydeterskler.
Eksempel: En produktkort-komponent. Hvis bredden er mindre enn 300px, kan den stable bildet og teksten vertikalt. Hvis bredden er mellom 300px og 600px, kan den plassere dem side om side. Over 600px kan den vise flere detaljer. ResizeObserver gir utløseren for disse betingede stilapplikasjonene.
ResizeObserver vs. andre DOM-observasjonsteknikker
Å forstå hvor ResizeObserver passer inn i økosystemet av DOM API-er er avgjørende. Det komplementerer, snarere enn erstatter, andre observasjonsteknikker.
window.resize: Fortsatt relevant for globale layouter
Som diskutert, er window.resize nyttig for endringer som påvirker hele visningsområdet, som for eksempel å omorganisere store layoutblokker (f.eks. flytte en sidekolonne til bunnen på mobil). Det er imidlertid ineffektivt og utilstrekkelig for justeringer på komponentnivå. Bruk window.resize når du trenger å reagere på den totale størrelsen på nettleservinduet; bruk ResizeObserver for spesifikke elementdimensjoner.
MutationObserver: For DOM-struktur og attributtendringer
MutationObserver er designet for å observere endringer i selve DOM-treet, som for eksempel node-tilføyelser/-fjerninger, endringer i tekstinnhold eller attributtmodifikasjoner. Det rapporterer ikke direkte om endringer i elementstørrelse. Selv om en endring i DOM-strukturen indirekte kan føre til at et element endrer størrelse, vil MutationObserver ikke fortelle deg de nye dimensjonene direkte; du må beregne dem selv etter mutasjonen. For eksplisitt størrelsessporing er ResizeObserver det riktige verktøyet.
Polling (setInterval): Et anti-mønster for størrelsessporing
Før ResizeObserver var en vanlig, men ineffektiv metode å gjentatte ganger sjekke et elements offsetWidth eller offsetHeight ved hjelp av setInterval. Dette er generelt et anti-mønster fordi:
- Det bruker unødvendig CPU-sykluser, selv når ingen størrelsesendring har skjedd.
- Pollingsintervallet er en avveining: for hyppig, og det er en ytelsestyv; for sjelden, og UI-en reagerer tregt.
- Det utnytter ikke nettleserens optimaliserte gjengivelsespipeline for layoutendringer.
ResizeObserver tilbyr et deklarativt, ytelseseffektivt og nettleseroptimalisert alternativ.
element.getBoundingClientRect() / element.offsetWidth: Statiske målinger
Metoder som getBoundingClientRect(), offsetWidth og offsetHeight gir umiddelbare, statiske målinger av et elements størrelse og posisjon i øyeblikket de kalles. De er nyttige for engangsmålinger, men tilbyr ingen reaktivitet. Du må kalle dem gjentatte ganger (f.eks. inne i en window.resize-håndterer eller en polle-løkke) for å oppdage endringer, noe som bringer oss tilbake til ineffektiviteten ResizeObserver løser.
Beste praksis og avanserte betraktninger
Selv om det er kraftig, krever effektiv bruk av ResizeObserver en forståelse av dets nyanser og potensielle fallgruver.
Unngå ResizeObserverLoopError
En vanlig feil når man først bruker ResizeObserver er å direkte modifisere layout-egenskapene (f.eks. bredde, høyde, padding, marginer) til et observert element innenfor sin egen callback-funksjon. Dette kan føre til en uendelig løkke: en størrelsesendring oppdages, callback-en modifiserer elementets størrelse, som utløser en ny størrelsesendring, og så videre. Nettleseren vil til slutt kaste en ResizeObserverLoopError for å forhindre at siden blir uresponsiv.
Løsning: Utsett layoutendringer med requestAnimationFrame.
For å trygt modifisere det observerte elementets layout, utsett disse endringene til neste animasjonsramme. Dette lar nettleseren fullføre den nåværende layout-passeringen før du introduserer nye endringer som kan utløse en ny størrelsesendring.
const saferObserver = new ResizeObserver(entries => {
for (let entry of entries) {
// Sørg for at vi ikke direkte modifiserer det observerte elementets størrelse her
// Hvis vi må, må vi utsette det.
const target = entry.target;
const newWidth = entry.contentRect.width;
// Eksempel: Hvis vi justerte skriftstørrelsen til målet basert på bredden
// DÅRLIG: target.style.fontSize = `${newWidth / 20}px`; // Kan forårsake en løkke
// BRA: Utsett stilendringen
requestAnimationFrame(() => {
// Bruk bare endringer hvis elementet fortsatt er koblet til DOM-en
// (viktig hvis elementer kan fjernes under en animasjonsramme)
if (document.body.contains(target)) {
target.style.fontSize = `${newWidth / 20}px`;
console.log(`Justerte skriftstørrelsen for ${target.id || target.tagName} til ${target.style.fontSize}.`);
}
});
}
});
const fontResizer = document.getElementById('fontResizerDiv');
if (fontResizer) {
saferObserver.observe(fontResizer);
}
Det er viktig å merke seg at denne feilen vanligvis oppstår når du modifiserer det observerte elementet selv. Å modifisere et barneelement eller et urelatert element innenfor callback-en er generelt trygt, da det ikke vil utløse en ny størrelsesendringshendelse på det opprinnelig observerte elementet.
Ytelsesimplikasjoner
ResizeObserver er designet for å være svært ytelseseffektiv. Nettleseren samler varsler om størrelsesendringer, noe som betyr at callback-en bare påkalles én gang per ramme, selv om flere observerte elementer endrer størrelse eller ett enkelt element endrer størrelse flere ganger innenfor den rammen. Denne innebygde strupingen (throttling) forhindrer overdreven utførelse av callback-funksjonen.
Du bør imidlertid fortsatt være oppmerksom på arbeidet som gjøres inne i callback-en din:
- Kostbare beregninger: Unngå tunge DOM-manipulasjoner eller komplekse beregninger inne i callback-en hvis de ikke er strengt nødvendige.
- Mange observatører: Selv om det er effektivt, kan det å observere et veldig stort antall elementer (f.eks. hundrevis eller tusenvis) fortsatt ha en ytelsesmessig overhead, spesielt hvis hver callback utfører betydelig arbeid.
- Tidlige avslutninger: Hvis en størrelsesendring ikke krever en handling, legg til en betingelse for tidlig avslutning i callback-en din.
For handlinger som er beregningsmessig kostbare og ikke trenger å skje ved hver eneste størrelsesendring (f.eks. nettverksforespørsler, komplekse omtegninger), bør du vurdere å bruke debouncing eller throttling på handlingene som utløses av ResizeObserver-callback-en, i stedet for selve callback-en. For de fleste UI-oppdateringer er imidlertid den innebygde strupingen tilstrekkelig.
Tilgjengelighetshensyn
Når du implementerer dynamiske layouter med ResizeObserver, må du alltid vurdere innvirkningen på tilgjengelighet. Sørg for at layoutendringer:
- Er forutsigbare: Unngå plutselige, desorienterende endringer i innhold uten brukerinitiering eller klar kontekst.
- Opprettholder lesbarhet: Tekst skal forbli leselig, og interaktive elementer skal forbli tilgjengelige, uavhengig av containerstørrelsen.
- Støtter tastaturnavigasjon: Responsive endringer bør ikke ødelegge tastaturfokusrekkefølgen eller gjøre elementer utilgjengelige.
- Gir alternativer: For kritisk informasjon eller funksjonalitet, sørg for at det finnes alternative måter å få tilgang til den på hvis dynamisk størrelsesendring fører til at den blir skjult eller mindre fremtredende.
Nettleserstøtte og polyfills
ResizeObserver har utmerket nettleserstøtte på tvers av alle moderne nettlesere, inkludert Chrome, Firefox, Edge, Safari og Opera. Dette gjør det til et pålitelig valg for moderne webutvikling.
For prosjekter som krever kompatibilitet med eldre nettlesere (f.eks. Internet Explorer), kan en polyfill brukes. Biblioteker som resize-observer-polyfill kan gi den nødvendige funksjonaliteten, slik at du kan bruke API-et konsekvent på tvers av et bredere spekter av miljøer.
Du kan sjekke den nyeste kompatibilitetsstatusen på Can I use... ResizeObserver.
Arbeid med CSS-layouter (Flexbox, Grid, calc())
ResizeObserver fungerer sømløst med moderne CSS-layoutteknikker som Flexbox og Grid. Når et elements størrelse endres på grunn av forelderens flex- eller grid-layoutregler, vil ResizeObserver korrekt utløse sin callback. Denne integrasjonen er kraftig:
- CSS håndterer den primære layoutlogikken (f.eks. elementer som fordeler plass).
- JavaScript (via ResizeObserver) håndterer eventuelle sekundære, innholdsspesifikke justeringer som CSS alene ikke kan håndtere (f.eks. omtegning av et diagram, dynamisk justering av egendefinerte rullefeltstørrelser).
På samme måte vil elementer hvis størrelser er definert ved hjelp av CSS-funksjoner som calc() eller relative enheter (em, rem, vw, vh, %) også utløse ResizeObserver når deres beregnede pikseldimensjoner endres. Dette sikrer at API-et er reaktivt til praktisk talt enhver mekanisme som påvirker et elements gjengitte størrelse.
Et trinn-for-trinn-eksempel: Lage et selvjusterende tekstområde
La oss gå gjennom et praktisk eksempel: et tekstområde som automatisk justerer høyden for å passe til innholdet, og som videre reagerer hvis den overordnede containeren endrer størrelse.
Målet er å lage en <textarea> som utvides vertikalt etter hvert som mer innhold skrives inn, men som også sikrer at dens inneholdende <div> kan påvirke dens maksimale tilgjengelige høyde hvis selve containeren endrer størrelse.
HTML-struktur
Vi setter opp en enkel HTML-struktur med en overordnet container og en textarea inni.
<div class="container" id="textContainer">
<h3>Justerbart innholdsområde</h3>
<p>Skriv her og se tekstområdet justere seg.</p>
<textarea id="autoResizeTextarea" placeholder="Start å skrive..."></textarea>
<div class="resize-handle"></div>
</div>
CSS-styling
Litt CSS for å gjøre det visuelt tydelig og tillate at containeren kan endres manuelt (for demonstrasjon).
.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;
/* Tillat manuell størrelsesendring for 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øyde */
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1em;
line-height: 1.5;
font-family: sans-serif;
overflow-y: hidden; /* Skjul rullefelt, vi styrer høyden */
resize: none; /* Deaktiver standard textarea-håndtak for størrelsesendring */
}
JavaScript-implementering
La oss nå legge til JavaScript for å gjøre tekstområdet dynamisk justerbart.
document.addEventListener('DOMContentLoaded', () => {
const textarea = document.getElementById('autoResizeTextarea');
const container = document.getElementById('textContainer');
if (!textarea || !container) {
console.error('Nødvendige elementer ikke funnet. Sjekk HTML-IDer.');
return;
}
// Funksjon for å justere tekstområdets høyde basert på innhold
const adjustTextareaHeight = () => {
// Tilbakestill høyden for å beregne scrollHeight nøyaktig
textarea.style.height = 'auto';
// Sett høyden til scrollHeight for å sikre at innholdet passer
textarea.style.height = `${textarea.scrollHeight}px`;
// VALGFRITT: Begrens tekstområdets høyde til den overordnede containerens innholdshøyde
// Dette forhindrer at tekstområdet vokser utover det synlige containerområdet.
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'; // Aktiver rulling igjen hvis begrenset
} else {
textarea.style.overflowY = 'hidden'; // Skjul rulling hvis innholdet passer
}
};
// 1. Lytt etter input-hendelser på tekstområdet for å justere høyden mens brukeren skriver
textarea.addEventListener('input', adjustTextareaHeight);
// 2. Bruk ResizeObserver for å reagere på containerens størrelsesendringer
const containerResizeObserver = new ResizeObserver(entries => {
for (let entry of entries) {
if (entry.target === container) {
console.log(`Container endret størrelse til: ${entry.contentRect.width}px x ${entry.contentRect.height}px`);
// Når containeren endrer størrelse, må vi re-evaluere tekstområdets høyde
// spesielt hvis den var begrenset av forelderens høyde.
// Utsett dette for å unngå ResizeObserverLoopError hvis containerens barn påvirker dens størrelse.
requestAnimationFrame(() => {
if (document.body.contains(container)) {
adjustTextareaHeight();
}
});
}
}
});
// Start observasjon av containeren
containerResizeObserver.observe(container);
// Innledende justering når siden lastes
adjustTextareaHeight();
});
I dette eksempelet:
- Vi har en
textareasom utvider høyden basert på sinscrollHeightnår brukeren skriver. - En
ResizeObserverer festet til den overordnede containeren (#textContainer). - Når containeren endres manuelt (ved hjelp av CSS-egenskapen
resize: both;), utløses observatørens callback. - Inne i callback-en kjører vi
adjustTextareaHeight()på nytt. Dette sikrer at hvis containeren krymper, blir tekstområdets høydebegrensning re-evaluert, og potensielt aktiverer rullefeltet hvis innholdet ikke lenger passer. - Vi bruker
requestAnimationFramefor kallet tiladjustTextareaHeight()inne i observatørens callback for å forhindre potensiellResizeObserverLoopError, spesielt hvis tekstområdets størrelse på en eller annen måte påvirket containerens størrelse i en mer kompleks layout.
Dette demonstrerer hvordan ResizeObserver gjør det mulig for en komponent (tekstområdet) å være virkelig responsiv, ikke bare til sitt eget innhold, men også til den dynamiske plassen som tilbys av sin forelder, og skaper en flytende og brukervennlig opplevelse.
Fremtiden: ResizeObserver og native Container Queries
Med fremveksten av native CSS Container Queries (f.eks. @container-regler), som får utbredt nettleserstøtte, oppstår et vanlig spørsmål: Har ResizeObserver fortsatt en rolle?
Svaret er et rungende ja.
- Container Queries: Fokuserer primært på CSS-drevet styling basert på en overordnet containers størrelse. De lar deg anvende stiler (som å endre
display,font-size,grid-template-columns) direkte i CSS-regler uten JavaScript. Dette er ideelt for rent presentasjons- og layoutrelatert responsivitet. - ResizeObserver: Utmerker seg når du trenger JavaScript for å reagere på størrelsesendringer. Dette inkluderer:
- Programmatisk omtegning av et canvas (f.eks. diagrammer, spill).
- Justering av kompleks JavaScript-drevet UI-logikk (f.eks. re-initialisering av et tredjepartsbibliotek, beregning av nye posisjoner for dra-bare elementer).
- Interaksjon med andre JavaScript API-er basert på størrelse (f.eks. dynamisk lasting av forskjellige bildestørrelser, kontroll av videoavspilling).
- Når du trenger presise pikseldimensjoner for beregninger som CSS alene ikke kan tilby eller utføre effektivt.
I hovedsak håndterer Container Queries den deklarative stylingen, mens ResizeObserver håndterer den imperative, programmatiske logikken. De er komplementære verktøy som sammen skaper det ultimate verktøysettet for virkelig responsive webapplikasjoner.
Konklusjon
ResizeObserver API-et er et uunnværlig verktøy for moderne webutviklere som streber etter å bygge virkelig dynamiske og responsive brukergrensesnitt. Ved å tilby en effektiv, hendelsesdrevet mekanisme for å observere størrelsesendringene til ethvert DOM-element, flytter det oss utover begrensningene til visningsområde-sentrert responsivitet og inn i riket av robust, komponentnivå-tilpasningsevne.
Fra å få datavisualiseringer til å sømløst tilpasse seg sine containere til å muliggjøre selvbevisste UI-komponenter, gir ResizeObserver deg muligheten til å skape mer motstandsdyktige, ytelseseffektive og brukervennlige webopplevelser. Omfavn dette kraftige API-et for å heve din front-end-utvikling, skape layouter som elegant tilpasser seg enhver kontekst, og levere eksepsjonelle digitale produkter til et globalt publikum.
Begynn å integrere ResizeObserver i prosjektene dine i dag og lås opp et nytt nivå av kontroll over dine responsive webdesign!