(?<=p)與:nth-child()的相似性分析

老姚發表於2019-03-01

標題挺有意思吧,一個來自正則,一個來自 CSS

前者是正則斷言,後者是 CSS 選擇器。

正則是用來做什麼的?匹配字元的。

選擇器是用來做什麼的?匹配元素的。

既然都是用來“匹配”的,那麼,如果二者在一些地方有什麼相似之處,應不足為奇。

我發現 (?<=p):nth-child() 就有很強的相似性。

這裡容我慢慢道來。

本文假設讀者對 CSS 選擇器更熟悉些,所以下面的例子都是先 CSS,後正則。

1. 匹配所有元素

假設頁面上有 9li,讓所有元素的字都變成紅色。此處不會使用 :nth-child 的,直接用標籤選擇器就行了。

li{
    color: red;
}
複製程式碼

(?<=p)與:nth-child()的相似性分析
同樣的,正則這邊也不需要使用 (?<=p)

'123456789'.replace(/./g, '*') 
// "*********"
複製程式碼

2. 匹配第1個元素

匹配首元素,在CSS 可以用 :first-child 選擇器:

li:first-child{
    color:red;
}
複製程式碼

(?<=p)與:nth-child()的相似性分析
而正則可以使用 ^ 位置匹配符:

'123456789'.replace(/^./g, '*') 
// "*23456789"
複製程式碼

而我們知道 :first-child 其實是 :nth-child 的特例:

li:nth-child(1){
    color:red;
}
複製程式碼

相應的,正則其實也可以用 (?<=p)

'123456789'.replace(/(?<=^)./g, '*') 
// "*23456789"
複製程式碼

(?<=^) 斷言其實匹配的是一個位置,^ 之後的位置,當然還是開頭。可以參考《JS正則迷你書》對位置的講解。

3. 匹配第3個元素

CSS 裡要匹配第 3 個元素,:nth-child(3) 即可

li:nth-child(3){
    color:red;
}
複製程式碼

(?<=p)與:nth-child()的相似性分析
而正則這邊呢?這裡需要轉個彎,要匹配第 3 個,其實是說該字元前面還有 2 個:

 '123456789'.replace(/(?<=^.{2})./g, '*') 
 // "12*456789"
複製程式碼

4. 匹配前3個元素

我們知道 :nth-child 選擇器厲害之處是在於它支援 an+b 表示式,比如匹配前 3 個:

li:nth-child(-n+3){
    color:red;
}
複製程式碼

(?<=p)與:nth-child()的相似性分析
正則這邊,要匹配的字元前面有 02 個字元,

 '123456789'.replace(/(?<=^.{0,2})./g, '*') 
 // "***456789"
複製程式碼

5. 匹配奇數位

CSS 這邊使用 2n+1

li:nth-child(2n+1){
    color:red;
}
複製程式碼

(?<=p)與:nth-child()的相似性分析
正則這邊,要匹配的字元前面有 024...個字元,

 '123456789'.replace(/(?<=^(.{2})*)./g, '*') 
 // "*2*4*6*8*"
複製程式碼

類似的匹配偶數位,即要匹配的字元前面有 135...個字元:

 '123456789'.replace(/(?<=^(.)(.{2})*)./g, '*') 
 // "1*3*5*7*9"
複製程式碼

6. 更一般的 an+b

比如 CSS 這邊使用 4n+3

li:nth-child(4n+3){
    color:red;
}
複製程式碼

(?<=p)與:nth-child()的相似性分析

正則這邊變成了:

 '123456789'.replace(/(?<=^(.{4})*.{2})./g, '*') 
 // "12*456*89"
複製程式碼

即:要匹配的字元前面還有 4n+2 個字元

7. (?=p) 與 :nth-last-child

我們知道 :nth-child 還有對應的 :nth-last-child。它的意思是,與 :nth-child 相反,不是從前往後數,而是從後面向前數,比如要匹配後 3li

li:nth-last-child(-n+3){
    color:red;
}
複製程式碼

(?<=p)與:nth-child()的相似性分析
正則這邊呢?(?<=p) 表示 p 後面的位置,與之相對的是 (?=p),表示 p 前面的位置。因此要匹配後 3 個字元:

 '123456789'.replace(/.(?=.{0,2}$)/g, '*') 
 // "123456***"
複製程式碼

更多的,與前幾條類似,這裡就不寫了。

8. (?<!p) 與 :not(:nth-child())

CSS 中,要匹配除了第 3 個元素之外的所有元素,可以配合使用 :not選擇器來實現“補集”。

li:not(:nth-child(3)){
    color:red;
}
複製程式碼

(?<=p)與:nth-child()的相似性分析
(?<=p) 表示 p 後面的位置。而 (?<!p) 有點繞,它表示所有位置中,不是 p 後面的那個位置,或者說當下位置的前面不是 p

 '123456789'.replace(/(?<!^.{2})./g, '*') 
 // "**3******"
複製程式碼

9. :nth-child(n+3):nth-child(-n+7)

:nth-child 除了取補,還可以取交集,比如匹配第 3-7 個元素

li:nth-child(n+3):nth-child(-n+7){
    color:red;
}
複製程式碼

(?<=p)與:nth-child()的相似性分析
(?<=p) 也可以支援交集的

 '123456789'.replace(/(?<=^.{2,})(?<=^.{0,6})./g, '*') 
 // "12*****89"
複製程式碼

交併補,還有並集,CSS 很簡單:

li:nth-child(3),
li:nth-child(7){
    color:red;
}
複製程式碼

(?<=p)與:nth-child()的相似性分析
正則呢,有 | 就是來做這個:

 '123456789'.replace(/(?<=^(.{2}|.{6}))./g, '*')
 // "12*456*89"
複製程式碼

自此,這麼一條條看下來,發現了二者確實有多相似之處。

這種跨界比較,我覺得很有趣!

本文完。

另外,歡迎繼續閱讀本人的《JS正則迷你書》

相關文章