H5 开发总结 - 解决问题篇(持续更新)

开篇

作为前端开发人员,业务上时常会涉及一些移动 H5 需求页面开发。但 Web 页面开发久了,转到 H5 页面形式开发难免会遇到一些问题。

笔者将自己所遇到的问题通过笔记形式,作为本文内容呈现给读者,希望能够一定程度给每一位读者带来帮助。

首要

在一个 H5 页面,你需要为此页面设置 标签来禁止页面被缩放,否则一个 input 框的聚焦都会导致页面被放大。

<meta name="viewport"
    content="width=device-width, initial-scale=1.0, maximum-scale=1, user-scalable=no,viewport-fit=cover">

一、元素 点击/长按 后 :hover 样式一直存在

移动 H5 环境下 点击 或 长按 元素时(如:material-ui Button),元素的 :hover 伪类选择器的样式一直应用存在,导致影响了元素默认的样式。

原则上 H5 环境下的元素是不需要设置 :hover 样式交互的,但避免比较会有一个组件元素同时适应 Web 和 H5。

图片[1]-H5 开发总结 - 解决问题篇(持续更新)-JieYingAI捷鹰AI

解决办法可以是:用元素自身的属性权重高于 :hover 内设定的属性权重。

图片[2]-H5 开发总结 - 解决问题篇(持续更新)-JieYingAI捷鹰AI

二、H5 部分元素点击会有背景阴影闪烁

当测试同学给你提了 Bug 说 H5 页面某一处元素点击后会有背景闪烁,这并不是我们有意为之。

在手机浏览器下,元素在被点击后会出现背景是为了提供视觉反馈和用户体验。这种背景被称为"高亮"或"按下"状态。

如果是 安卓 或者是 电脑浏览器模拟 H5 开发 场景,出现这个原因可能是元素设置了 cursor: pointer,原则上 H5 应用是不用设置 cursor 的,通过设置 cursor: default; 取消 pointer 交互行为得到解决。

但经过上面设置发现在 iOS 下并不生效,需要设置 -webkit-tap-highlight-color: transparent; 禁用这种默认行为。

最好的方式是同时设置(可以在全局统一设置):

cursor: default;
-webkit-tap-highlight-color: transparent;

三、iOS 浏览器导航回退,页面未重新加载

在 iOS 端(如:Safari 浏览器),从 A 页面到 B 页面后,通过浏览器导航返回按钮,回到 A 页面后,会发现 A 页面没有重新加载,包括数据请求。

比如实现一个支付功能,在 B 页面支付完成了,回到 A 页面后期望重新请求数据来更新支付状态。安卓一切正常(部分安卓可能也有类似问题),但 iOS 并不会如期实现。

分析原因:这是因为 浏览器的“往返缓存(back-forward cache - bfcache)”特性 的一种缓存机制,用来加快用户在点击浏览器前进或者后退按钮时的速度。bfcache 将离开页面时的DOM和JS的状态全都保存了下来,也就是说将整个页面保存了下来放在了页面的缓存当中。

解决方案:onpageshow 事件会在每次加载页面时触发(不论页面是否来自 bfcache),结合 PageTransitionEvent 对象的 persisted 属性来判断页面是否从缓存中读取。

// iOS 浏览器后退页面后,决绝读取的缓存快照问题
window.onpageshow = function (e) {
  if (e.persisted) {
    // 如果页面是读取缓存,刷新页面
    location.reload();
  }
}

四、Android 安卓浏览器导航会退,ajax 看上去请求了,但实际未走到后台

在 Android 手机浏览器上,从 A 页面到 B 页面后,通过浏览器导航返回按钮,回到 A 页面后,与 iOS 不同的是:页面是重新加载了,并且会执行 mount 时定义的接口请求。

但是接口下发的数据都是老的,并不是在 B 页面操作后最新的数据。

联系后台同学查看 API 接口日志后,发现 ajax 看似在前端浏览器内发起请求了,但实际却是未进入后台。

经过查询分析,发现 ajax 在一些浏览器下,为了减少重复请求,会将载入的数据进行缓存,在请求 url 没有发生变化时,会直接读取缓存中的数据,并不会真正请求到后台。

所以我们需要禁止这种缓存特征,提供两种方式来解决:

在ajax发送请求前加上 anyAjaxObj.setRequestHeader("Cache-Control","no-cache");在 URL 后面加上时间戳:"?random=" + Date.now()。

参考:…

五、Android 华为系统浏览器导航会退,页面监听不到 onpageshow 动作

在鸿蒙系统浏览器内,从 B 页面会退到 A 页面会,A 页面组件不但不执行 mount,且绑定的 onpageshow 也不会去执行,这该如何在页面拿到最新数据?

出现这个情况的原因我们也是可以推断,页面完全被缓存了。

我的做法是在进入 B 页面时,开启一个计时器,通过轮询的方式,在会退到页面后执行 location.reload() 强制刷新页面。

const reload = () => {
  location.reload();
  setTimeout(reload, 500);
}
reload();

但要注意,使用 setInterval 在这个场景下会失效,故采用 setTimeout 模拟轮询。

六、iOS 使用 textarea 标签实现复制文本时,页面滚动闪烁问题

