news 2026/10/17 5:45:56

CKEditor 4 右到左(RTL)文本方向下长单词换行与滚动条问题修复:ticket 10448 测试用例全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
CKEditor 4 右到左(RTL)文本方向下长单词换行与滚动条问题修复:ticket 10448 测试用例全解析
  • 前端
  • UI组件

【免费下载链接】ckeditor4

The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.

项目地址:https://gitcode.com/gh_mirrors/ck/ckeditor4
点击查看免费下载

CKEditor 4 的 bidi 插件用于处理阿拉伯语、希伯来语等从右到左(RTL)书写的文本方向。在 4.5.8 版本中,官方修复了 RTL 方向下编辑区缺少滚动条的问题(ticket 10448),而本仓库tests/tickets/10448/目录完整保留了该缺陷的手工测试脚本、自动化回归测试与复现夹具。本文以该 ticket 的测试文档为骨架,结合 bidi 插件源码与 Bender 测试框架配置,说明该缺陷的复现方式、修复验收标准以及背后的实现原理,读者可据此在自己的 CKEditor 4 集成中排查 RTL 排版相关的溢出与滚动问题。

一、问题背景:ticket 10448 是什么

在 CHANGES.md 的 CKEditor 4.5.8 版本更新日志中,可以找到该缺陷的官方记录:

Fixed: Lack of scrollbar in the right-to-left text direction.

即:在 RTL 文本方向下,当编辑区内出现超长不可断行内容(例如一串没有空格的长单词)时,编辑区无法正常出现水平滚动条,内容被裁切或溢出,用户无法看到/滚动到被裁掉的部分。

该 ticket 被标记为bug,修复版本为4.5.8,对应的插件是 bidi(Bidirectional Text 插件,即双向文本方向控制)。当前仓库根目录 package.json 显示的开发版本为4.25.1,该修复早已合入主线并持续被回归测试守护。

二、手工测试文档逐行解读

tests/tickets/10448/2.md 是 Bender 框架下的手工测试(manual test)脚本,全文内容如下:

@bender-tags: 4.5.8, bug, trac10448 @bender-ui: collapsed @bender-ckeditor-plugins: wysiwygarea, toolbar, bidi ---- 1. Look at the editable. **Expected:** The text inside editable area should be broken between several lines.

其结构分为三部分:

1. Bender 元数据头

  • @bender-tags: 4.5.8, bug, trac10448:声明该测试对应版本 4.5.8、类型为 bug 回归、跟踪编号为 trac10448(即 Trac 工单系统编号)。
  • @bender-ui: collapsed:Bender 手工测试的 UI 呈现方式,表示编辑器以折叠/紧凑形式展示,方便人工聚焦编辑区。
  • @bender-ckeditor-plugins: wysiwygarea, toolbar, bidi:声明运行该测试所需加载的插件白名单——wysiwygarea(iframe 经典编辑区)、toolbar(工具栏)、bidi(双向文本方向控制)。这正是复现 RTL 滚动条缺陷所必需的最小插件组合。

2. 测试步骤

1. Look at the editable.

即:打开测试页面后,直接观察编辑区中的内容呈现效果。

3. 期望结果

Expected:The text inside editable area should be broken between several lines.

编辑区内的文本应当被断成多行(即浏览器自动换行),超长单词在达到编辑区右边界时折行显示,而不是横向溢出把编辑区撑破、或导致滚动条异常。

三、复现夹具与演示页面:超长 RTL 单词的构造

手工测试所加载的页面资源就存放在同一目录下。

复现 HTML 夹具

tests/tickets/10448/1.html 是整个 ticket 的核心复现素材,内容仅有一行:

<p dir="rtl">VerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLinesVerylongWordThatShouldBreakIntoSeveralLines</p>

关键点在于:

  • dir="rtl":段落显式声明 RTL 文本方向;
  • 文本内容是一个由多个VerylongWordThatShouldBreakIntoSeveralLines拼接而成的超长无空格连续单词。普通文本遇到空格才会断行,而无空格的长单词需要依赖浏览器的overflow-wrap/word-break行为;
  • 该夹具在自动化测试中通过CKEDITOR.document.findOne( 'p' ).getOuterHtml()被直接抓取并注入编辑器(见下文第四节)。

演示页面

tests/tickets/10448/2.html 提供了人工可交互的演示页:

<div id="editor1"> <p dir="rtl">VerylongWordThatShouldBreakIntoSeveralLines…</p> </div> <script> CKEDITOR.replace( 'editor1', { width: 600 } ); </script>

页面将同一段超长 RTL 文本放入editor1,并通过 CKEDITOR.replace() 以width: 600实例化编辑器。人工测试者在2.md步骤引导下打开该页,即可观察文本是否被正确断行。如果修复前,文本会横向溢出编辑区边界且不出现滚动条;修复后,文本应自动折行,编辑区宽度不再被撑破。

四、自动化回归测试:断言“段落不超出编辑区宽度”

除手工测试外,同目录的 tests/tickets/10448/1.js 给出了自动化回归测试,用精确的 DOM 度量来守护修复不被回退:

/* bender-tags: 10448 */ /* bender-ckeditor-plugins: toolbar, bidi, wysiwygarea */ ( function() { 'use strict'; bender.editor = { config: { width: 300 } }; bender.test( { _should: { ignore: { 'test paragraph fits into editable width': CKEDITOR.env.safari && CKEDITOR.env.iOS } }, 'test paragraph fits into editable width': function() { var html = CKEDITOR.document.findOne( 'p' ).getOuterHtml(), editor = this.editor, editable = editor.editable(), editableSize = editable.getSize( 'width' ), paragraphSize; this.editorBot.setData( html, function() { paragraphSize = editable.findOne( 'p' ).$.scrollWidth; assert.isTrue( editableSize >= paragraphSize ); } ); } } ); } )();

测试逻辑拆解

  1. 环境声明:bender.editor.config.width: 300将测试编辑器宽度设为 300px(比手工测试的 600px 更窄,更能暴露溢出问题);同时通过_should.ignore在Safari + iOS组合下跳过该用例——这类移动端浏览器的scrollWidth度量行为与桌面端不一致,属于已知平台差异。

  2. 数据注入:从测试页 DOM 中抓取1.html里的<p dir="rtl">超长文本,经editorBot.setData()写入编辑器。

  3. 度量比较:

    • editableSize = editable.getSize( 'width' ):编辑区(iframe body)的布局宽度;
    • paragraphSize = editable.findOne( 'p' ).$.scrollWidth:段落 DOM 原生scrollWidth,即内容滚动宽度;
    • assert.isTrue( editableSize >= paragraphSize ):断言编辑区宽度不小于段落内容宽度。

    如果段落内容横向溢出(修复前状态),scrollWidth会大于编辑区宽度,断言失败;修复后浏览器正确折行,scrollWidth被限制在编辑区宽度内,断言通过。这一度量方式与“断成多行”的人工期望完全对应——折行成功意味着内容宽度不再超出容器。

五、底层原理:bidi 插件如何管理 RTL 方向

要理解为什么dir="rtl"的长文本会触发该类缺陷,需要看 bidi 插件如何把方向写入内容。

1. 方向写入的核心命令

plugins/bidi/plugin.js 定义了bidiCommand( dir )工厂,注册了bidirtl(设为 RTL)与bidiltr(设为 LTR)两个命令:

function bidiCommand( dir ) { return { context: 'p', allowedContent: { 'h1 h2 h3 h4 h5 h6 table ul ol blockquote div tr p div li td': { propertiesOnly: true, attributes: 'dir' } }, requiredContent: 'p[dir]', refresh: function( editor, path ) { ... }, exec: function( editor ) { ... } }; }

命令执行时通过switchDir()完成方向切换,其核心逻辑(plugin.js):

function switchDir( element, dir, editor, database ) { ... element.removeStyle( 'direction' ); if ( useComputedState ) { element.removeAttribute( 'dir' ); if ( dir != element.getComputedStyle( 'direction' ) ) { element.setAttribute( 'dir', dir ); } } else { element.setAttribute( 'dir', dir ); } editor.forceNextSelectionCheck(); }

可见插件优先清理元素上的direction样式,再按useComputedState配置决定是写入dir属性还是依赖计算样式。dir属性最终由浏览器渲染引擎决定文本方向,因此在测试夹具中直接使用<p dir="rtl">等价于用户点击工具栏“从右到左”按钮后的结果。

2. 方向的读取与回显

core/dom/element.js 的getDirection()完整实现了方向解析的回退链:

getDirection: function( useComputed ) { if ( useComputed ) { return this.getComputedStyle( 'direction' ) || this.getDirection() || this.getParent() && this.getParent().getDirection( 1 ) || this.getDocument().$.dir || 'ltr'; } else { return this.getStyle( 'direction' ) || this.getAttribute( 'dir' ); } }

非计算模式下先查 CSSdirection样式、再查dir属性;计算模式下依次向上回溯父元素与文档级dir,最终兜底'ltr'。bidi 插件工具栏按钮的状态回显(plugin.js)正是基于该 API 判断当前选区方向的。

3. 方向变更的事件通知

插件还通过dirChangeNotifier(plugin.js)重写了setStyle/removeStyle/setAttribute/removeAttribute,在方向属性变化时触发dirChanged与contentDirChanged事件,并给工具栏添加cke_mixed_dir_content类提示混合方向内容。这也说明 RTL 支持是贯穿“写入 → 渲染 → UI 状态”的全链路能力。

