javascriptcss-selectorscss-specificity

CSS Specificity Counter


So the CSS specificity rules dictate that ids are the most powerful, then classes, then tag names and then *.

So #id > .class > div > *.

What I'm trying to do is get a 'rating' for each if I provide a string of CSS selectors.

Say I have a Specificity array = [#id, .class, tagname]

span should return [0, 0, 1]

body p should return [0, 0, 2]

.red .orange should return [0, 2, 0]

menu .item #checkout.active should return [1, 2, 1]

I've tried this code, but something is definitely off:

function compare(a) {
    const comparisonOperators = ["#", ".", " "];
    const aRating = [];

    for (let x = 0; x < comparisonOperators.length; x++) {
        aRating[x] = a.includes(comparisonOperators[x])
            ? a.split(comparisonOperators[x]).filter((x) => x).length
            : 0;
    }

    console.log(aRating);
}

compare("menu .item #checkout.active");
// returns [2,3,3]

Any thoughts on this?


Solution

  • I've taken a different approach than yours to solve this.

    function compare(a) {
        var items = a.split([' ']);
        var aRating = [0,0,0];
        items.forEach(function(i) {
            //case 1
            if(i.split('#').length > 1) {
                aRating[0] = aRating[0] + (i.split('#').length - 1)
            }
            //case 2
            if(i.split('.').length > 1) {
                aRating[1] = aRating[1] + (i.split('.').length - 1)
            }
            //case 3
            if(!i.startsWith('#') && !i.startsWith('.')) { 
                aRating[2] = aRating[2] + 1
            }
            
        });
        console.log(aRating);
    }
    

    Initially, I'm splitting the string based on whitespace. This gives the total number of elements to check for. I'm then looping over each item in that array to determine and count the number of selectors.

    In case 1, if an item contains an ID then its length will be greater than 1. For example, #myId. If I split this based on # then I'll get 2 items in an array. I'm using this to determine if it contains an ID or not. In this case, it's true so I increment the aRating array by 1. If it is not an ID then its length will always be 1 because it cannot be split.

    The same logic is applied to case 2 for css class.

    Case 3 is slightly different. If a string does not start with # or a '.', then it must be a tagname.