您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~
问题描述
在native
中拉起键盘再收回,滚动列表实际距离发生变化,被键盘一起弹上去了(我这里大约是400px的样子)
这是初始页面:
当我聚焦页面中某个Input
后,再失去焦点,页面变成了这样:
可以看到页面被弹上去了,再次重复步骤页面会继续弹上去,排查了一下发现是在IOS
中的问题。
解决方案
我这里想到的一个比较好的方案是监听弹起键盘
和收起键盘
两个事件,在弹起时记录被破坏的页面scrollY
,收起时恢复。
记录弹起时的页面scrollY
挺简单的,直接这样:
window.addEventListener('focusin', () => {console.log('弹起前高度:', window.scrollY);
}, false)
这样思路就很简单了,代码实现一下:
const focusScrollHeight = useRef<number>(0);useEffect(() => {window.addEventListener('focusin', focusIn, false);window.addEventListener('focusout', focusOut, false);return () => {window.removeEventListener('focusin', focusIn, false);window.removeEventListener('focusout', focusOut, false);};}, []);const focusIn = () => {focusScrollHeight.current = window.scrollY;};const focusOut = (e) => {window.scrollTo(0, focusScrollHeight.current);};
把这段代码放在出现问题的页面里,发现页面会不稳定的回弹到顶部,排查下来应该是antd
的组件触发了收起键盘的事件,所以考虑用类名定位的方案来搞定,同时我们直接封装成一个hook
,在出现问题的页面中直接使用,就像这样:
useResetScrollY.ts
/*** @description: 声明式hook,在页面声明即可自动归位手机端键盘弹起撑开的高度* @param {string} listenerClassList 监听的类名* @return {*}*/
const useResetScrollY = (listenerClassList: string[]) => {const focusScrollHeight = useRef<number>(0);useEffect(() => {window.addEventListener('focusin', focusIn, false);window.addEventListener('focusout', focusOut, false);return () => {window.removeEventListener('focusin', focusIn, false);window.removeEventListener('focusout', focusOut, false);};}, []);const focusIn = () => {focusScrollHeight.current = window.scrollY;};const focusOut = (e) => {const classNameList: string[] = Array.from(e?.target?.classList);if (classNameList?.some((item: string) => listenerClassList?.includes(item))) {window.scrollTo(0, focusScrollHeight.current);}};
};export { useResetScrollY };
这里入参我们给出在页面中需要被监听的组件类名,一般来说都是Input
、Textarea
这类表单组件,然后在这个页面我使用的是antd
的Input
组件,所以使用是这样的:
const Page = () => {// ...if(isIos) {useResetScrollY(['adm-input-element']);}// ...
}
这样子在hook
内部触发事件时只需要判断事件对象的classList
与入参classList
是否存在公共项的情况,就会触发键盘副作用归位的行为。
写在后面
这是笔者在业务中遇到的一个问题,简单总结记录,如果对于这种情况有更好的方案也欢迎留言讨论~
您好,如果喜欢我的文章,可以关注我的公众号「量子前端」,将不定期关注推送前端好文~