【问题标题】:Sorting a set of CSS selectors on the basis of specificity根据特异性对一组 CSS 选择器进行排序
【发布时间】:2011-07-06 17:30:26
【问题描述】:

如何根据 JS 函数中的 CSS 特性对一组 CSS 选择器进行排序?

function SortByCssSpecificity(input_array_of_css_selectors) {
  ...
  return sorted_array_of_css_selectors;
}

【问题讨论】:

  • 好吧,虽然计算特异性很容易,但首先解析简单的选择器将是一个相当大的挑战。稍后我将添加一个准系统伪代码解决方案。与此同时,我将首先引用规范来回答我的问题。边说边编辑。
  • 我创建了我的答案社区维基。如果您或其他任何人提出了实际的 JavaScript 实现,我们很乐意看到它!
  • @BoltClock:在四处搜索时,我发现了一个 PHP 实现 suzyit.com/tools/specificity.php?source。如果可能,我将尝试创建 JS 实现并编辑 wiki。

标签: javascript dom css-selectors css-specificity


【解决方案1】:

来自the Selectors level 3 spec

选择器的特异性计算如下:

  • 统计选择器中 ID 选择器的数量 (= a)
  • 统计选择器中类选择器、属性选择器和伪类的数量 (= b)
  • 统计选择器中类型选择器和伪元素的数量 (= c)
  • 忽略通用选择器

否定伪类 [:not()] 中的选择器与其他选择器一样计算,但否定本身不计为伪类。

连接三个数字 a-b-c(在具有大基数的数字系统中)给出了特异性。

例子:

*               /* a=0 b=0 c=0 -> specificity =   0 */
LI              /* a=0 b=0 c=1 -> specificity =   1 */
UL LI           /* a=0 b=0 c=2 -> specificity =   2 */
UL OL+LI        /* a=0 b=0 c=3 -> specificity =   3 */
H1 + *[REL=up]  /* a=0 b=1 c=1 -> specificity =  11 */
UL OL LI.red    /* a=0 b=1 c=3 -> specificity =  13 */
LI.red.level    /* a=0 b=2 c=1 -> specificity =  21 */
#x34y           /* a=1 b=0 c=0 -> specificity = 100 */
#s12:not(FOO)   /* a=1 b=0 c=1 -> specificity = 101 */

Selectors level 4,在此答案之后发布,由于引入了一些当前超出此答案范围的新选择器,因此为特异性增加了另一层复杂性。)

这里有一个伪代码实现,可以帮助您入门,它远非完美,但我希望这是一个合理的起点:

function SortByCssSpecificity(selectors, element) {
    simple_selectors = [][]
    for selector in selectors {
        // Optionally pass an element to only include selectors that match
        // The implementation of MatchSelector() is outside the scope
        // of this answer, but client-side JS can use Element#matches()
        // https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
        if (element && !MatchSelector(selector, element)) {
            continue
        }

        simple_selectors[selector] = ParseSelector(selector)
        simple_selectors[selector] = simple_selectors[selector].filter(x | x != '*')

        // This assumes pseudo-elements are denoted with double colons per CSS3
        // A conforming implementation must interpret
        // :first-line, :first-letter, :before and :after as pseudo-elements
        a = simple_selectors[selector].filter(x | x ^= '#').length
        b = simple_selectors[selector].filter(x | x ^= '.' or x.match(/^:[^:]+/) or x.match(/^\[.+\]$/)).length
        c = simple_selectors[selector].length - (a + b)

        simple_selectors[selector][count] = parseInt('' + a + b + c)
    }

    return simple_selectors.sort(x, y | x[count] < y[count])
}

function ParseSelector(selector) {
    simple_selectors = []
    // Split by the group operator ','
    // Split each selector group by combinators ' ', '+', '~', '>'
    // :not() is a special case, do not include it as a pseudo-class

    // For the selector div > p:not(.foo) ~ span.bar,
    // sample output is ['div', 'p', '.foo', 'span', '.bar']
    return simple_selectors
}

【讨论】:

  • 而且,我最喜欢的惊喜是重复计数:a.foo.foo (21) 比 a.foo (11) 更具体。
  • @BoltClock 我认为函数 SortByCssSpecificity(selectors) 需要使用 SortByCssSpecificity(selectors, node) 进行更正,因为某些 CSS 选择器文本可能与节点无关。例如,组操作员,。一些 CSS 选择器文本与节点无关,因此不应计算在内。
  • @einstein:不知道我 9 年怎么错过了你的评论,但我现在已经添加了这个功能,虽然它不在最初的问题中。为了准确起见,我确实将节点重命名为元素 - 并非所有节点都是元素节点,选择器只会匹配元素节点。
猜你喜欢
  • 2012-05-25
  • 1970-01-01
  • 1970-01-01
  • 2013-01-14
  • 1970-01-01
  • 2021-03-08
  • 2015-08-21
  • 1970-01-01
相关资源
最近更新 更多