了解CSS中的级联

层叠样式表是Web的样式语言,使用简单的语法,但有时,如果作者不知道它的“层叠”部分是如何工作的,那么它们的简单性可能是骗人的。通过查看翻译后的SLD,并想知道所有的SLD规则是如何从一组更小的CSS规则中产生的,这种困惑可能会变得更大。

本文档试图阐明级联是如何工作的,如何在SLD转换中控制它,对于那些更喜欢更简单(如果更详细)的样式的文档,将演示如何永远关闭级联。

CSS规则应用程序

给定一个特定的特性,如何应用CSS规则?这大致就是算法:

  • 查找选择器与当前功能匹配的所有规则

  • 按专一性分类,不太具体到更具体

  • 让更具体的规则添加到并重写在不太具体的规则中设置的属性

如您所见,根据特征属性,由上述算法构建新规则,混合该特征的所有适用规则。

该算法的核心允许为非常复杂的规则集准备相当简洁的样式表,方法是在不太具体的规则中设置公共位,并重写它们,在更具体的规则中指定规范的异常。

理解特异性

在网页中CSS specificity 设置为四个数字的元组,称为a、b、c、d:

  • a :如果样式是元素的本地样式,即在元素中定义的样式,则设置为1。 style 属性

  • b :统计选择器中ID属性的数量

  • c :统计选择器中其他属性和伪类的数量

  • d :统计选择器中元素名称或伪元素的数目

ab ,这比 c ,以此类推,例如,如果一个规则 a=1 第二个是 a=0 第一个更具体,不管值是什么 bcd .

以下是CSS规范中的一些示例,从不太具体到更具体:

*             {}  /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
li            {}  /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
li:first-line {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul li         {}  /* a=0 b=0 c=0 d=2 -> specificity = 0,0,0,2 */
ul ol+li      {}  /* a=0 b=0 c=0 d=3 -> specificity = 0,0,0,3 */
h1 + *[rel=up]{}  /* a=0 b=0 c=1 d=1 -> specificity = 0,0,1,1 */
ul ol li.red  {}  /* a=0 b=0 c=1 d=3 -> specificity = 0,0,1,3 */
li.red.level  {}  /* a=0 b=0 c=2 d=1 -> specificity = 0,0,2,1 */
#x34y         {}  /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */
style="..."       /* a=1 b=0 c=0 d=0 -> specificity = 1,0,0,0 */

在地图CSS中,没有HTML元素可以具有本地样式,因此 a 总是零。其他计算如下:

  • b :规则中的功能ID数

  • c :cql过滤器和伪类中的属性数(例如, :mark )在选择器中使用

  • d :1如果指定了类型名,则为0,否则为

以下是一些例子,从小到大:

*                  {}  /* a=0 b=0 c=0 d=0 -> specificity = 0,0,0,0 */
topp:states        {}  /* a=0 b=0 c=0 d=1 -> specificity = 0,0,0,1 */
:mark              {}  /* a=0 b=0 c=1 d=0 -> specificity = 0,0,1,0 */
[a = 1 and b > 10] {}  /* a=0 b=0 c=1 d=0 -> specificity = 0,0,2,0 */
#states.1          {}  /* a=0 b=1 c=0 d=0 -> specificity = 0,1,0,0 */

如果两条规则具有相同的特殊性,则文档中的最后一条规则获胜。

在级联模式下理解CSS到SLD的转换

如上所述,CSS规则应用程序可能会为每个功能生成不同的规则,这取决于其属性以及如何通过不同的CSS选择器进行匹配。

另一方面,SLD从规则开始,依次将所有规则应用于每个特性,绘制每个匹配规则。这两种评估模式是完全不同的,为了将CSS转换成SLD,翻译器必须生成所有可能的CSS规则组合,同时确保生成的SLD规则是互斥的(CSS最终为给定的功能生成了一个单一的规则)。

所有规则的组合称为 power set ,并且通过取消所有先前生成的SLD规则的过滤器并添加到当前规则中来保证排他性。可以想象,这将产生许多规则,其中包含非常复杂的过滤器。

译者通过应用一些基本策略来解决上述问题:

  • 生成的过滤器将在内存中进行评估,如果发现过滤器“不可能”,也就是说,某些无法与现有功能匹配的内容,则不会发出相关规则(例如, a = 1 and a = 2a = 1 and not(a = 1)

  • 生成的SLD有一个供应商选项 <sld:VendorOption name="ruleEvaluation">first</sld:VendorOption> 这迫使渲染器在其中一个规则与某个功能实际匹配时放弃进一步的规则评估。

理论上来说,上面的内容是很好和足够的,而实际上,它可以分解为非常复杂的CSS样式,这些样式有许多正交选择器(例如,控制属性值填充的10个规则) a 以及控制属性值的笔画的10条规则 b ,以及另外10个基于属性控制填充和笔划不透明度的规则 c 产生1000种可能的组合)。

因此,默认情况下,只有当规则集“小”时,翻译器才会尝试生成简化的和完全排他的规则,否则将生成完全功率集,以避免在CSS到SLD的翻译时间(分钟,如果不是小时)中出现。

翻译模式由 @mode 指令,具有以下值:

  • 'Exclusive' :使用简化的选择器以最少一组SLD规则转换样式表,占用所需的时间和内存。

  • 'Simple' :只生成电源集,而不尝试构建最小样式表,确保翻译速度快,即使生成的SLD看起来非常复杂。

  • 'Auto' :这是默认值,它将执行电源集扩展,然后将继续 Exclusive 模式(如果电源集包含的派生规则少于100个),或 Simple 否则进入模式。可以使用 @autoThreshold 指令。

平面翻译模式

这个 @mode 指令有最后一个可能值, Flat 这使得平面翻译模式不应用特殊性和级联。

在这种模式下,CSS将被翻译成几乎1:1的对应SLD,每个生成CSS规则和等效SLD规则的规则,但具有伪类的规则除外,这些伪类通常指定如何删除/填充标记和符号。

编写带有伪类的规则时应该小心,只有当它们的选择器与前面的规则之一匹配时才会考虑它们。考虑这个例子:

@mode "Flat";

[type = 'Capital'] {
  mark: symbol(circle);
}

[type = 'Capital'] :mark {
  fill: white;
  size: 6px;
}

:mark {
  stroke: black;
  stroke-width: 2px;
}

在上面的示例中,第一个规则 :mark 将考虑伪类并与大写类合并,忽略第二类。由此产生的SLD将不包含“圆形”标记的任何笔画规格:

<?xml version="1.0" encoding="UTF-8"?><sld:StyledLayerDescriptor xmlns="http://www.opengis.net/sld"
      xmlns:sld="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc"
      xmlns:gml="http://www.opengis.net/gml" version="1.0.0">
  <sld:NamedLayer>
    <sld:Name/>
    <sld:UserStyle>
      <sld:Name>Default Styler</sld:Name>
      <sld:FeatureTypeStyle>
        <sld:Rule>
          <ogc:Filter>
            <ogc:PropertyIsEqualTo>
              <ogc:PropertyName>type</ogc:PropertyName>
              <ogc:Literal>Capital</ogc:Literal>
            </ogc:PropertyIsEqualTo>
          </ogc:Filter>
          <sld:PointSymbolizer>
            <sld:Graphic>
              <sld:Mark>
                <sld:WellKnownName>circle</sld:WellKnownName>
                <sld:Fill>
                  <sld:CssParameter name="fill">#ffffff</sld:CssParameter>
                </sld:Fill>
              </sld:Mark>
              <sld:Size>6</sld:Size>
            </sld:Graphic>
          </sld:PointSymbolizer>
        </sld:Rule>
      </sld:FeatureTypeStyle>
    </sld:UserStyle>
  </sld:NamedLayer>
</sld:StyledLayerDescriptor>

平模的优点是:

  • 很容易理解,规则是按编写顺序应用的。

  • 图例控件,生成的图例不包含意外,因为规则未混合在一起且未重新排序

主要的缺点是,在一般规则中不再有共享通用样式位的方法,所有通用位都必须在所有规则中重复。

备注

在未来,我们希望增加嵌套规则的能力,这将解决平面模式的一些限制,而不引入标准级联模式中最复杂的位。

比较级联模式和平面模式,一个例子

考虑以下CSS:

* { stroke: black; stroke-width: 10 }

[cat = 'important'] { stroke: yellow; }

如果将上述样式转换为级联模式,将生成两个互斥的SLD规则:

  • 一个在所有cat属性为“重要”的特性上应用10px宽的黄色笔画。

  • 一个在所有cat属性不是“重要”的功能上应用10px宽的黑色笔画。

因此,每一个特征都将被画成一条线,黄色或黑色。

如果样式包含 @mode 'Flat' 指令顶部,它将生成两个非互斥的SLD规则:

  • 一个在所有功能上应用10px宽的黑色笔画

  • 一个在所有cat属性为“重要”的功能上应用1px宽的Yewlow笔画。

因此,所有功能至少将被漆成10px黑色,但“重要”功能也将有第二条1px黄色线。 在第一个上面

Previous: 指令