06/02/2025
Forståelse af Rulning og Zoom i Safari
På mobile enheder, især iPhones og iPads, er touch-interaktioner kernen i brugeroplevelsen. Safari, Apples standardbrowser, fortolker en række bevægelser som rulning, zoom og panorering. Mens disse funktioner generelt forbedrer navigationen, kan der opstå situationer, hvor det er nødvendigt at begrænse eller helt deaktivere dem. Dette er især relevant for webudviklere, der ønsker at skabe specifikke brugergrænseflader, såsom fuldskærmsmodaler, hamburgermenuer eller interaktive elementer, hvor baggrundsinformationen ikke skal kunne tilgås ved et uheld under interaktionen med et specifikt indholdsområde.

Denne artikel vil dykke ned i, hvordan man kan styre og potentielt deaktivere rulning og zoom i Safari. Vi vil udforske både de indbyggede mekanismer i webteknologier og de metoder, der kan anvendes for at opnå den ønskede kontrol. Det er vigtigt at bemærke, at en fuldstændig deaktivering af alle touch-interaktioner kan have en negativ indvirkning på brugeroplevelsen, hvis det ikke implementeres korrekt. Målet er ofte at forhindre uønsket baggrunds-rulning, mens specifikke elementer forbliver scrollbare.
CSS-baserede Løsninger: touch-action
CSS tilbyder en kraftfuld egenskab kaldet touch-action, som giver udviklere mulighed for at styre, hvordan et element reagerer på touch-input. Denne egenskab er designet til at håndtere almindelige touch-gestus som rulning, zoom og panning.
Grundlæggende Brug af touch-action
Den mest direkte måde at begrænse rulning på er ved at sætte touch-action til none på et specifikt element. Dette vil som udgangspunkt forhindre både vandret og lodret rulning samt zoom på det pågældende element.
.no-scroll { touch-action: none; }Hvis du anvender denne klasse på et body-element, vil det potentielt forhindre al rulning på siden. Dette kan være nyttigt i visse scenarier, men det er vigtigt at være opmærksom på begrænsningerne.
Begrænsninger ved touch-action: none
Selvom touch-action: none er en effektiv løsning, har den sine begrænsninger, især i komplekse layouts:
- Underordnede Elementer med Egen Rulning: Hvis et underordnet element har sin egen definerede rulning (f.eks.
overflow: scrollelleroverflow: auto), kantouch-action: nonepå forældreelementet blive tilsidesat eller opføre sig uforudsigeligt. - Safari Kompatibilitet: Som nævnt i kildematerialet, har Safari historisk set haft visse udfordringer med fuld overholdelse af
touch-action: none, især når det kombineres med andre CSS-egenskaber i underordnede elementer. Specifikt kanoverflow-x: hiddeni et barn annullere effekten.
Specifikke værdier for touch-action
Udover none understøtter touch-action også specifikke værdier, der giver finere kontrol:
auto: Standardværdien, tillader alle standard gestus.none: Deaktiverer alle standard gestus.pan-x: Tillader kun vandret panning.pan-y: Tillader kun lodret panning.pinch-zoom: Tillader kun pinch-to-zoom gestus.
Du kan også kombinere disse, f.eks. pan-x pinch-zoom for at tillade vandret rulning og zoom, men deaktivere lodret rulning.
JavaScript-baserede Løsninger: Event Listeners
Når CSS-løsninger ikke er tilstrækkelige, eller når der kræves mere dynamisk kontrol, kommer JavaScript til undsætning. Ved at lytte til og manipulere touch-events kan vi opnå præcis den adfærd, vi ønsker.
Forhindring af Grundlæggende Rulning
Den mest basale JavaScript-metode til at forhindre rulning er at aflytte touchmove-eventet og kalde event.preventDefault(). Dette forhindrer standardhandlingen, som i dette tilfælde er rulning.
document.addEventListener('touchmove', function (e) { e.preventDefault(); });Advarsel: Som kildematerialet pointerer, kan det at anvende preventDefault() direkte på touchstart-eventet på document eller window deaktivere rulning på alle efterfølgende områder. Det er ofte bedre at være mere specifik.
Forhindring af Dokument-Rulning, men Tilladelse af Element-Rulning
Et almindeligt scenarie er at have en modal eller et sidepanel, der skal være scrollbart internt, men som ikke skal forårsage, at baggrunden ruller. Her er en mere sofistikeret tilgang:
Denne metode udnytter, at Safari bruger den første touch-bevægelse til at bestemme, om det er dokumentet eller et specifikt element, der skal rulles.
var firstMove = true; window.addEventListener('touchstart', function (e) { firstMove = true; }); window.addEventListener('touchmove', function (e) { if (firstMove) { e.preventDefault(); firstMove = false; } });Denne kode forhindrer rulning, når den første berøring efterfulgt af en bevægelse sker. Dette er en simplificeret, men ofte effektiv metode.
En Mere Robust Løsning med Element-Inspektion
For en endnu mere robust løsning, der tager højde for forskellige scrollbare elementer i hierarkiet, kan man inspicere det berørte element og dets forældre for at bestemme, om dokumentet skal rulles.

