javascriptcssstylesgetcomputedstyle

getComputedStyle: how to get the height defined on CSS instead of the actual rendered height?


I have this code:

<style>

    #div {
        height: 100px;
        background-color: #A00;
    }

    .short {
        max-height: 0px;
    }
</style>

<div id="div" class="short"></div>

<script>

    var div = document.getElementById("div");
    var computed = window.getComputedStyle(div);
    console.log(computed.height); //... I expected 100px instead of the value for max-height definition.

</script>

I know that the max-height limits the height, but how do I get the "height" definition instead of the rendered value?


Solution

  • We can read the CSS stylesheet rules of the document (document.styleSheets, see MDN) and extract rules with the selector text of #div and then use the first height value. The first item should be the highest prioritized style.

    We iterate through all of the styles until we get a result that isn't an empty string, otherwise the function could break in the sense that another #div style without a height property would still be prioritized over a previously defined #div style with a height property.

    Using the function provided at: https://stackoverflow.com/a/22638396/20073186

    function css(el) {
        var sheets = document.styleSheets, ret = [];
        el.matches = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector 
            || el.msMatchesSelector || el.oMatchesSelector;
        for (var i in sheets) {
            var rules = sheets[i].rules || sheets[i].cssRules;
            for (var r in rules) {
                if (el.matches(rules[r].selectorText)) {
                    ret.push(rules[r]);
                }
            }
        }
        return ret;
    }
    

    We can look specifically for the #div styling, and if we can't find it (or a height value), use the computed styling:

    function getStylesheetHeight(element, selectorText) {
        const rules = css(element);
        const selectorRules = rules.filter((rule) => rule.selectorText == selectorText);
    
        let height = null;
    
        for(let index = 0; index < selectorRules.length; index++) {
            if(!selectorRules[index].style.height.length)
                continue;
    
            height = selectorRules[index].style.height;
    
            break;
        }
    
        return height ?? element.style.height;
    };
    

    And use it as such:

    const element = document.getElementById("div");
    
    function css(el) {
        var sheets = document.styleSheets, ret = [];
        el.matches = el.matches || el.webkitMatchesSelector || el.mozMatchesSelector 
            || el.msMatchesSelector || el.oMatchesSelector;
        for (var i in sheets) {
            var rules = sheets[i].rules || sheets[i].cssRules;
            for (var r in rules) {
                if (el.matches(rules[r].selectorText)) {
                    ret.push(rules[r]);
                }
            }
        }
        return ret;
    };
    
    function getStylesheetHeight(element, selectorText) {
        const rules = css(element);
        const selectorRules = rules.filter((rule) => rule.selectorText == selectorText);
    
        let height = null;
    
        for(let index = 0; index < selectorRules.length; index++) {
            if(!selectorRules[index].style.height.length)
                continue;
    
            height = selectorRules[index].style.height;
    
            break;
        }
    
        return height ?? element.style.height;
    };
    
    console.log(getStylesheetHeight(element, "#div"));
    #div {
        height: 100px;
        background-color: #A00;
    }
    
    .short {
        max-height: 0px;
    }
    <div id="div" class="short"></div>

    It's important to note that if the selector text would e.g. be body #div, then it would go uncaught by this function. You could work around this by replacing rules.filter((rule) => rule.selectorText == selectorText) with rules.filter((rule) => rule.selectorText.endsWith(selectorText)) but this could potentionally be misleading.