Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Detect dynamic media queries with JavaScript without hardcoding the breakpoint widths in the script?

I've been searching for a lightweight, flexible, cross-browser solution for accessing CSS Media Queries in JavaScript, without the CSS breakpoints being repeated in the JavaScript code.

CSS-tricks posted a CSS3 animations-based solution, which seemed to nail it, however it recommends using Enquire.js instead.

Enquire.js seems to still require the Media Query sizes to be hardcoded in the script, e.g.

enquire.register("screen and (max-width:45em)", { // do stuff }

The Problem

All solutions so far for accessing Media Queries in Javascript seem to rely on the breakpoint being hardcoded in the script. How can a breakpoint be accessed in a way that allows it to be defined only in CSS, without relying on .on('resize')?

Attempted solution

I've made my own version that works in IE9+, using a hidden element that uses the :content property to add whatever I want when a Query fires (same starting point as ZeroSixThree's solution):

HTML

<body>
    <p>Page content</p>
    <span id="mobile-test"></span>
</body>

CSS

#mobile-test {
    display:none;
    content: 'mq-small';
}
@media screen only and (min-width: 25em) {
    #mobile-test {
        content: 'mq-medium';
    }
}
@media screen only and (min-width: 40em) {
    #mobile-test {
        content: 'mq-large';
    }
}

JavaScript using jQuery

// Allow resizing to be assessed only after a delay, to avoid constant firing on resize. 
var resize;
window.onresize = function() {
    clearTimeout(resize);
    // Call 'onResize' function after a set delay
    resize = setTimeout(detectMediaQuery, 100);
};

// Collect the value of the 'content' property as a string, stripping the quotation marks
function detectMediaQuery() {
    return $('#mobile-test').css('content').replace(/"/g, '');
}

// Finally, use the function to detect the current media query, irrespective of it's breakpoint value
$(window).on('resize load', function() {
    if (detectMediaQuery() === 'mq-small') {
        // Do stuff for small screens etc
    }
});

This way, the Media Query's breakpoint is handled entirely with CSS. No need to update the script if you change your breakpoints. How can this be done?

like image 675
Timmah Avatar asked Jun 22 '17 00:06

Timmah


People also ask

What is the breakpoint in the media query?

Essentially, media query breakpoints are pixel values that a developer/designer can define in CSS. When a responsive website reaches those pixel values, a transformation (such as the one detailed above) occurs so that the website offers an optimal user experience.

How do you use media query for width?

Take a look: @media only screen and (min-width: 360px) and (max-width: 768px) { // do something in this width range. } The media query above will only work for the feature expression (the screen size of the mobile device that you're writing a style for) provided above.

How do I write a media query in JavaScript?

Using Media Queries With JavaScript The window. matchMedia() method returns a MediaQueryList object representing the results of the specified CSS media query string. The value of the matchMedia() method can be any of the media features of the CSS @media rule, like min-height, min-width, orientation, etc.

Can I use min-width and max width in media query?

Combining media query expressions Max-width and min-width can be used together to target a specific range of screen sizes. @media only screen and (max-width: 600px) and (min-width: 400px) {...} The query above will trigger only for screens that are 600-400px wide.


2 Answers

try this

const mq = window.matchMedia( "(min-width: 500px)" );

The matches property returns true or false depending on the query result, e.g.

if (mq.matches) {

  // window width is at least 500px
} else {
  // window width is less than 500px
}

You can also add an event listener which fires when a change is detected:

// media query event handler
if (matchMedia) {
  const mq = window.matchMedia("(min-width: 500px)");
  mq.addListener(WidthChange);
  WidthChange(mq);
}

// media query change
function WidthChange(mq) {
  if (mq.matches) {
    // window width is at least 500px
  } else {
    // window width is less than 500px
  }

}
like image 181
Francesco Taioli Avatar answered Oct 07 '22 15:10

Francesco Taioli


See this post from expert David Walsh Device State Detection with CSS Media Queries and JavaScript:

CSS

.state-indicator {
    position: absolute;
    top: -999em;
    left: -999em;
}
.state-indicator:before { content: 'desktop'; }

/* small desktop */
@media all and (max-width: 1200px) {
    .state-indicator:before { content: 'small-desktop'; }
}

/* tablet */
@media all and (max-width: 1024px) {
    .state-indicator:before { content: 'tablet'; }
}

/* mobile phone */
@media all and (max-width: 768px) {
    .state-indicator:before { content: 'mobile'; }
}

JS

var state = window.getComputedStyle(
    document.querySelector('.state-indicator'), ':before'
).getPropertyValue('content')

Also, this is a clever solution from the javascript guru Nicholas C. Zakas:

  // Test a media query.
  // Example: if (isMedia("screen and (max-width:800px)"){}
  // Copyright 2011 Nicholas C. Zakas. All rights reserved.
  // Licensed under BSD License.
  var isMedia = (function () {

    var div;

    return function (query) {

      //if the <div> doesn't exist, create it and make sure it's hidden
      if (!div) {
        div = document.createElement("div");
        div.id = "ncz1";
        div.style.cssText = "position:absolute;top:-1000px";
        document.body.insertBefore(div, document.body.firstChild);
      }

      div.innerHTML = "_<style media=\"" + query + "\"> #ncz1 { width: 1px; }</style>";
      div.removeChild(div.firstChild);
      return div.offsetWidth == 1;
    };
  })();
like image 4
itacode Avatar answered Oct 07 '22 14:10

itacode