网站在苹果 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 较多,大家参考完进行头脑风暴比较好。

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

相关推荐
ywf12152 分钟前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭9 分钟前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf6 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特6 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
泯泷7 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
mengchanmian7 小时前
前端node常用配置
前端
华洛7 小时前
利好打工人,openclaw不是企业提效工具,而是个人助理
前端·javascript·产品经理
xkxnq8 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js
A黄俊辉A9 小时前
vue css中 :global的使用
前端·javascript·vue.js
小码哥_常9 小时前
被EdgeToEdge适配折磨疯了,谁懂!
前端