let touchTarget = null; let scrollMap = {}; let disableScroll = false; function conditionParentUntilTrue(element, condition) { let outcome; if (element === document.body) { return false; } outcome = condition(element); if (outcome) { return true; } else { return conditionParentUntilTrue(element.parentNode, condition); } } window.addEventListener('touchstart', function (e) { touchTarget = e.targetTouches[0].target; scrollMap.left = conditionParentUntilTrue(touchTarget, (el) => el.scrollLeft > 0); scrollMap.top = conditionParentUntilTrue(touchTarget, (el) => el.scrollTop > 0); scrollMap.right = conditionParentUntilTrue(touchTarget, (el) => el.scrollWidth > el.clientWidth && el.scrollWidth - el.clientWidth > el.scrollLeft); scrollMap.bottom = conditionParentUntilTrue(touchTarget, (el) => el.scrollHeight > el.clientHeight && el.scrollHeight - el.clientHeight > el.scrollTop); let touch = e.targetTouches[0]; let touchScreenX = touch.screenX; let touchScreenY = touch.screenY; disableScroll = false; // Check if the touch is on an element that should not scroll the body if (scrollMap.left || scrollMap.top || scrollMap.right || scrollMap.bottom) { // It's possible this element or its parent can scroll, let's assume no document scroll for now. } else { // This touch is likely intended for document scroll. } }); window.addEventListener('touchmove', function (e) { if (disableScroll) { e.preventDefault(); return; } let touch = e.targetTouches[0]; let moveScreenX = touch.screenX; let moveScreenY = touch.screenY; if ( (moveScreenX > touchScreenX && scrollMap.left) || (moveScreenY < touchScreenY && scrollMap.bottom) || (moveScreenX < touchScreenX && scrollMap.right) || (moveScreenY > touchScreenY && scrollMap.top) ) { // Scrolling an element or its parent, not the document. } else { // This movement is intended for the document body scroll. e.preventDefault(); disableScroll = true; } });Denne mere komplekse løsning analyserer om elementet eller dets forældre kan scrolles i en given retning. Hvis det kan, tillades bevægelsen. Hvis ikke, og bevægelsen er i en retning, der normalt ville scrolles i dokumentet, så forhindres det.
Den Anbefalede JavaScript-tilgang: Containere med Scrollbar Børn
En meget praktisk løsning, som beskrevet i kildematerialet, involverer at håndtere interaktioner mellem en forældrecontainer og scrollbare børn.
Låsning af Forældre med Scrollbart Barn
Denne funktion tilføjer en klasse til forælderen, der skjuler overflow, og tilføjer event listeners for at stoppe hhv. wheel og touchmove events på forælderen, mens stopPropagation bruges på barnet for at tillade intern rulning.
const preventDefaultScroll = (e) => e.preventDefault(); const allowScrollWithoutPropogation = (e) => e.stopPropagation(); const lockParentWithScrollingChild = (parentSelector, childSelector) => { const parentElement = document.querySelector(parentSelector); const childElement = document.querySelector(childSelector); parentElement?.classList.add('locked-scroll'); parentElement?.addEventListener('wheel', preventDefaultScroll, { passive: false }); parentElement?.addEventListener('touchmove', preventDefaultScroll, { passive: false }); childElement?.addEventListener('wheel', allowScrollWithoutPropogation, false); childElement?.addEventListener('touchmove', allowScrollWithoutPropogation, false); }; // Eksempel på brug: // lockParentWithScrollingChild('#menu-container-header', '#menu-container');Vigtigt om passive: false: Ved at sætte passive: false tillader vi, at preventDefault() kan kaldes inden for scroll-relaterede events. Uden dette ville man støde på fejlmeddelelsen "Unable to preventDefault inside passive event listener invocation.". Dette kan dog have en lille indvirkning på ydeevnen, så det bør bruges med omtanke.
Ophævelse af Låsning
Det er lige så vigtigt at kunne rydde op efter sig, når låsningen ikke længere er nødvendig. Dette inkluderer at fjerne klassen og de tilføjede event listeners.
const unlockParentWithScrollingChild = (parentSelector, childSelector) => { const parentElement = document.querySelector(parentSelector); const childElement = document.querySelector(childSelector); parentElement?.classList.remove('locked-scroll'); parentElement?.removeEventListener('wheel', preventDefaultScroll); parentElement?.removeEventListener('touchmove', preventDefaultScroll); childElement?.removeEventListener('wheel', allowScrollWithoutPropogation); childElement?.removeEventListener('touchmove', allowScrollWithoutPropogation); }; // Eksempel på brug: // unlockParentWithScrollingChild('#menu-container-header', '#menu-container');Tabel: CSS vs. JavaScript Metoder
| Metode | Fordele | Ulemper | Bedst til |
|---|---|---|---|
CSS touch-action: none | Simpel, ingen JavaScript nødvendig | Begrænsninger med underordnede elementer, potentiel Safari-inkonsistens | Simpel deaktivering af rulning på et element |
JavaScript preventDefault() på touchmove | Mere kontrol, kan anvendes dynamisk | Kan være for aggressiv, hvis ikke brugt korrekt (deaktiverer alt) | Simpel deaktivering af rulning på hele siden |
JavaScript med stopPropagation() på børn | Tillader intern rulning i specifikke elementer, robust | Kræver JavaScript, mere kompleks implementering, passive: false overvejelser | Modaler, sidepaneler, hvor kun specifikke områder skal være scrollbare |
Konklusion
At styre rulning og zoom i Safari på mobile enheder kræver en forståelse af både CSS og JavaScript. Mens touch-action giver en elegant CSS-baseret løsning til enkle tilfælde, giver JavaScript mere granulær kontrol, især når man skal håndtere interaktionen mellem scrollbare forældre og børn. Den anbefalede JavaScript-tilgang med stopPropagation er ofte den mest effektive til at skabe en problemfri brugeroplevelse, hvor specifikke dele af UI'en kan rulles, mens resten af siden forbliver låst.
Husk altid at teste din implementering grundigt på forskellige enheder og iOS-versioner for at sikre den ønskede funktionalitet og undgå ubehagelige overraskelser for brugerne. En velovervejet tilgang til at håndtere touch-interaktioner er afgørende for et vellykket mobilt webdesign.
Ofte Stillede Spørgsmål (FAQ)
Hvordan forhindrer jeg zoom i Safari på iPhone?
Du kan forhindre zoom ved at bruge touch-action: manipulation eller touch-action: none i din CSS. manipulation tillader rulning, men deaktiverer zoom. For en mere specifik kontrol kan JavaScript bruges til at aflytte gesturestart og gesturechange events og kalde event.preventDefault().
Hvorfor virker min touch-action: none ikke i Safari?
Dette kan skyldes, at et underordnet element har sin egen overflow-egenskab sat, eller at der er andre CSS-regler, der konflikter. Prøv at forenkle dit CSS eller brug JavaScript-løsningen for mere pålidelighed.
Kan jeg kun forhindre lodret rulning?
Ja, du kan bruge touch-action: pan-x til kun at tillade vandret rulning og dermed forhindre lodret rulning. Omvendt kan touch-action: pan-y bruges til kun at tillade lodret rulning.
Hvis du vil læse andre artikler, der ligner Stop rulning i Safari: En komplet guide, kan du besøge kategorien Mobil.
