Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing CSS media query rules via JavaScript/DOM

I've been using a number of libraries (including my own) to dynamically load assets based upon media queries I've outlined in CSS files. For example:

In CSS:

  @media screen and (max-width: 480px) {
    .foo {
      display: none;
    }
  }

And using an asset loader; require.js, modernizr.js etc or using window.matchMedia and associated addListener() functions:

  if (function("screen and (max-width: 480px)")){
    // Load several files
    load(['mobile.js','mobile.css']);
  }

Declaring them twice is awkward/silly and as far as I can find, all JS helper libraries and asset loaders require you to repeat the media queries rather than locating them programmatically from JS/DOM.

So, I've been exploring the ability to access the values programmatically via document.stylesheets, but I'm not sure if they're accessible and there seems very little documentation to suggest they are.

The furthest I've got is looking for CSSMediaRule and using console.dir(document.stylesheets) amongst others to explore the stylesheet object.

But no references are made (within document.stylesheets) to the actual media query rules used in CSS - only the classes to be applied as a result of the media queries... What I'm trying to locate, programmatically, is:

"screen and (max-width: 480px)"

Is there any way of accessing such CSS Media query rules via JavaScript/DOM?

like image 323
nickhar Avatar asked Mar 29 '13 01:03

nickhar


1 Answers

For get rules use cross browser variant:

var styleSheet = document.styleSheets[0];
var rules = styleSheet.cssRules || styleSheet.rules; // IE <= 8 use "rules" property

For detect CSSMediaRule object in rules list use (not work in IE <= 8, because "CSSMediaRule" class available only in IE >= 9):

var i = 0;
if (rules[i].type == 4)
{
    // Do something
}

Some functions for get styles from current DOM (not works in IE <= 8):

function getCssRulesFromDocumentStyleSheets(media)
{
    var resultCssRules = '';
    for (var i = 0; i < document.styleSheets.length; i++)
    {
        var styleSheet = document.styleSheets[i];

        if (isRuleFromMedia(styleSheet, media))
            resultCssRules += getCssRulesFromRuleList(styleSheet.cssRules || styleSheet.rules, media);
    }

    return resultCssRules;
}

function getCssRulesFromRuleList(rules, media)
{
    var resultCssRules = '';
    for (var i = 0; i < rules.length; i++)
    {
        var rule = rules[i];
        if (rule.type == 1) // CSSStyleRule
        {
            resultCssRules += rule.cssText + "\r\n";
        }
        else if (rule.type == 3) // CSSImportRule
        {
            if (isRuleFromMedia(rule, media))
                resultCssRules += getCssRulesFromRuleList(rule.styleSheet.cssRules || rule.styleSheet.rules, media);
        }
        else if (rule.type == 4) // CSSMediaRule
        {
            if (isRuleFromMedia(rule, media))
                resultCssRules += getCssRulesFromRuleList(rule.cssRules || rule.rules, media);
        }
    }

    return resultCssRules;
}

function isRuleFromMedia(ruleOrStyleSheet, media)
{
    while (ruleOrStyleSheet)
    {
        var mediaList = ruleOrStyleSheet.media;
        if (mediaList)
        {
            if (!isMediaListContainsValue(mediaList, media) && !isMediaListContainsValue(mediaList, 'all') && mediaList.length > 0)
                return false;
        }

        ruleOrStyleSheet = ruleOrStyleSheet.ownerRule || ruleOrStyleSheet.parentRule || ruleOrStyleSheet.parentStyleSheet;
    }

    return true;
}

function isMediaListContainsValue(mediaList, media)
{
    media = String(media).toLowerCase();

    for (var i = 0; i < mediaList.length; i++)
    {
        // Access to mediaList by "[index]" notation now work in IE (tested in versions 7, 8, 9)
        if (String(mediaList.item(i)).toLowerCase() == media)
            return true;
    }

    return false;
}

Functions usage example:

<style type="text/css">
    @media screen and (max-width: 480px) {
        p { margin: 10px; }
    }

    @media screen and (max-width: 500px) {
        p { margin: 15px; }
    }

    @media print {
        p { margin: 20px; }
    }
</style>

<!-- ... -->

<script type="text/javascript">
    alert(getCssRulesFromDocumentStyleSheets('print'));
    alert(getCssRulesFromDocumentStyleSheets('screen and (max-width: 480px)'));
    // For IE (no space after colon), you can add fix to "isMediaListContainsValue" function
    alert(getCssRulesFromDocumentStyleSheets('screen and (max-width:480px)'));
</script>

Here is a JS Fiddle for it: https://jsfiddle.net/luisperezphd/hyentcqc/

like image 187
Mihail H. Avatar answered Sep 18 '22 09:09

Mihail H.