移动端兼容性问题

57 阅读9分钟

1. CSS3动画卡顿

尽量使用transform,避免使用height,width,margin,padding等。可以开启GPU硬件加速,但用硬件加速的时候也要注意,因为这个也有坑,不合理使用反而会让应用越来越卡!

2. margin bottom

典型的bug,其实这个bug大家都有办法解决,直接使用padding-bottom就好了。

3. 移动端滑动穿透

弹框的内容明显长过屏幕,需要弹框内容滑动,主页页面不滑动

xml
 体验AI代码助手
 代码解读
复制代码
<script type="text/javascript">
  //解决遮罩层滚动穿透问题,分别在遮罩层弹出后和关闭前调用
  const ModalHelper = ( (bodyCls) =>{
    let scrollTop;
    return {
      afterOpen: function () {
        scrollTop = document.scrollingElement.scrollTop;
        document.body.classList.add(bodyCls);
        document.body.style.top = -scrollTop + 'px';
      },
      beforeClose: function () {
        document.body.classList.remove(bodyCls);
        // scrollTop lost after set position:fixed, restore it back.
        document.scrollingElement.scrollTop = scrollTop;
      }
    };
  })('dialog-open');
</script>

body.dialog-open {
  position: fixed;
  width: 100%;
}
复制代码

原理:弹出弹框时,使背景主页内容作为固定定位,这样页面的滑动对其就没有任何影响,关闭弹框使,移除这个固定定位的类名,回复正常

3. 经典的1px边框

一般是采用伪元素模拟的方式,原理:把原先元素的 border 去掉,然后利用 :before 或者 :after 重做 border ,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位。

css
 体验AI代码助手
 代码解读
复制代码
  .scale{
    position: relative;
    border:none;
  }
  .scale:after{
    content: '';
    position: absolute;
    bottom: 0;
    background: #000;
    width: 100%;
    height: 1px;
    transform: scaleY(0.5);
    transform-origin: 0 0;
  }
复制代码

具体几种方法见:

关于移动端开发1px边框的一些理解及解决办法

4. android,border-radius:50%不圆

具体是因为,使用了rem布局,在部分机型上出现的问题,设置具体的px数值,不用50%即可

5. android里line-height不居中

解决方案:来源互联网

  1. 把字号内外边距等设置为需求大小的2倍,使用transform进行缩放。(不适用)
  2. 把字号内外边距等设置为需求大小的2倍,使用zoom进行缩放,可以完美解决。(可以)
  3. 把line-height设置为0,使用padding值把元素撑开,说是可以解决(不适用)。

具体原因Android浏览器下line-height垂直居中为什么会偏离

6. 安卓部分版本input的placeholder偏上

input的line-height设为normal

css
 体验AI代码助手
 代码解读
复制代码
  input{
      line-height:normal;
  }
复制代码

原理看stackoverflow上的回答:

HTML5 placeholder css padding

7. ios的body设置overflow:hidden仍然可以滚动

一般在所有元素最外层再包一个大盒子.wrap

css
 体验AI代码助手
 代码解读
复制代码
  .wrap{
      position:relative;
      overflow:hidden;
  }
复制代码

stackoverflow上有好几种处理方法,可以顺便参考下:

Does overflow:hidden applied to work on iPhone Safari?

8. ios 滚动不流畅

使用了absolute布局之后,ios会发现元素内的滚动非常不流畅,滑动的手指松开后,滚动立刻停止,失去了原本的流畅滚动特性。百度一下弹性滚动的问题,发现在 webkit 中,下面的属性可以恢复弹性滚动。

css
 体验AI代码助手
 代码解读
复制代码
-webkit-overflow-scrolling: touch;
复制代码

9. ios fixed问题

用ios下当键盘弹起时fixed会失效。解决办法就是把页面滚动改为容器内滚动。

10. 点透问题

在移动端开发中,点透(click 穿透)  是常见的交互问题,通常发生在快速点击叠加元素时(如遮罩层消失后,下方元素意外触发点击)。其本质是由于移动端 click 事件存在 300ms 延迟,而 touch 事件(如 touchstart/touchend)触发更快,导致上层元素隐藏后,延迟的 click 事件落到了下层元素上。

常见场景

  1. 弹窗 / 遮罩层关闭后,下方的按钮 / 链接被意外点击。
  2. 快速点击两层重叠的可点击元素,下层元素触发点击。

