1

I’m trying to find a way to solve this problem:

p {color: red}
p {color: —-var(defaultcolor)}

—-var(defaultcolor) doesn’t exist yet. it’s being created by JavaScript by clicking a button as a root value. How can I set p to red until the visitor actually click on it?

I can’t use the fallback function —-var(defaultcolor, red) cause I don’t know which color p is going to have. In the example I used red but could be any color.

The p{color:red} is in css file I have no control on. I’m trying to override p but keeping the value as “default” value.

Is there any way to “skip” the value to the second highest class?

  • Try this `p {color: red!important;}` – sta Jul 31 '20 at 13:27
  • 2
    Do you mean `p { color: var (--defaultColor)`? – Juan Mendes Jul 31 '20 at 13:27
  • `!important` will mean that all p tags are red forever. – Loktar Jul 31 '20 at 13:29
  • @Loktar Not really, you can have another rule come after it in source order with `!important` to override it. I think I know what you mean but your statement above is incorrect – Juan Mendes Jul 31 '20 at 13:34
  • @JuanMendes yeah you're right, it would require another declaration to override.. but it seems very much like an X/Y problem honestly. Adding multiple `!importants` let alone one is asking for trouble down the road. – Loktar Jul 31 '20 at 13:36
  • 1
    Why does the var not exist? Can't you just create it and then change it? – Juan Mendes Jul 31 '20 at 13:36
  • Cause if I create the var, it will override the p color, and I don’t want to override it until the user click the button. – Maxime Beguin Jul 31 '20 at 13:39
  • @MaximeBeguin I'm asking why don't you do it differently? Instead of setting the color with css, just the variable initially, [as Rauli suggested](https://stackoverflow.com/a/63192887/227299) – Juan Mendes Jul 31 '20 at 13:40
  • This post is difficult to suss out in terms of what you're asking.. But *I think* you are looking at getting the css of the parent element. [Check this other SO post on the subject.](https://stackoverflow.com/questions/1014861/is-there-a-css-parent-selector) – wahwahwah Jul 31 '20 at 13:47
  • Because the first p row is inside a css file I have no control on. I try to override this row with a variable that would herit the value as a default value, then change it based on the button click. – Maxime Beguin Jul 31 '20 at 13:48
  • I edited the question to reflect the fact that I can’t manipulate p{color:red}, and red is a random color for the example, could be any value. – Maxime Beguin Jul 31 '20 at 13:58

2 Answers2

1

Set defaultcolor in your css to your default value before using this in the rest of css.

:root { --defaultcolor: red; }
p {color: var(--defaultcolor); }

Alternatively, you can use inherit or any other fancy color value:

:root { --defaultcolor: inherit; }
p {color: var(--defaultcolor); }

For changing fixed css rule to unknown value variable, take a look at snippet

// wait for onload to get all polyfills and stuff initialised
window.onload = (event) => {

    let el = document.getElementsByTagName('p')[0]

    // get all applied styling
    let matchedcss = window.getMatchedCSSRules(el, 'color')
    
    // get original colour
    let color = matchedcss[0].style.color
    document.documentElement.style.setProperty('--defaultcolor', color)

    // lets change this later
    el.onclick = () => document.documentElement.style.setProperty('--defaultcolor', 'green')

}

// polyfill needed for newer browsers!
// polyfill https://stackoverflow.com/questions/2952667/find-all-css-rules-that-apply-to-an-element by 7vujy0f0hy
// polyfill window.getMatchedCSSRules() in FireFox 6+
if (typeof window.getMatchedCSSRules !== 'function') {
    var ELEMENT_RE = /[\w-]+/g,
            ID_RE = /#[\w-]+/g,
            CLASS_RE = /\.[\w-]+/g,
            ATTR_RE = /\[[^\]]+\]/g,
            // :not() pseudo-class does not add to specificity, but its content does as if it was outside it
            PSEUDO_CLASSES_RE = /\:(?!not)[\w-]+(\(.*\))?/g,
            PSEUDO_ELEMENTS_RE = /\:\:?(after|before|first-letter|first-line|selection)/g;
        // convert an array-like object to array
        function toArray(list) {
            return [].slice.call(list);
        }

        // handles extraction of `cssRules` as an `Array` from a stylesheet or something that behaves the same
        function getSheetRules(stylesheet) {
            var sheet_media = stylesheet.media && stylesheet.media.mediaText;
            // if this sheet is disabled skip it
            if ( stylesheet.disabled ) return [];
            // if this sheet's media is specified and doesn't match the viewport then skip it
            if ( sheet_media && sheet_media.length && ! window.matchMedia(sheet_media).matches ) return [];
            // get the style rules of this sheet
            return toArray(stylesheet.cssRules);
        }

        function _find(string, re) {
            var matches = string.match(re);
            return matches ? matches.length : 0;
        }

        // calculates the specificity of a given `selector`
        function calculateScore(selector) {
            var score = [0,0,0],
                parts = selector.split(' '),
                part, match;
            //TODO: clean the ':not' part since the last ELEMENT_RE will pick it up
            while (part = parts.shift(), typeof part == 'string') {
                // find all pseudo-elements
                match = _find(part, PSEUDO_ELEMENTS_RE);
                score[2] += match;
                // and remove them
                match && (part = part.replace(PSEUDO_ELEMENTS_RE, ''));
                // find all pseudo-classes
                match = _find(part, PSEUDO_CLASSES_RE);
                score[1] += match;
                // and remove them
                match && (part = part.replace(PSEUDO_CLASSES_RE, ''));
                // find all attributes
                match = _find(part, ATTR_RE);
                score[1] += match;
                // and remove them
                match && (part = part.replace(ATTR_RE, ''));
                // find all IDs
                match = _find(part, ID_RE);
                score[0] += match;
                // and remove them
                match && (part = part.replace(ID_RE, ''));
                // find all classes
                match = _find(part, CLASS_RE);
                score[1] += match;
                // and remove them
                match && (part = part.replace(CLASS_RE, ''));
                // find all elements
                score[2] += _find(part, ELEMENT_RE);
            }
            return parseInt(score.join(''), 10);
        }

        // returns the heights possible specificity score an element can get from a give rule's selectorText
        function getSpecificityScore(element, selector_text) {
            var selectors = selector_text.split(','),
                selector, score, result = 0;
            while (selector = selectors.shift()) {
                if (matchesSelector(element, selector)) {
                    score = calculateScore(selector);
                    result = score > result ? score : result;
                }
            }
            return result;
        }

        function sortBySpecificity(element, rules) {
            // comparing function that sorts CSSStyleRules according to specificity of their `selectorText`
            function compareSpecificity (a, b) {
                return getSpecificityScore(element, b.selectorText) - getSpecificityScore(element, a.selectorText);
            }

            return rules.sort(compareSpecificity);
        }

        // Find correct matchesSelector impl
        function matchesSelector(el, selector) {
          var matcher = el.matchesSelector || el.mozMatchesSelector || 
              el.webkitMatchesSelector || el.oMatchesSelector || el.msMatchesSelector;
          return matcher.call(el, selector);
        }

        //TODO: not supporting 2nd argument for selecting pseudo elements
        //TODO: not supporting 3rd argument for checking author style sheets only
        window.getMatchedCSSRules = function (element /*, pseudo, author_only*/) {
            var style_sheets, sheet, sheet_media,
                rules, rule,
                result = [];
            // get stylesheets and convert to a regular Array
            style_sheets = toArray(window.document.styleSheets);

            // assuming the browser hands us stylesheets in order of appearance
            // we iterate them from the beginning to follow proper cascade order
            while (sheet = style_sheets.shift()) {
                // get the style rules of this sheet
                rules = getSheetRules(sheet);
                // loop the rules in order of appearance
                while (rule = rules.shift()) {
                    // if this is an @import rule
                    if (rule.styleSheet) {
                        // insert the imported stylesheet's rules at the beginning of this stylesheet's rules
                        rules = getSheetRules(rule.styleSheet).concat(rules);
                        // and skip this rule
                        continue;
                    }
                    // if there's no stylesheet attribute BUT there IS a media attribute it's a media rule
                    else if (rule.media) {
                        // insert the contained rules of this media rule to the beginning of this stylesheet's rules
                        rules = getSheetRules(rule).concat(rules);
                        // and skip it
                        continue
                    }

                    // check if this element matches this rule's selector
                    if (matchesSelector(element, rule.selectorText)) {
                        // push the rule to the results set
                        result.push(rule);
                    }
                }
            }
            // sort according to specificity
            return sortBySpecificity(element, result);
        };
}
/* original css*/
p {color: red; }

