必须从<html>设dir="rtl"并同步lang,否则光标、焦点、滚动条等底层RTL行为失效;混排文本须用<bdi>隔离;CSS需改用逻辑属性而非物理属性。

必须从 <html> 开始设 dir,否则光标、焦点、滚动条全错位
浏览器只在 <html> 元素上看到 dir="rtl" 时,才真正启用 RTL 感知机制。只在 <body> 或某个 <div> 上加,会导致:<input> 光标仍从左入、Tab 焦点流不翻转、<select> 下拉方向不变、滚动条卡在右侧——这些不是样式问题,是底层行为没被触发。
正确写法是:<html dir="rtl" lang="ar">。lang 必须同步设置,否则字体连字、数字样式(如阿拉伯数字 ١٢٣)、屏幕阅读器语音规则都会失效。
- 服务端渲染(SSR)必须初始输出带
dir和lang的<html>,避免 hydration 不一致 - 客户端动态切换语言时,只改
document.body.dir是无效的,必须用document.documentElement.dir = "rtl" - 第三方 UI 库(如 Bootstrap、MUI)多数依赖
<html>的继承方向做类名映射(如ms-2→me-2),没设对根方向,样式直接失效
<bdi> 是混排文本隔离的唯一可靠方案,dir="auto" 几乎不能用
当一段内容来自 API(比如用户名、搜索词),方向不确定且可能混排(如 أحمد123),dir="auto" 在真实场景里几乎不可靠:首字符是空格、数字、中文、括号或变音符号时,Chrome/Safari 行为不一致;JS 动态插入后也不重新检测;粘贴含 Unicode 方向标记的文本时更易错判。
正确做法是用 <bdi> 包裹不确定方向的内联文本:
立即学习“前端免费学习笔记(深入)”;
<p dir="rtl">النص بالعربية <bdi>https://example.com</bdi></p>
-
<bdi>是 HTML5 原生语义标签,所有现代浏览器支持良好,无需 polyfill - 它会自动隔离该段文本的 Bidi 环境,避免污染父级方向
- 不要用
<bdo dir="rtl">—— 它强制覆盖,会把"123"显示成"321",仅适合调试 - 避免在
<input>或<textarea>上漏掉dir="auto"或dir="rtl",否则用户输入阿拉伯语时退格键行为异常
CSS 物理属性不会随 dir 自动翻转,必须改用逻辑属性
dir="rtl" 会翻转文本对齐、Flex 主轴起始、Grid 的 justify-content 含义,但以下物理属性完全不受影响,必须手动处理:
-
margin-left/padding-right→ 改用margin-inline-start/padding-inline-end -
float: left→ 改用float: inline-start(或降级用:dir(rtl) { float: right; }) -
left: 10px(绝对定位)→ 改用inset-inline-start: 10px,或兼容写法::dir(rtl) { right: 10px; } -
background-position: left top→ 改用background-position: inline-start top -
text-align: right→ 改用text-align: end(推荐始终用start/end,而非left/right)
错误做法是先写 margin-left,再靠 JS 检测 dir 动态切类名——这无法响应运行时 dir 变更(比如用户切换语言后未刷新页面)。
表单 placeholder 和输入内容方向不一致?必须分开控制
placeholder 默认继承父元素的 direction,但输入内容方向由 dir 决定。如果两者冲突(比如 RTL 页面里 placeholder 显示为 LTR),必须显式分开控制:
- 给
<input>或<textarea>单独加dir="rtl",确保输入行为正确(光标、选词、退格) - 用 CSS 控制
placeholder方向:::placeholder { direction: rtl; },而不是指望它自动继承 - 不要在
<input>上只写style="direction: rtl;"而不设dir属性——前者不触发双向算法,复制粘贴顺序仍会乱
真正难的不是加 dir,而是判断哪一层该继承、哪一层该隔离、哪一层该用 <bdi>——多数方向 bug 都出在“以为设了 dir="rtl" 就结束了”,结果阿拉伯语里的英文 URL 被拆成两行还反向换行。



