4. 可用的配置项

  • config.useComputedState(布尔):决定方向读写基于计算样式还是显式dir属性,默认值为false,即显式写入dir属性——这也是测试夹具中dir="rtl"能被插件体系识别的原因;
  • config.enterMode(CKEDITOR.ENTER_P/CKEDITOR.ENTER_BR/CKEDITOR.ENTER_DIV):影响命令执行时对段落块的处理方式,见bidiCommand的exec中对getFullySelected与段落迭代的调用。

六、如何本地运行该 ticket 的测试

本仓库使用 Bender 测试框架(见 bender.js 配置与 package.json 中的benderjs系列 devDependencies)。要验证 ticket 10448 的修复状态,可按以下方式操作:

  1. 安装依赖并初始化 Bender:
    npm install npx bender init
  2. 运行手工测试:启动 Bender 服务器后,在测试树中定位tests/tickets/10448下的2.html,按 2.md 的步骤观察编辑区文本是否断行;1.js作为自动化用例会直接参与bender run的回归批次。
  3. 自动化测试的关键断言路径:测试将编辑区宽度设为 300px(1.js),注入超长 RTL 文本后断言editableSize >= paragraphSize,任何导致横向溢出的回归都会让该用例失败。

七、总结与排查建议

ticket 10448 是 CKEditor 4 在 RTL 场景下的一个典型布局缺陷:超长不可断行文本在dir="rtl"段落中横向溢出且不出现滚动条。仓库通过 tests/tickets/10448/2.md(人工验收)、1.html(复现夹具)、2.html(演示页)与 1.js(自动化断言)四件套,将修复固化为可持续回归的测试资产。

若你在自己的 CKEditor 4 集成中遇到类似 RTL 溢出问题,可参照以下思路排查:

  • 确认内容中dir="rtl"段落内的超长无空格串是否被 CSS 正确折行,重点检查编辑区容器的overflow与scrollWidth表现;
  • 核对useComputedState配置对dir属性写入方式的影响(plugins/bidi/plugin.js);
  • 复用 1.js 中的editableSize >= scrollWidth断言思路,为你的定制编辑区补充宽度溢出回归用例。
  • 前端
  • UI组件

【免费下载链接】ckeditor4

The best enterprise-grade WYSIWYG editor. Fully customizable with countless features and plugins.

项目地址:https://gitcode.com/gh_mirrors/ck/ckeditor4
点击查看免费下载
上一篇:如何用docker-redis-cluster快速构建高可用Redis集群?新手必看教程
下一篇:ResMLP蒸馏技术揭秘:resmlp_36_224.fb_distilled_in1k训练过程详解

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 5:44:56

运算符与表达式练习:从陷阱到综合刷题指南

写运算符和表达式的练习&#xff0c;这事听起来特别基础&#xff0c;但说句实话&#xff0c;我带过不少初学者&#xff0c;也看过很多工作两三年的开发者在代码评审里翻车&#xff0c;问题往往就出在这块。不管是C、C、Java还是Python&#xff0c;运算符和表达式永远是语言的第…

作者头像 李华
网站建设 2026/10/17 5:44:14

C语言结构体详解:数组装不下时如何自定义类型

不知道你注意到没有&#xff0c;学 C 语言到数组这一章&#xff0c;一切还算顺风顺水&#xff1a;一维数组、二维数组&#xff0c;无非是往箱子里塞数据。可一旦碰到"学生信息管理"这类题目&#xff0c;很多人就懵了——一个人身上有姓名、学号、成绩、年龄、性别&am…

作者头像 李华
网站建设 2026/10/17 5:44:12

卷积定理详解:从时域卷积到频域乘积及调制加窗应用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/17 5:43:48

马尔可夫转移矩阵全解析:从数据构造到稳态分布与实战应用

做数据分析这行&#xff0c;多多少少都会碰到一类问题&#xff1a;手里有一串状态序列&#xff0c;想预测下一步最可能是什么&#xff0c;或者想给一堆节点按“重要程度”排个序。搜索引擎的网页排名、App 里的个性化推荐、甚至天气预报里“今天晴明天雨”的概率&#xff0c;背…

作者头像 李华
网站建设 2026/10/17 5:42:11

【c++】STL-容器list的使用

hello~ 很高兴见到大家! 这次带来的是C中关于list容器这部分的一些知识点,如果对你有所帮助的话,可否留下你的三连呢? 个 人 主 页: 默|笙 文章目录一、list的构造二、迭代器分类三、Operations成员函数3.1 sort&#xff08;了解&#xff09;高速缓存命中率3.2 其他的一些函数…

作者头像 李华
网站建设 2026/10/17 5:42:00

GitHub_Trending/templa/templates模板使用统计:最受欢迎的10个模板分析

GitHub_Trending/templa/templates模板使用统计&#xff1a;最受欢迎的10个模板分析 GitHub_Trending/templa/templates是基于开源新版QD框架站发布的公共har模板库&#xff0c;提供了丰富的网站自动化操作模板&#xff0c;帮助用户轻松实现各类网站的签到、数据获取等功能。下…

作者头像 李华