必须在上设dir="rtl",否则光标、tab流、flex主轴、滚动条等关键行为不翻转;/需单独设dir;混合文本用隔离,禁用dir="auto"。

dir 属性不是排版“开关”,而是文本逻辑方向的语义锚点——没它,RTL 页面里光标乱跳、数字倒序、括号翻转、Tab 键顺序错乱这些都不是样式问题,是底层双向算法(UBA)压根没启动。
为什么必须在 上设 dir="rtl"
只给 或某个 <div> 加 <code>dir="rtl",浏览器不会初始化整页 RTL 上下文:
-
<input>和<textarea></textarea>光标仍从左端起始,退格行为按 LTR 逻辑执行 - Tab 键焦点流不翻转,用户按 Tab 仍从左到右跳,不符合 RTL 用户直觉
-
flex-direction: row主轴起点不变,项目排列顺序未反转 - 滚动条卡在右侧(而非 RTL 系统默认的左侧)
-
<select></select>下拉项、<button></button>文本内部渲染逻辑不受影响
真正生效的写法只有:。其中 lang="ar" 不可省略——它联动字体连字、东阿拉伯数字(١٢٣)、屏幕阅读器语音规则。缺了 lang,dir 就只是“视觉上像”,不是“逻辑上对”。
<td> 和 <code><th> 必须单独加 <code>dir
表格行/列容器(<tr>、<code><table>)不参与文本排版,方向不继承:
<ul><li>在 <code><tr dir="rtl"> 上设 <code>dir,单元格内文本方向不变
<td> 或 <code><th> 上,例如:<code><td dir="rtl">مرحبا</td>
<input>,还需再给该 <input> 单独加 dir="rtl",否则输入框内光标和方向仍按文档默认走混合文本中用 <bdi></bdi> 隔离,别碰 dir="auto"
dir="auto" 依赖首个强方向字符判断,但现实数据几乎总让它失效:
- 开头是数字(
"123 عربي")、ASCII 字母("Hello عالم")、空格或中文 → 全部 fallback 到ltr - Vue/React 动态插入内容时,初始为空或含占位符,方向被锁定后不再重算
- Safari 旧版本对带变音符号的阿拉伯字母识别不稳定
正确做法是:对不可控的混排子串(如 API 返回的用户名、URL、邮箱),用 <bdi></bdi> 显式隔离:
<p dir="rtl">مرحبا <bdi>user@مثال.com</bdi></p> <button><bdi>Ahmad123</bdi></button>
<bdi></bdi> 创建独立 Bidi 环境,切断父子污染,且所有现代浏览器支持良好;而 <bdo dir="rtl"></bdo> 是强制覆盖,会把 "123" 显示成 "321",仅适合调试。
动态切换语言时,只改 document.documentElement.dir 不够
JavaScript 切换方向不能只改 DOM 属性,还要同步处理配套信号:
- 必须同时设置
document.documentElement.lang,否则字体、数字、语音规则不同步 - 第三方组件(如 React UI 库、Bootstrap 表单)可能未适配 RTL,需额外加载 RTL 专用样式表,重置
margin-left→margin-right等物理属性 - CSS 逻辑属性(如
margin-inline-start)比物理属性更可靠,但旧版浏览器需降级写法:dir(rtl) { right: 10px; } - 验证是否生效,不能只看
getComputedStyle,要检查document.documentElement.dir属性值 + 实际光标位置 + Tab 流向 + 复制粘贴后文本顺序
最难的从来不是加 dir,而是判断哪一层该继承、哪一层该隔离、哪一层该显式覆盖——多数方向 bug 都出在“以为加了 dir="rtl" 就结束了”,结果阿拉伯语里的英文 URL 被拆成两行还反向换行,或者表单光标还在左边。