通常我们在实现复制内容时,会采用 input 元素执行来 select() 实现;但为了保留复制的文本格式,会选择 textarea 元素实现复制。

但在 iOS Safari 浏览器下,textarea 进行复制时会出现页面滚动闪烁,尽管设置 textarea 宽高为 0 也无法得到解决。

一个有效的解决办法是:为 textarea 元素设置 readOnly 属性。

export const copyToClipboard = (text: string) => {
  const tempTextArea = document.createElement("textarea");
  tempTextArea.readOnly = true; // 解决问题的关键一步
  tempTextArea.style.position = "fixed";
  tempTextArea.style.left = "-9999px";
  document.body.appendChild(tempTextArea);
  tempTextArea.value = text;
  tempTextArea.setAttribute("wrap", "off");
  tempTextArea.select();
  document.execCommand("copy");
  document.body.removeChild(tempTextArea);
};

七、H5 Input 输入框确认输入的内容

业务上有一个需求:在输入框中填写内容按下回车键添加到数据列表中。

这在 Web 端很好实现:监听 onkeydown 键盘事件,如果按下的是回车键,则将输入框的 value 数据进行添加。

但在移动 H5 如何实现呢?手机键盘右下角有一个「换行/确认」键,它的作用与 Web 电脑键盘上的 回车键 是一致的,所以在 H5 中确认输入框中的数据,也可以通过 onkeydown 键盘事件 来实现。

代码示例:(React)

useEffect(() => {
  const onKeydown = (event: KeyboardEvent) => {
    const inputEle = inputRef.current!; // input ref.
    // Web 端键盘确认键添加,同时支持 H5 手机键盘 换行 键进行添加。
    if (event.keyCode === 13 && inputEle.value && inputEle === document.activeElement) {
      const value = inputEle.value;
      // 执行添加操作
    }
  }
  document.addEventListener('keydown', onKeydown);
  return () => {
    document.removeEventListener('keydown', onKeydown);
  }
}, []);

八、iOS Input 输入框 focus 聚焦问题

为什么无法通过 JS 代码让 Input 自动聚焦?

iOS 系统下的浏览器对输入框聚焦有限制,需要用户行为如:click、touchstart、touchend 等事件动作才能让输入框聚焦。所以在未进行用户行为的条件下执行 input.focus() 无法实现聚焦。

相关介绍:github.com/ant-design/…

为什么我在用户行为事件如 click 中执行 input.focus() 还是无法让输入框聚焦?

iOS 下让输入框聚焦,除了用户行为触发外,还有一个必要条件:事件回调中必须同步执行 input.focus() 让输入框聚焦。

如果你发现输入就聚焦不生效,可能是执行逻辑放在了异步回调中,如 setTimeout。

所以,对于这类场景:由文本状态切换到编辑状态,在 iOS 下无法实现输入框聚焦(在框架内,获取 input 元素势必需要等待节点挂载后进行,就会产生异步)。

为应对这种场景,只能是给容器设置为 编辑 状态下的视图,引导用户手动点击输入框进行聚焦唤起输入法。

九、H5 底部弹框内容滚动带动了下方页面滚动

在 H5 底部出弹框展示数据列表并进行滚动时,如果弹框之下的页面存在滚动条,也会随着弹框内容一起滚动,这样的体验并不好。

为避免页面随之滚动,常见的方案是为 body 设置 overflow: hidden 禁止页面进行滚动。

假设弹框通过 open state 控制打开与关闭,在弹框打开时为 body 元素设置 overflow: hidden;在弹框关闭时移出设置的 overflow 属性。

设置方式可以是 className,也可以是操作 style:

const openDrawer = () => {
  // document.body.style.overflow = 'hidden';
  document.body.classList.add('gl-overflow-hidden');
}
const closeDrawer = () => {
  // document.body.style.removeProperty('overflow');
  document.body.classList.remove('gl-overflow-hidden');
}
.gl-overflow-hidden{
  overflow: hidden!important;
}

十、H5 Safari 设置 100vh 出现滚动条解决方法

在开发 H5 时,为保证元素高度展示为一屏,通常会采用 100vh 来设置,但在 ios safari 浏览器下却出现了滚动条。

经过分析,safari 浏览器 100vh 会包含浏览器头部和底部区域高度,其他浏览器的 100vh 仅是页面可视区域。

图片[3]-H5 开发总结 - 解决问题篇(持续更新)-JieYingAI捷鹰AI

解决方式:通过 JS 计算 1vh 高度,结合 CSS 变量来实现。

封装 react hook useViewVh:

import { useEffect } from "react";
const useViewVh = () => {
  const isMobole = isRunMobile();
  const setViewVh = () => {
    const vh = window.innerHeight / 100;
    document.documentElement.style.setProperty("--vh", vh + "px");
  };
  // 窗口变化监听
  useEffect(() => {
    setViewVh();
    // orientationchange 监听 Safari 由横向查看模式切换为纵向查看模式
    window.addEventListener("orientationchange" in window ? "orientationchange" : "resize", setViewVh);
  }, []);
};
export default useViewVh;

CSS 使用方式:

height: 100vh;
--> 
height: calc(var(--vh) * 100);

参考:移动端100vh解决方法blog.csdn.net/qq_43223007…

更新中...

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享