解决方案

1. 避免使用 click,优先用 touch 事件

移动端优先使用 touchstart/touchend 替代 click,减少延迟导致的穿透。注意touchstart 触发过早(手指刚接触屏幕),touchend 更接近点击完成的时机,推荐使用 touchend

javascript

运行

// 替代 click 事件
element.addEventListener('touchend', (e) => {
  // 阻止事件冒泡和默认行为(避免触发上层click)
  e.stopPropagation();
  e.preventDefault();
  // 处理逻辑
  handleClick();
});

2. 给上层元素添加 pointer-events: none 延迟

当上层元素(如遮罩)隐藏时,先设置 pointer-events: none(禁止点击穿透),延迟一段时间后再真正移除元素,等待底层 click 事件过期。

css

.mask {
  /* 遮罩样式 */
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: rgba(0,0,0,0.5);
}

javascript

运行

const mask = document.querySelector('.mask');

// 关闭遮罩时
function closeMask() {
  // 先禁止点击穿透
  mask.style.pointerEvents = 'none';
  // 隐藏动画(如透明度渐变)
  mask.style.opacity = '0';
  // 延迟300ms(匹配click延迟)后彻底移除
  setTimeout(() => {
    mask.remove();
  }, 300);
}

3. 使用 fastclick 库消除 300ms 延迟

fastclick 是专门解决移动端 click 延迟的库,原理是在 touchend 时立即触发模拟的 click 事件,避免 300ms 等待,从根源减少点透可能。

使用步骤

  1. 引入库(CDN 或本地):

    html

    预览

    <script src="https://cdnjs.cloudflare.com/ajax/libs/fastclick/1.0.6/fastclick.min.js"></script>
    
  2. 初始化:

    javascript

    运行

    if ('addEventListener' in document) {
      document.addEventListener('DOMContentLoaded', () => {
        FastClick.attach(document.body); // 全局生效
      }, false);
    }
    

4. 阻止事件冒泡和默认行为

在触发上层元素的事件时,通过 e.stopPropagation() 和 e.preventDefault() 阻止事件传递到下层。

javascript

运行

// 上层元素点击事件
upperElement.addEventListener('touchend', (e) => {
  e.stopPropagation(); // 阻止冒泡到下层
  e.preventDefault();  // 阻止默认行为(如浏览器默认click)
  upperElement.style.display = 'none'; // 隐藏上层元素
});

5. 针对特殊元素(如 <a> 标签)

  • 避免使用 <a href> 直接作为下层元素,可改用 <div> 模拟链接,通过 JS 跳转。
  • 若必须用 <a>,可在点击上层元素后,临时给 <a> 添加 pointer-events: none,延迟后恢复。

最佳实践

  1. 移动端优先使用 touch 事件(touchend),配合 pointer-events 控制穿透。
  2. 若需兼容 click 事件(如 PC 端),可结合 fastclick 消除延迟。
  3. 遮罩 / 弹窗关闭时,添加适当延迟再移除元素,确保底层 click 事件失效。

11. 键盘遮挡输入框问题

核心思路

当虚拟键盘弹出时,通过监听键盘事件或元素位置变化,动态调整输入框(或其容器)的位置,使其滚动到可视区域内。

解决方案

1. 基础方案:利用浏览器自动滚动(最简单)

部分浏览器(如 Chrome、Safari)会在输入框聚焦时,自动将其滚动到可视区域。若未生效,检查是否存在以下问题:

  • 输入框的父元素是否设置了 overflow: hidden 或固定高度,导致无法滚动。
  • 页面是否使用了 position: fixed 布局,可能干扰浏览器自动调整。

修复建议:移除不必要的 overflow: hidden,或避免在滚动容器上使用固定定位。

2. 监听焦点事件,手动滚动输入框到可视区

当输入框聚焦时,强制将其滚动到视口内(利用 scrollIntoView 方法)。

html

预览

<input type="text" class="input-field" placeholder="点击输入">

<script>
  // 获取所有输入框
  const inputs = document.querySelectorAll('.input-field');
  
  inputs.forEach(input => {
    // 聚焦时触发
    input.addEventListener('focus', () => {
      // 延迟执行,确保键盘弹出后再滚动(兼容不同浏览器)
      setTimeout(() => {
        // 滚动到可视区,参数 { block: 'center' } 可调整垂直位置
        input.scrollIntoView({ block: 'center', behavior: 'smooth' });
      }, 300);
    });
  });
