iOS中contenteditable元素格式化文本后如何恢复平滑滚动?
iOS上Contenteditable元素格式化后平滑滚动失效的问题与临时解决思路
我最近在处理iOS平台上的contenteditable富文本编辑场景时,碰到了一个非常头疼的问题——当对多行文本进行粗体格式化后,元素的平滑滚动功能直接失效了。下面先把问题的复现步骤、影响范围以及我摸索出来的临时解决方法整理出来,希望能帮到遇到同样问题的人。
问题复现步骤
- 先平滑滚动到contenteditable元素内的目标文本位置
- 触摸元素唤起系统键盘
- 选中多行文本并将其设置为粗体格式
- 此时尝试滚动内容,会发现原本的平滑滚动完全消失,滚动变得生硬卡顿
最简复现代码
<html> <head> <meta name="viewport" content="width=device-width,minimum-scale=1,maximum-scale=1,user-scalable=no"> <style> body { font-size: 24px; } </style> </head> <body> <button>button</button> | <a onclick="console.log();">anchor</a><br> <br> <div contenteditable> What is Lorem Ipsum? Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum. Why do we use it? It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout. The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English. Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like). </div> <br> <button>button</button> | <a onclick="console.log();">anchor</a> </body> </html>
问题影响范围
这个Bug并非单个浏览器的问题,在Safari、Chrome、Firefox的iOS版本中都能复现;Opera虽然看似修复了部分场景,但会引入其他功能异常。包括CKEditor5、Medium编辑器、AlloyEditor在内的主流富文本编辑器,都受到这个问题的影响。
已知的滚动恢复方式
目前发现只有两种操作能让平滑滚动恢复正常:
- 收起系统键盘后,重新触摸任意contenteditable元素唤起键盘
- 点击页面上其他可交互元素(比如按钮、链接)
测试设备情况
我在多台iOS设备上做了测试,结果如下:
- 出现异常的设备:iPhoneX(iOS11.4)、iPhone6(iOS11.4/11.3.1)、iPhone5s(iOS11.4)
- 表现正常的设备:iPhone5s(iOS11.3.1)、iPhoneSE(iOS11.4/9.3.2)
相关调查进展
- 2018年5月31日已经向苹果官方提交了Bug报告,但至今未收到回复
- 2018年6月15日发现了新的触发场景:选中更多行文本时也会引发该问题(对应WebKit Bug编号:186333)
- 2018年6月25日尝试将contenteditable放入iframe测试:唤起键盘后先滚动再格式化,能暂时保持平滑滚动,但关闭键盘重新打开后问题又会出现。推测这个问题和键盘唤起时系统创建的"虚拟空间"以及元素尺寸刷新机制有关。
临时解决思路
由于这是iOS系统层面的Bug,目前没有完美的根治方法,但可以通过一些workaround来临时恢复平滑滚动:
1. 临时切换焦点触发状态重置
在完成文本格式化操作后,短暂将焦点切换到一个隐藏的可交互元素,再切回contenteditable元素,这样可以触发系统重新计算滚动相关的状态:
function restoreSmoothScroll() { const editableEl = document.querySelector('[contenteditable]'); if (!editableEl) return; // 创建一个隐藏的临时触发元素 const hiddenBtn = document.createElement('button'); hiddenBtn.style.cssText = 'position: absolute; opacity: 0; pointer-events: none;'; document.body.appendChild(hiddenBtn); // 切换焦点 hiddenBtn.focus(); setTimeout(() => { editableEl.focus(); document.body.removeChild(hiddenBtn); }, 100); } // 在格式化完成后调用这个函数 restoreSmoothScroll();
2. 强制触发元素重排重绘
通过修改元素样式触发浏览器重排,让系统重新计算滚动属性:
function triggerRedraw(el) { el.style.display = 'none'; void el.offsetHeight; // 强制浏览器计算元素尺寸,触发重排 el.style.display = ''; } // 格式化完成后调用 const editableEl = document.querySelector('[contenteditable]'); triggerRedraw(editableEl);
3. 利用iframe隔离(复杂度较高)
如果业务场景允许,可以将contenteditable元素放在iframe中,并且确保在格式化操作前已经完成滚动操作。不过这种方法需要处理iframe和主页面的交互逻辑,实现起来相对麻烦,适合对滚动体验要求极高的场景。
需要注意的是,这些方法都是临时的权宜之计,最终还是需要苹果官方修复这个系统级的Bug。
内容的提问来源于stack exchange,提问作者Bruno
相关产品推荐
相关产品推荐