/* my css */
p {color: var(--defaultcolor); }
<p>click for colour change</p>
Rauli Rajande
  • 1,836
  • 1
  • 18
  • 23
0

You can use Javascript to dynamically alter your style sheet reading the current color using getCurrentStyle()

var pTag = document.querySelector('p');
// Read the tag before creating the dynamic css rule
var pTagColor = getComputedStyle(pTag).color;
// There are other ways to do this, like creating a new style sheet altogether
const rule =  `p { color: var(--default-color, ${pTagColor} )}`;
// Must be inserted after the existing rule
document.styleSheets[0].insertRule(rule, 1);

pTag.addEventListener('click', () => {
  document.documentElement.style.setProperty('--default-color', 'green');
});
p { color: red }
<p> I am red and will turn to green when clicked</p>

DISCLAIMER

If you can change the place that is writing p {color: red} to

:root { --default-color: red }
 p { color: var(--default-color, red) }

as Rauli's answer suggested, I would do that since it's the natural way of using css custom properties.

Juan Mendes
  • 80,964
  • 26
  • 138
  • 189
  • The problem there was that getCurrentStyle() doesn't show the original style, which gets overwritten by second stylesheet rule. So we want to get this original rule, what gets "skipped". That's why there is need to use getMatchedCSSRules(), which sadly got deprecated 2017 :( Interesting question! – Rauli Rajande Jul 31 '20 at 16:56
  • @RauliRajande I don't follow, doesn't the example work without changing the code the OP says they can't change? Read my example carefully, I think you're getting confused with the disclaimer below where I am saying I would prefer to not have to do what I suggested above. I do not have a second line of CSS, notice that I read the color before I create the rule setting the color to the non-existing css variable. – Juan Mendes Jul 31 '20 at 17:14
  • That's ok, I just gave the example what to do, if both css files are not freely changeable. Question about "skipping" was about reading "second highest" css rule as well. – Rauli Rajande Jul 31 '20 at 17:35
  • @RauliRajande Question was about how to solve their problem considering they can't change the first line. The question is poorly named because the OP thinks that's how you should solve the problem See https://en.wikipedia.org/wiki/XY_problem – Juan Mendes Jul 31 '20 at 21:51
  • Thank you Juan, really appreciate your help. i’ll test you answer asap. – Maxime Beguin Aug 01 '20 at 15:18