css-selector-thing

为什么CSS选择器是从右往左解析?,突然看到这个题目。只是觉得,有蹊跷。(有看过浏览器源代码的同学请指教)。下面自己理一理这个问题。

20180704.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="20180704.css">
<title>S</title>
</head>
<body>
<div class="one">
<article class="little">
<header>little</header>
<p class="time">下午</p>
<p>下雨</p>
</article>
<article id="more">
<header>not little</header>
<p class="time">晚上</p>
<p data-weather="rain">
<span></span>停了
</p>
</article>
</div>
</body>
</html>

20180704.css

1
2
3
4
5
6
7
8
@charset "utf-8";
p {text-decoration: underline;}
.one {border-top: 3px solid #123456;}
.one header {font-size: 32px;}
.little .time {color: cyan;}
.one [data-weather="rain"] span {color: brown;}
.one .two .three {font-size: 250in;}
.three .two .one {font-size: 250in;}

~测试用的代码,不用在意细节~

看图,我也不说啥了。

the Document tree

我们看一下 CSS2.1 2.3 The CSS 2.1 processing model 第4小条, Annotate

Annotate every element of the document tree by assigning a single value to every
property that is applicable to the target media type.

the annotated document tree

好了,就先到这里。(我们只考虑我们自己写的样式)问题是 怎么得到这 annotated tree

第一种办法

从dom中取一个节点,然后拿这个节点去cssRules匹配。举几个栗子。

  1. 拿到 html,啥也没干。(只考虑自己写的样式)
  2. 拿到 div(class="one"),正好有 .one {border-top: 3px solid #123456;},标记上去。
  3. 拿到 p(class="time"), 哎,有 p {text-decoration: underline;},标记上去;再往下看,有 .little .time {color: cyan;},查看一下它的先祖,有 .little存在,标记上去。
  4. ……

第二种办法

我们拿一个cssRule,去dom中匹配。试一试。

  1. 拿到 p {text-decoration: underline;},那好,dom中那么多 p,全给标记上。
  2. 拿到 .one [data-weather="rain"] span {color: brown;},找到,标记上。
  3. 拿到 .one .two .three {font-size: 250in;},没找到,对不起。

仔细看第一种方法,拿一个节点,去匹配所有cssRules。也就是说我拿所有的cssRules去测试一个节点,然后下一个节点……。这黄花菜都凉了。

我们看第二种方法。我们就有了从左开始还是从右开始匹配的问题。

  1. .one [data-weather="rain"] span {color: brown;}
  • ltr:找到 .one,ok;然后再找子孙 [data-weather="rain"],ok;然后再找子孙 span,ok。
  • rtl:找到 span,查看先祖条件.one [data-weather="rain"],没问题。
  1. .one .two .three {font-size: 250in;}
  • ltr:找到 .one,ok;然后再找 .two,不存在。
  • rtl:没有 .three,不存在。

找到,我们上面用了这个词。怎么找到?我们的栗子简化多了。一般都是遍历dom返回一组节点。比如找到了一组 span,然后用限制条件,把不合格的剔除出去。这个简单的栗子中,rtl显然比ltr要快,rtl第一步就找到嫌疑节点(不过看看 .three .two .one,ltr第一步就排除,rtl开始还以为找到了。但这仅仅是我们构造的一个不寻常规则。)。复杂的情况下,节点,cssRules数量很大。rtl会具有更大的优势。

我们回头再看.one [data-weather="rain"] span {color: brown;}.one .two .three {font-size: 250in;},为什么从右边开始?因为我本来就是要找 span.three,它们自身就处于右边(当然,还要进行过滤,满足前面的限制条件)。浏览器也是用从右向左这个策略。浏览器还提供了几个常用的API:document.querySelector,document.querySelectorAll。比如document.querySelector('.one [data-weather="rain"] span')我们就能轻易的找到 <span>雨</span>所代表的节点。

这也就是用一个selector,怎么找到匹配的节点。Sizzle 就是干这个事情的(没用过,没看过,听别人说的)。暂时,我觉得document.querySelector,document.querySelectorAll,document.getElement*,够用了。