简体   繁体   中英

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?

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/

This is how I do it:

In css create classes to expose or hide content at various breakpoints. This is a handy utility anyway. These are already available in Twitter Bootstrap for example.

<style type="text/css">
  .visible-sm, .visible-md, .visible-lg{
    display:none;
  }
  @media (max-width: 480px) {
     .visible-sm{
       display: block;
     }
  }
  @media (min-width: 481px) and (max-width: 960px) {
     .visible-md{
       display: block;
     }
  }
  @media (min-width: 961px) {
     .visible-lg{
       display: block;
     }
  }
</style>

In all your documents add empty spans with these classes. They won't show up on the page if you keep the spans inline.

<span id="media_test">
  <span class="visible-sm"></span>
  <span class="visible-md"></span>
  <span class="visible-lg"></span>
</span>

Add this short jquery extension to your script file. This sets a new class in the body tag that matches the current media query.

(function ($) { 
  $.fn.media_size = function () {
    //the default port size
    var size = 'lg';
    //the sizes used in the css
    var sizes = ['sm','md','lg'];
    //loop over to find which is not hidden
    for (var i = sizes.length - 1; i >= 0; i--) {
     if($('#media_test .visible-'+sizes[i]).css("display").indexOf('none') == -1){
      size = sizes[i];
      break;
     };
    };
    //add a new class to the body tag
    $('body').removeClass(sizes.join(' ')).addClass(size);
  }
}(jQuery));
$(document).media_size();

Now you have an automatic integration with your css media queries Modernizr style.

You can write javascript (jquery) that is conditional on based on your media queries:

<a href="#">how big is this viewport?</a>

<script type="text/javascript">
  $('.sm a').click(function(e){ alert('Media queries say I\'m a small viewport');});
  $('.lg a').click(function(e){ alert('Media queries say I\'m a large viewport');});
</script>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM