Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Universal Selector behaves like a Type Selector #3

Open
mahonnaise opened this issue Dec 22, 2010 · 3 comments
Open

Universal Selector behaves like a Type Selector #3

mahonnaise opened this issue Dec 22, 2010 · 3 comments

Comments

@mahonnaise
Copy link

Not a mistake per se, but it makes using the API a bit odd.

E.g. take a look at this little function which calculates a selector's specificity:

calculateSpecificity = function (selector) {
    var p, plen, part,
        m, mlen, modifier,
        SelectorPart = parserlib.css.SelectorPart,
        specificity = 0,
        // the specificity is stored as a 3 digit base32 number
        LOW  =    1, // 2^0
        MID  =   32, // 2^5
        HIGH = 1024; // 2^10

    for (p = 0, plen = selector.parts.length; p < plen; p++) {
        part = selector.parts[p];
        if (part instanceof SelectorPart) {
            if (part.elementName && part.elementName !== '*') {
                specificity += LOW;
            }
            for (m = 0, mlen = part.modifiers.length; m < mlen; m++) {
                modifier = part.modifiers[m];
                switch (modifier.type) {
                case 'id':
                    specificity += HIGH;
                    break;
                case 'attribute':
                case 'class':
                    specificity += MID;
                    break;
                case 'pseudo':
                    specificity += LOW;
                    break;
                default:
                    console.error('dunno: ' + modifier.type);
                }
            }
        }
    }
    return specificity;
};

For completeness' sake here is the function which makes that number human-readable:

formatSpecificity = function (a) {
    var r = a.toString(32).toUpperCase();
    return '000'.slice(r.length) + r;
};

If you want to toy around with it here is some array with selectors and their specificity (taken from the specs):

[
    ['*{}',              '000'],
    ['li{}',             '001'],
    ['li:first-line{}',  '002'],
    ['ul li{}',          '002'],
    ['ul ol+li{}',       '003'],
    ['h1 + *[rel=up]{}', '011'],
    ['ul ol li.red{}',   '013'],
    ['li.red.level{}',   '021'],
    ['#x34y{}',          '100']
]

Now, back to the thing I didn't like... checking explicitly if this thing which looks like the name of an element is indeed the name of an element (part.elementName !== '*') feels sorta wrong.

@nzakas
Copy link
Contributor

nzakas commented Dec 22, 2010

To summarize, you'd like something more explicit to tell you that you're using a universal selector instead of a type selector. Do you have something in mind?

@mahonnaise
Copy link
Author

part.type === 'type' and part.type === 'universal' perhaps? Well, 'type' is maybe a bit odd, but that's what the spec is using.

With that change in place the if-line would look like this:

if (part.type === 'type')

Hm. Still somewhat icky. Let's go back to the specs...

A selector is a chain of one or more sequences of simple selectors separated by combinators. One pseudo-element may be appended to the last sequence of simple selectors in a selector.

Maybe the breakdown should follow that description a bit more closely.

So, a selector gets broken down into something like: SimpleSelector, [Combinator, SimpleSelector]* and [PseudoElement].

A simple selector is either a type selector, universal selector, attribute selector, class selector, ID selector, or pseudo-class.

If we go with that, each SimpleSelector would contain an array with that stuff from the list above.

E.g.: div#foo.bar > p:hover::first-letter would be broken down to:

1. SimpleSelector (div#foo.bar)
    [type selector (div), ID selector (#foo), class selector (.bar)]

2. Combinator (>)

3. SimpleSelector (p:hover)
    [type selector (p), pseudo class (:hover)]

4. PseudoElement (::first-letter)

With those changes in place the function could then look like this:

calculateSpecificity = function (selector) {
    var p, plen, part,
        s, slen, subPart,
        SimpleSelector = parserlib.css.SimpleSelector,
        PseudoElement = parserlib.css.PseudoElement,
        specificity = 0,
        // the specificity is stored as a 3 digit base32 number
        LOW  =    1, // 2^0
        MID  =   32, // 2^5
        HIGH = 1024; // 2^10

    for (p = 0, plen = selector.parts.length; p < plen; p++) {
        part = selector.parts[p];
        if (part instanceof SimpleSelector) {
            for (s = 0, slen = part.subParts.length; s < slen; s++) {
                subPart = part.subParts[s];
                switch (subPart.type) {
                case 'id':
                    specificity += HIGH;
                    break;
                case 'attribute':
                case 'class':
                    specificity += MID;
                    break;
                case 'type':
                case 'pseudo':
                    specificity += LOW;
                    break;
                case 'universal':
                    break;
                default:
                    console.error('dunno: ' + subPart.type);
                }
            }
        } else if (part instanceof PseudoElement) {
            specificity += LOW;
        }
    }
    return specificity;
};

Since that pseudo element is spec wise on a different level, it's kinda nice to have code which reflects this. (I also renamed 'modifier' to 'subPart'.)

Hope this makes sense. :)

@mahonnaise
Copy link
Author

A simple selector is either [...]

"either"... oops.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants