网站在苹果 Safari 进行适配遇到的问题

在网站进行移动端 Web 适配开发中,弹窗和导航栏弹出等常常会出现一些问题,如果是奇奇怪怪的客户严格要求的话,那么就会有下面这些情况:

  • 打开弹窗后页面自动放大,视图区被放大到看不全
  • 打开对话框打开后背景仍然能滚动
  • 导航栏弹窗后,背后内容可滚动,影响体验
  • 点击聚焦输入框,导致视图放大

下面通过 Nuxtjs3 示例,分析问题原因并提供一些解决方案,有错误或者有其他想法可以评论提出!


1. 问题分析

在 iOS Safari 中,可能开发者都看到了已经对 body 进行超出隐藏了,但是就会出现穿透滑动滚动现象(查了下资料说是 iOS Safari 对这个属性无效,实际大家可以自己去看看);

弹窗中有输入框或效果切换时,浏览器会触发自动缩放行为,导致全页视图放大一丢丢,视图溢出和乱移。


2. 解决思路

视图放大问题

通过 meta viewport 可以限制浏览器的缩放操作(也就是 html 原生放在 head 上的 meta):

ts 复制代码
useHead({
  meta: [
    { name: 'viewport', content: 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no' }
  ]
})

禁滚 body & 禁滑

当弹窗或菜单打开时,通过动态修改 body CSS 来防止背景滚动以及监听触碰滑动(可单独拎出):

ts 复制代码
const preventDefault = (e: Event) => {
  e.preventDefault()
}

const disableBodyScroll = () => {
  document.body.style.overflow = 'hidden';
  document.body.style.position = 'fixed';
  document.body.style.width = '100%';
  document.body.style.top = `-${window.scrollY}px`;
  document.addEventListener('touchmove', preventDefault, { passive: false })
};

const enableBodyScroll = () => {
  const scrollY = document.body.style.top;
  document.body.style.overflow = '';
  document.body.style.position = '';
  document.body.style.width = '';
  document.body.style.top = '';
  if (scrollY) {
    window.scrollTo(0, parseInt(scrollY || '0') * -1);
  }
  document.removeEventListener('touchmove', preventDefault)
};

通过记录当前滚动位置,关闭弹窗后恢复滚动状态,可以防止页面上下移动。

注:这里是自定义的弹窗组件,如果禁滑的话要在对应弹窗内部加上单防穿透(@touchmove.stop),避免内部超出不可触摸滚动,原理如下(可以看看官方属性进行理解):

当用户在页面上滑动时:

触摸点

↓ 触发 touchmove

某个 div

↓ 冒泡

父元素

↓ 冒泡

body

↓ 冒泡

html

↓ 冒泡

document ← 在这里被拦截并 preventDefault()

↓ 冒泡

window

当弹窗内部滑动时:

弹窗内的滚动区域

↓ 触发 touchmove

↓ 遇到 @touchmove.stop

停止冒泡(stopPropagation)

不会到达 document,因此不会被 preventDefault

保留默认滚动行为

Vue 中通过 watch 监听

ts 复制代码
watch(visible, (newVal) => {
  if (newVal) {
    disableBodyScroll();
  } else {
    enableBodyScroll();
  }
});

当 visible 为 true 时禁滚,其他情况恢复滚动。


3. 实现例子

客服对话窗 (Chat Window)

vue 复制代码
watch(visible, (newVal) => {
  if (newVal) {
    minimized.value = false;
    loadMessagesFromStorage();
    disableBodyScroll();
  } else {
    enableBodyScroll();
  }
});

每次弹窗打开,禁滚 body;关闭后恢复滚动。

通过 Teleport 把对话窗注入 body,保证层级级次上在最上方,避免被其他元素遮挡或发生 z-index 第一问题。

顶部导航栏 (Navbar)

移动端打开横向菜单后,同样需要禁滚 body:

ts 复制代码
const toggleMobileMenu = () => {
  mobileMenuOpen.value = !mobileMenuOpen.value;
  if (mobileMenuOpen.value) {
    disableBodyScroll();
  } else {
    enableBodyScroll();
  }
};

5. 总结

移动端弹窗和横向菜单的滚动漏洞,在经典 WebApp 中很容易被忽视。使用上述方法,可以在 Vue3 / Nuxt3 项目中简单且稳定地处理这些问题,但是并没有分开详细介绍,只给大家提供思路,因为现在 AI 较多,大家参考完进行头脑风暴比较好。

如需查看全部实现代码,可以留言,会把弹窗代码分享下。

相关推荐
kyriewen29 分钟前
2026 年了,这 6 个 npm 包可以卸载了——浏览器原生 API 已经能替代
前端·javascript·npm
Csvn3 小时前
Monorepo 迁移血泪史:从 Multi-Repo 到 Turborepo,这 3 个坑我帮你踩完了
前端
星栈3 小时前
Dioxus 多页面怎么做:`dioxus-router`、嵌套路由、`Outlet` 和页面组织,一篇给你讲顺
前端·rust·前端框架
用户987409238873 小时前
用 Remotion + edge-tts 打造中文教学视频全自动流水线
前端
风骏时光牛马3 小时前
Less前端工程化实战:变量混合器与项目样式分层落地
前端
假如让我当三天老蒯4 小时前
Options API(选项式 API) 和 Composition API(组合式 API)
前端·vue.js·面试
SameX4 小时前
iOS 独立开发实践:用 MapKit + 像素渲染实现 Citywalk 轨迹地图 App「雁过留痕」
前端
skyey4 小时前
页面加载时,深色模式闪白的问题解决
前端
IT_陈寒4 小时前
Java 并行流把我坑惨了,这6小时加班值了
前端·人工智能·后端
anOnion14 小时前
构建无障碍组件之Menu Button pattern
前端·html·交互设计