dir 与 direction
在 HTML 中,指示元素中文本方向有如下几种方式。
第一种是直接使用 dir
属性去描述,详情 - dir
<div dir="rtl">
<span>test1</span>
<div dir="ltr">test2
<div dir="auto">test3</div>
</div>
</div>
第二种方式是使用 style
内联样式去实现,属性为 direction
,常用的值有 ltr
、rtl
,详情 - direction
<div style="direction: rtl;">
<span>test1</span>
<div style="direction: ltr;">test2
<div>test3</div>
</div>
</div>
还有一种方法是使用 writing-mode
去修改,这里不讨论这种情况,感兴趣的可以去了解
张鑫旭 - 改变CSS世界纵横规则的writing-mode属性
【CSS深入】设置不同块级流方向时的属性百分比计算
我们这里讨论前两种情况
上面代码的效果如下
dir
的取值有如下几种
ltr
, 指从左到右,用于那种从左向右书写的语言(比如英语)。rtl
, 指从右到左,用于那种从右向左书写的语言(比如阿拉伯语)。auto
, 指由用户代理决定方向。它在解析元素中字符时会运用一个基本算法,直到发现一个具有强方向性的字符,然后将这一方向应用于整个元素。
这个属性可以被 CSS 属性 direction
和 unicode-bidi
覆盖,如果 CSS 网页有效且该元素支持这些属性的话。
direction
则是一个 CSS 属性,用来设置文本、表列水平溢出的方向,与 dir
功能类似。
unicode-bidi
与 direction
是仅有的两个不受 all
简写影响的属性。unicode-bidi
是用来描述双书写方向文本的,如果一块内容同时包含有从左到右书写和从右到左书写的文本,那么用户代理(the user-agent)会使用复杂的 Unicode
算法来决定如何显示文本。unicode-bidi
属性会覆盖此算法,允许开发人员控制文本嵌入(text embedding)。
与 HTML 中的 dir
属性不同,direction
属性不会从表列继承到表单元格, 因为 CSS 继承遵从文档流, 而表单元格位于行内部, 但不在列内部。
二者的另一个区别体现在选择器的匹配上,相应的选择器有两种,类选择器 :dir()
和属性选择器 [dir=ltr]
[dir=ltr]
只能匹配有dir
属性的元素,如果是用内联样式去实现的话就匹配不到。:dir()
用来匹配特定书写方向的元素,他可以匹配使用样式实现后的元素。详情 :dir()。
例如给上面的代码加上如下样式
[dir=ltr] {
color: blueviolet;
}
这里只有使用了 dir
属性的元素会被匹配,匹配简单元素就需要使用类选择器
添加如下代码
div:dir(rtl) {
color: red;
}
div:dir(ltr) {
color: orange;
}
[dir=ltr] {
color: blueviolet;
}
这里需要注意的是,类选择器只有火狐实现了支持,其他浏览器都是不支持的。
下面为 chrome 和 firefox 中的效果图
基本显示规则
我们看如下代码,其中一个 div 包含纯文本,一个 div 包含一个 span 包裹的文本
<h2>example 2</h2>
<hr>
<div dir="rtl">hello CSS test</div>
<div dir="rtl">
hello
<span>CSS </span>
<span>test</span>
</div>
他们的显示效果是一致的
因为这里会改变的只是内联元素块的左右顺序,所有的内联内容,都被算作一个同质内联盒子,是当作一个整体处理的,因此,只有近似右对齐效果,而具体每个文字都没有左右顺序的变化。
内联元素块,包括替换元素(replaced element),如 img
、button
、input
、video
、object
等。因此,上面的例子,而当里面的 inline
元素设置 display: inline-block;
,则会看到左右顺序的变化。如下
<h2>example 2</h2>
<hr>
<div dir="rtl">hello CSS test</div>
<div dir="rtl">
hello
<span style="display: inline-block;">CSS </span>
<span style="display: inline-block;">test</span>
</div>
<div dir="rtl">
hello
<p style="display: inline-block;">CSS </p>
<p style="display: inline-block;">test</p>
</div>
对块级元素,只有类似 text-align: right;
的效果。
<div dir="rtl">
hello
<p>CSS </p>
<p>test</p>
</div>
<div style="text-align: right;">
hello
<p>CSS </p>
<p>test</p>
</div>
扩展
到这里,基本的用法都已经介绍完了,但是看下面这些示例
<h2>example 3</h2>
<hr>
<div style="direction: rtl;">
<span>1</span>
<span>2</span>
<span>
4555
<span>432</span>
</span>
</div>
<p style="direction: rtl;">
<span>span1</span>
<span>span2</span>
<span>span3</span>
</p>
<div style="direction: rtl;">
<span>,</span>
<span>。</span>
<span>
!
<span>~!@</span>
</span>
</div>
what? 有没有一种,明明代码一毛一样,就是显示不一样的既视感。显示结果让你怀疑人生,粘贴复制不相信自己。
然并卵,不管在什么浏览器,怎么粘贴复制,显示结果还是这样。
数字、符号、字母 的显示结果完全不一样。
可以看在其他浏览器中的显示结果
firefox
IE
这说明,他们的显示是按照一定规则去显示的。
划重点!
这里涉及到一个概念 Unicode 双向算法
。
我们要知道的是,字符在内存中存放的逻辑顺序,和在页面显示的顺序是不一样的。最常用来处理双向文字的算法是 Unicode 双向算法
,Unicode
定义了它其中每个字符的方向属性,浏览器应用的一组规则(通过这个来进行自动判断文本 Unicode
方向属性应该使用哪种方向)在显示时产生正确的顺序由 Unicode 双向算法
进行描述,也可以简称为 bidi算法
。
Unicode
方向属性包含三种类型:强字符
、弱字符
、中性字符
。
首先,文字方向分为 Left To right
,例如,英文、汉字等。Right To Left
,例如,阿拉伯文字、希伯来文字等。
字符 | 方向 | 显示 | 包含 |
---|---|---|---|
强字符 | Left or Right | 方向性确定,Left or Right,和上下文无关,且可能影响其前后字符的方向性 | 英文、汉字、阿拉伯文字等 |
弱字符 | Left or Right | 方向性确定,但是不会影响前后字符的方向性 | 数字、数字相关的字符 |
中性字符 | Neutral | 方向性不确定,由上下文环境决定其方向 | 大部分标点符号、空格 |
看到这里,相信很多人已经大致可以猜到为什么会有上面的显示结果了。
页面上有一个文本显示的基础方向(全局方向)
,定义的是一个区域的总体方向。他决定从哪个位置开始书写文字。例如 HTML 的 dir
就可以控制一个区域的文本方向。
而一段文本中具有相同方向性的连续字符,称为方向串
,例如一段文本中,有多个字符,包含阿拉伯文字和英文字符,这里英文字符和阿拉伯字符就属于不同的方向串
。
例如下面这段文字
ولدت (+86)176-1234-0000
对他进行删除,移动光标等操作,就会发现位置变换和你的预期是不一样的,因为这段文本包含了不同的方向串
。
他的方向如下,分为 7 个方向串
数字是弱字符,有自己的方向性,向右
阿拉伯文字为强字符,向左
符号为中性字符,受到强字符的影响,向左
ولدت (+86)176-1234-0000
0000 | >
- | <
1234 | >
- | <
176 | >
( | <
86 | >
+) ولدت | <
而我们把其中的阿拉伯文字删掉后,自动会变为我们熟悉的样子。
(+86)176-1234-0000
我们还可以强制转换字符的方向性,使用如下的编码
第一类是隐式双向控制字符
,使用他们可以影响被包裹起来的中性字符方向
名称 | Unicode Code | HTML Code | 描述 |
---|---|---|---|
LEFT-TO-RIGHT MARK (LRM) | U+200E | ‎ 、‎ | 左到右 |
Right-To-Left Mark(RLM) | U+200F | ‏ 、‏ | 右到左 |
<!-- 使用隐式实体 -->
<p>ولدت ‎(+‎86‎)‎176‎-‎1234‎-‎0000</p>
<p>ولدت ‎(+86)176-1234-0000‎</p>
<!-- 使用一个内联元素区分开强字符并设置中性字符的方向 -->
<p>ولدت <span dir="ltr">(+86)176-1234-0000</span></p>
<p><span dir="ltr">ولدت</span> (+86)176-1234-0000</p>
<!-- bdi标签能隔离外面的方向,默认值为auto,自动判断文本应该使用哪种方向 -->
<p>ولدت <bdi dir="auto">(+86)176-1234-0000</bdi></p>
<p><bdi dir="auto">ولدت</bdi> (+86)176-1234-0000</p>
显示标记
如下,需成对使用,使用一个开始标记和结束标记包裹
LEFT-TO-RIGHT EMBEDDING (LRE) | U+202A | ‪ or dir="ltr"
RIGHT-TO-LEFT EMBEDDING (RLE) | U+202B | ‫ or dir="rtl"
LEFT-TO-RIGHT OVERRIDE (LRO) | U+202D | ‭ or <bdo dir="ltr">
RIGHT-TO-LEFT OVERRIDE (RLO) | U+202E | ‮ or <bdo dir="rtl">
POP DIRECTIONAL FORMATTING (PDF) | U+202C | ‬ or </bdo>
例如这样去使用
就会发现后面的电话号码是正常显示的,符号方向也正常。