</script>

原理scrollIntoView 会自动调整父容器滚动位置,使元素出现在视口中。

3. 监听键盘高度变化(更精准)

通过 visualViewport 或 resize 事件感知键盘弹出(键盘弹出会导致视口高度变小),动态调整输入框位置。

html

预览

<input type="text" class="input-field" placeholder="点击输入">

<script>
  const input = document.querySelector('.input-field');
  let originalHeight = window.innerHeight; // 初始视口高度

  input.addEventListener('focus', () => {
    // 监听视口大小变化(键盘弹出会触发)
    window.addEventListener('resize', handleResize);
  });

  input.addEventListener('blur', () => {
    // 失去焦点后移除监听
    window.removeEventListener('resize', handleResize);
  });

  function handleResize() {
    const currentHeight = window.innerHeight;
    // 若视口高度变小(判断为键盘弹出)
    if (currentHeight < originalHeight) {
      const keyboardHeight = originalHeight - currentHeight;
      // 获取输入框底部到视口顶部的距离
      const inputBottom = input.getBoundingClientRect().bottom;
      // 若输入框底部超过当前视口高度(被键盘遮挡)
      if (inputBottom > currentHeight) {
        // 滚动到输入框可见位置(可根据需要调整偏移量)
        window.scrollBy({
          top: inputBottom - currentHeight + 20, // 加20px留白
          behavior: 'smooth'
        });
      }
    } else {
      // 键盘收起,恢复初始高度记录
      originalHeight = currentHeight;
    }
  }
</script>

优势:通过计算键盘高度和输入框位置,精准避免遮挡。

4. 针对固定定位元素(如底部输入框)

若输入框在 position: fixed; bottom: 0 布局中(常见于聊天界面),键盘弹出时会被完全遮挡,需动态调整 bottom 值:

css

.fixed-input {
  position: fixed;
  bottom: 0;
  left: 0;
  width: 100%;
  padding: 10px;
}

javascript

运行

const fixedInput = document.querySelector('.fixed-input');
let originalBottom = 0;

fixedInput.addEventListener('focus', () => {
  window.addEventListener('resize', adjustFixedInput);
});

fixedInput.addEventListener('blur', () => {
  window.removeEventListener('resize', adjustFixedInput);
  // 恢复初始位置
  fixedInput.style.bottom = '0';
});

function adjustFixedInput() {
  const currentHeight = window.innerHeight;
  if (currentHeight < originalHeight) {
    // 键盘高度 = 初始高度 - 当前高度
    const keyboardHeight = originalHeight - currentHeight;
    // 将输入框底部调整为键盘高度(显示在键盘上方)
    fixedInput.style.bottom = `${keyboardHeight}px`;
  }
}

补充建议

  1. 测试兼容性:不同手机浏览器(如微信浏览器、Safari、Chrome)对键盘事件的处理存在差异,需多设备测试。
  2. 避免过度滚动:滚动时保留适当留白(如 20px),提升用户体验。
  3. 结合框架特性:若使用 Vue/React,可封装成自定义指令(如 v-auto-scroll),简化代码复用。

12. 浮层上进行滑动,浮层下面的页面也跟着滚动

具体就是当浮层出现、隐藏的时候,设置相应的overflow值

13. ios日期转换NAN问题

具体就是,new Date('2020-11-12 00:00:00')在ios中会为NAN

决绝方案:用new Date('2020/11/12 00:00:00')的日期格式,或者写个正则转换

14. ios滚动时动画停止

移动端滚动懒人推荐使用[better-scroll]

15. 长按闪退

css
 体验AI代码助手
 代码解读
复制代码
    element {
        -webkit-touch-callout:none;
    }
复制代码

16. 禁止数字识别为电话号码

ini
 体验AI代码助手
 代码解读
复制代码
   <meta name = "format-detection" content = "telephone=no">
复制代码

17. transition闪屏

css
 体验AI代码助手
 代码解读
复制代码
  .box { 
       -webkit-transform-style: preserve-3d; 
       -webkit-backface-visibility: hidden; 
       -webkit-perspective: 1000; 
  }
复制代码