4

JS関数のCSSの特異性に基づいて、CSSセレクターのセットをどのように並べ替えることができますか?

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

1 に答える 1

7

Selectors レベル 3 仕様から:

セレクターの特異性は次のように計算されます。

  • セレクター内の ID セレクターの数を数える (= a)
  • セレクター内のクラスセレクター、属性セレクター、疑似クラスの数を数える (= b)
  • セレクター内の型セレクターと疑似要素の数を数える (= c)
  • ユニバーサルセレクターを無視する

否定疑似クラス [ :not()] 内のセレクターは、他のものと同様にカウントされますが、否定自体は疑似クラスとしてカウントされません。

3 つの数 abc を (基数が大きい数体系で) 連結すると、特異性が得られます。

例:

*               /* 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
}
于 2011-03-01T18:25:29.007 に答える