HTML 中,<sup> 緊接 <bdo dir="rtl"> 時為何會排到 <bdo> 左邊去?

為什麼「123」顯示到前面去了?


截圖中的奇怪順序和 &、& 元素一點關係都沒有,純粹是因為 &。你那數字「123」不管是放在 & 里還是 & 里還是不用任何標籤,只要在同一段落內和前面那些強制 RTL 的文本相鄰就會受影響。

& 元素的功能是覆蓋掉 Unicode 字元默認的書寫方向。當你使用 &,其內部字元的書寫方向就被強制設定成了 RTL,不再遵循 Unicode 的判斷演算法。這段 RTL 文本後面跟著的空格和數字(weak type)也和這段 RTL 文本一起從右向左排,所以就跑到它左邊去了。數字內部的字元順序不會顛倒(因為在阿拉伯語、希伯來語等 RTL 書寫系統中,數字都是 LTR 的)。

「abc X 123 def」這段文字,如果我把其中的「X」換成一個阿拉伯字母「 」就會有問題中這種情況,如果把數字改成字母就不會:

abc X 123 def

abc 123 def

abc GHI def

數字和拉丁字母的書寫方向性質不同,數字(weak type)會受相鄰 RTL 文本的影響,而拉丁字母(strong type)不會。具體演算法詳見下文。

至於解決方案,因為不知道你到底為什麼要用 &,所以也不知道該怎麼處理你的情況。

W3C 有一篇文檔題為〈Authoring HTML: Handling Right-to-left Scripts〉[0-1],詳細介紹了在 HTML 中應當如何處理 bidi 文本。這篇文章比較淺顯,也比較實用,推薦所有需要處理 bidi 文本的 HTML 開發者閱讀。〈7.2 Weak/neutral characters at the edge of a directional run〉這一節 [0-2] 舉的例子和問題類似,基本處理方法就是給位置出錯的 weak type 文本加上明確的 dir 屬性(注意,不是加 &,是在 & 這樣普通的元素上加 dir)。

[0-1] http://www.w3.org/TR/i18n-html-tech-bidi/

[0-2] http://www.w3.org/TR/i18n-html-tech-bidi/#ri20030726.140315918

* * *

以下簡介 Unicode Bidirectional Algorithm 的相關知識,主要都來自〈Unicode Standard Annex #9〉[1-1],恐怕沒什麼人會感興趣,但我還是順便整理出來吧:

& 元素的功能詳見文檔 [1-2],其中這句話很明確:

If the elements dir attribute is in the ltr, then for the purposes of the bidirectional algorithm, the user agent must act as if there was a U+202E RIGHT-TO-LEFT OVERRIDE character at the start of the element, and a U+202C POP DIRECTIONAL FORMATTING at the end of the element.

[1-1] Unicode Standard Annex #9: http://www.unicode.org/reports/tr9/

[1-2] bdo – BiDi override http://dev.w3.org/html5/spec/the-bdo-element.html

HTML 文檔中提到了兩個字元:

  • U+202E RIGHT-TO-LEFT OVERRIDE 簡稱 RLO [2-1]

  • (當然,另有 U+202D LEFT-TO-RIGHT OVERRIDE 簡稱 LRO)

  • U+202C POP DIRECTIONAL FORMATTING 簡稱 PDF [2-2]

LRO 和 RLO 是 Unicode 的兩個「explicit embedding code」之一,它們會把文本流中自己後面的所有字元都強制設定為 RTL,直到 PDF 這個「terminating explicit directional code」來中止。

所以 & 的作用就是強制其內部的書寫方向為 RTL,不管那些文本默認是什麼方向(比如,拉丁字母默認是 LTR,阿拉伯字母默認是 RTL)。

[2-1] http://www.unicode.org/reports/tr9/#Explicit_Directional_Overrides

[2-2] http://www.unicode.org/reports/tr9/#Terminating_Explicit_Directional_Code

Unicode Bidirectional Algorithm(UBA)很強大的一點就是:它不簡簡單單是把字元分成 LTR 和 RTL,而是把字元分成很多種 bidirectional character type,然後根據不同 type 的不同性質以及各種不同的書寫方向嵌套情況來決定當前字元到底該用什麼方向。於是 [3-1]:

  • 拉丁字母都是 L 這一 type 的,而希伯來字母是 R,阿拉伯字母是 AL,這三個 type 都屬於 strong type,它們直接決定自己的書寫方向,除非被 explicit embedding code 這類事情影響;

  • 歐洲數字(UBA 文檔中用「European number」這個說法來把 123… 和阿拉伯世界用的數字 … 分開)屬於 weak type 中的 EN,它的書寫方向和上下文有關;

  • 而空格屬於 neutral(中性)的 WS 一類,完全依賴上下文來確定自己的書寫方向。

[3-1] http://www.unicode.org/reports/tr9/#Bidirectional_Character_Types

UBA 對文本流做一系列處理來得出每一個字元所處的 embedding level。偶數 level 表示 LTR,奇數 level 表示 RTL。[4-1]

如果我有一段英文,其中有一段阿拉伯文,阿拉伯文中又有一段英文——那麼最外層英文的 level 為 0,阿拉伯文的 level 為 1,內層英文的 level 為 2。這就是 embedding。每次反轉書寫方向都嵌套一層。

一個段落的初始 level 為 0(不過如果一開頭就是 R 或 AL 字元那麼這個段落的 level 就是 1)。

上文說的「LRO 會把文本流中自己後面的所有字元都強制設定為 RTL」實際上是把後面的字元都設定為 R 這個 strong type(書寫方向為 RTL),並且使 embedding level 增大到下一個奇數。[4-2]

[4-1] http://www.unicode.org/reports/tr9/#BD2

[4-2] http://www.unicode.org/reports/tr9/#X4

我們構造一個例子:

ab &cd& 12 ef

處理 RLO–PDF 之後,這段文本的 bidirectional character type 和 embedding level 就成了:

[L-L-WS]-[R-R]-[WS-EN-EN-WS-L-L]

[0-0-0-[1-1]-0-0-0-0-0-0]

處於同一 level 的連續字元分入一個 run,於是我們有了「L-L-WS / 0-0-0」(ab )、「R-R / 1-1」(cd)和「WS-EN-EN-WS-L-L / 0-0-0-0-0-0」( 12 ef)這三個 run。問題的關鍵在於「 12 ef」(WS-EN-EN-WS-L-L / 0-0-0-0-0-0)這個 run 會怎麼處理,這裡面的 WS 和 EN(一個 neutral 和一個 weak type)是變數。

「1-1」和「0-0-0-0-0-0」相鄰,於是「0-0-0-0-0-0」這個 run 的 sor(start-of-level-run)由 level 較高的 run 決定,為 R。(這一步判斷的依據詳見文檔 [5-1])

「WS-EN-EN-WS-L-L」的第一個字元 WS(空格)左邊是等價為 R 的 sor,右邊是等價為 R 的 EN,所以 WS 變成 R。(這一步變化的依據詳見文檔 [5-2])

這個 R 類空格落在當前 level 為 0 的區域了,level 要加 1;兩個 EN 也落在當前 level 為 0 的區域了,level 要加 2。(這一步變化的依據詳見文檔 [5-3])

[5-1] http://www.unicode.org/reports/tr9/#X10

[5-2] http://www.unicode.org/reports/tr9/#N1

[5-3] http://www.unicode.org/reports/tr9/#Resolving_Implicit_Levels

加上其他字元的判斷,embedding level 序列變成了:

ab cd 12 ef

[0-0-0-[1-1-1-[2-2]]-0-0-0]

每一次 embedding level 增大都會多翻轉一下字元序列 [6-1],所以原來的「ab cd 12 ef」變成了:

ab 12 dc ef

* * *

題主自己搞不清問題本質就試圖草率總結,然後還自我感覺良好地阻止別人把問題理清(「細節越多越局限,禁止更改!」),這是很糟糕的提問方式。

感謝 @Tutu-Lux 細心的測試。

* * *

答案 #1023


&
&
&&
&
&The quick brown fox jumps over the lazy dog.&&really&
&

&The quick brown fox jumps over the lazy dog.&really&&
&

&

由 @梁海 證實,這些出自 Unicode 對於不同文字閱讀順序的規定。

如果寫例子測試時加入數字成分應該就會發現的嗯。


& 標籤支持 html中的全局屬性。


加上&

&,分開段落就可以了


剛剛試了一下,發現針對下標字元串是數字還是字母瀏覽器(Chrome 23、IE9)會有不同表現:

1.純字母:

2.純數字(即題目中的情況):

3.左數字右字母:

4.左字母右數字:

5.混合:

將 dir=rtl 去掉的話則統一將上標顯示在右上角。

其他瀏覽器的情況未做測試。

據此應該能了解這兩個瀏覽器的quirk了。至於具體原因。容我再詳查哈~本人也是正在學習Web開發的菜鳥。


推薦閱讀:

web應用使用jsp還是html做前端頁面?
給HTML初學者的30條最佳實踐
從零開始搭建Vue組件庫 VV-UI
想要對 HTML 和 CSS 有深入的理解,是不是需要學習傳統排版的知識?如果需要,應該學習到什麼程度?

TAG:HTML | Unicode統一碼 |