W3C 规范中对 CSS 样式的应用算法有规定, 这个规范中的算法比较复杂, 简单来说, 就是根据 CSS 样式选择器中的不同类型的元素出现的次数来计算优先级, 如果某个节点同时命中多个 CSS 样式规则, 以高优先级的样式为准.
W3C 规范具体可以见这个文档: http://www.w3.org/TR/CSS2/cascade.html, "6.4.3 Calculating a selector's specificity" 一节.
例如下面的两条 CSS 样式规则和 HTML:
<style> ul li .clz{color: #33f;} li .clz{color: #f33;} </style> <ul> <li><span class="clz">Hello World!</span></li> </ul>
如果按照 W3C 规范来计算优先级, 那么会计算出:
第一条的优先级: a=0, b=0, c=1, d=2 第二条的优先级: a=0, b=0, c=1, d=1
那么, "Hello World!" 文字的颜色应该是蓝色的. 这个规则之间的优先级定义在文档中说得比较清楚, 简单翻译下就是:
CSS 样式规则优先级
- HTML 标签中的 'style' 属性定义的内联CSS, a=1
- 规则的选择器中, ID 每出现一次, b+=1
- 规则的选择器中, Class 每出现一次, c+=1
- 规则的选择器中, Tag Name 每出现一次, d+=1
简单来理解, a 就是整数的十亿位数, b 就是整数上的百万位数, c 就是整数上的千位数, d 就是整数上的个位数. 代码如下:
// 类: IStyleRule for(NSString *key in selectors){ unichar c = [key characterAtIndex:0]; switch(c){ case '#': // ID _weight += 1 * 1000 * 1000; break; case '.': // Class _weight += 1 * 1000; key = [key lowercaseString]; break; default: // Tag _weight += 1; key = [key lowercaseString]; break; } }
样式来源匹配顺序
上面提到的是规则之间的优先级. 而 CSS 样式还要根据来源的不同来划分优先级, 根据来源划分优先级如下(从低到高):
- 默认 CSS
- 样式表 CSS(HTML 中通过 style 标签来定义)
- 内联 CSS(HTML 标签中通过 style 属性定义)
- 动态修改的样式
这个和 CSS 规范是相符的, 具体可以参考 Wiki.
前面说的是算法和流程, 那么具体到代码中应该怎么实现呢? 首先, 需要关注 IStyleDeclBlock 类.
IStyleDeclBlock 类是一种列表(数组)结构, 所以, 只要根据前面定义的优先级把各种样式添加进去, 当你从前往后遍历这个数组时, 依次应用(渲染)样式即可. 在解析 HTML/XML 时(类 IViewLoader), 按优先级顺序将样式已经加到 IStyleDeclBlock 列表里了.
// 类: IViewLoader // 1. builtin(default) css // 2. stylesheet(by style tag) css // 3. inline css // $: dynamically set css // 1. if(defaultCss){ [view.style set:defaultCss]; } // 2. [view.style.declBlock addKey:@"@" value:@""]; if(attributeDict){ // 3. NSString *css = [attributeDict objectForKey:@"style"]; if(css){ [view.style set:css]; } }
具体的样式渲染代码在 IStyle 类里:
// 类: IStyle for(IStyleDecl *decl in _declBlock.decls){ if([decl.key isEqualToString:@"@"]){ IStyleSheet *sheet = _view.inheritedStyleSheet; if(sheet){ for(IStyleRule *rule in sheet.rules){ if([rule match:_view]){ for(IStyleDecl *decl in rule.declBlock.decls){ [self applyDecl:decl baseUrl:rule.declBlock.baseUrl]; } } } } }else{ [self applyDecl:decl baseUrl:_declBlock.baseUrl]; } }
关于 match 的实现, 可以看这篇博客文章: ideawu - CSS 样式规则的匹配算法实现.
CocoaUI 项目官方网站: http://www.cocoaui.com/, 源码下载: https://github.com/ideawu/cocoaui