Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
libin1991 opened this issue Jan 3, 2019 · 0 comments
Open

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

libin1991 opened this issue Jan 3, 2019 · 0 comments
Labels

Comments

@libin1991
Copy link
Owner

标题挺有意思吧,一个来自正则,一个来自 CSS

前者是正则断言,后者是 CSS 选择器。

正则是用来做什么的?匹配字符的。

选择器是用来做什么的?匹配元素的。

既然都是用来“匹配”的,那么,如果二者在一些地方有什么相似之处,应不足为奇。

我发现 (?<=p):nth-child() 就有很强的相似性。

这里容我慢慢道来。

本文假设读者对 CSS 选择器更熟悉些,所以下面的例子都是先 CSS,后正则。

1. 匹配所有元素

假设页面上有 9li,让所有元素的字都变成红色。此处不会使用 :nth-child 的,直接用标签选择器就行了。

li{
    color: red;
}
复制代码

同样的,正则这边也不需要使用 (?<=p)

'123456789'.replace(/./g, '*') 
// "*********"
复制代码

2. 匹配第1个元素

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

li:first-child{
    color:red;
}
复制代码

而正则可以使用 ^ 位置匹配符:

'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;
}
复制代码

而正则这边呢?这里需要转个弯,要匹配第 3 个,其实是说该字符前面还有 2 个:

 '123456789'.replace(/(?<=^.{2})./g, '*') 
 // "12*456789"
复制代码

4. 匹配前3个元素

我们知道 :nth-child 选择器厉害之处是在于它支持 an+b 表达式,比如匹配前 3 个:

li:nth-child(-n+3){
    color:red;
}
复制代码

正则这边,要匹配的字符前面有 02 个字符,

 '123456789'.replace(/(?<=^.{0,2})./g, '*') 
 // "***456789"
复制代码

5. 匹配奇数位

CSS 这边使用 2n+1

li:nth-child(2n+1){
    color:red;
}
复制代码

正则这边,要匹配的字符前面有 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;
}
复制代码

正则这边变成了:

 '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) 表示 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) 表示 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) 也可以支持交集的

 '123456789'.replace(/(?<=^.{2,})(?<=^.{0,6})./g, '*') 
 // "12*****89"
复制代码

交并补,还有并集,CSS 很简单:

li:nth-child(3),
li:nth-child(7){
    color:red;
}
复制代码

正则呢,有 | 就是来做这个:

 '123456789'.replace(/(?<=^(.{2}|.{6}))./g, '*')
 // "12*456*89"
复制代码

自此,这么一条条看下来,发现了二者确实有多相似之处。

这种跨界比较,我觉得很有趣!

本文完。

另外,欢迎继续阅读本人的《JS正则迷你书》

@libin1991 libin1991 added the CSS label Jan 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant