Skip to content

Instantly share code, notes, and snippets.

@codev0
Created November 24, 2019 16:18
Show Gist options
  • Save codev0/c92a0dbd22651f4ef7e8e7ced28cc77e to your computer and use it in GitHub Desktop.
Save codev0/c92a0dbd22651f4ef7e8e7ced28cc77e to your computer and use it in GitHub Desktop.
Zurb Foundation mediaQuery utility on es6
import { debounce } from 'debounce';
const defaultQueries = {
'default': 'only screen',
landscape: 'only screen and (orientation: landscape)',
portrait: 'only screen and (orientation: portrait)',
retina: 'only screen and (-webkit-min-device-pixel-ratio: 2),' +
'only screen and (min--moz-device-pixel-ratio: 2),' +
'only screen and (-o-min-device-pixel-ratio: 2/1),' +
'only screen and (min-device-pixel-ratio: 2),' +
'only screen and (min-resolution: 192dpi),' +
'only screen and (min-resolution: 2dppx)'
};
class MediaQuery {
constructor() {
this.queries = [];
this.current = '';
this._init();
}
/**
* Initializes the media query helper, by extracting the breakpoint list from the CSS and activating the breakpoint watcher.
* @function
* @private
*/
_init() {
// make sure the initialization is only done once when calling _init() several times
if (this.isInitialized === true) {
return;
} else {
this.isInitialized = true;
}
const self = this;
const $meta = document.querySelector('meta.foundation-mq');
if (!$meta.length) {
const node = document.createElement('meta');
node.setAttribute('class', 'foundation-mq')
document.head.appendChild(node);
}
const extractedStyles = window.getComputedStyle(document.querySelector('meta.foundation-mq')).getPropertyValue('font-family')
let namedQueries;
namedQueries = this._parseStyleToObject(extractedStyles);
self.queries = []; // reset
for (let key in namedQueries) {
if (namedQueries.hasOwnProperty(key)) {
self.queries.push({
name: key,
value: `only screen and (min-width: ${namedQueries[key]})`
});
self.queries.push({
name: `${key}-down`,
value: `only screen and (max-width: ${namedQueries[key]})`
});
}
}
this.current = this._getCurrentSize();
this._watcher();
}
/**
* Reinitializes the media query helper.
* Useful if your CSS breakpoint configuration has just been loaded or has changed since the initialization.
* @function
* @private
*/
_reInit() {
this.isInitialized = false;
this._init();
}
/**
* Checks if the screen is at least as wide as a breakpoint.
* @function
* @param {String} size - Name of the breakpoint to check.
* @returns {Boolean} `true` if the breakpoint matches, `false` if it's smaller.
*/
atLeast(size) {
const query = this.get(size);
if (query) {
return window.matchMedia(query).matches;
}
return false;
}
/**
* Checks if the screen matches to a breakpoint.
* @function
* @param {String} size - Name of the breakpoint to check, either 'small only' or 'small'. Omitting 'only' falls back to using atLeast() method.
* @returns {Boolean} `true` if the breakpoint matches, `false` if it does not.
*/
is(size) {
size = size.trim().split(' ');
if (size.length > 1 && size[1] === 'only') {
if (size[0] === this._getCurrentSize()) return true;
} else {
return this.atLeast(size[0]);
}
return false;
}
/**
* Gets the media query of a breakpoint.
* @function
* @param {String} size - Name of the breakpoint to get.
* @returns {String|null} - The media query of the breakpoint, or `null` if the breakpoint doesn't exist.
*/
get(size) {
for (let i in this.queries) {
if (this.queries.hasOwnProperty(i)) {
var query = this.queries[i];
if (size === query.name) return query.value;
}
}
return null;
}
/**
* Gets the current breakpoint name by testing every breakpoint and returning the last one to match (the biggest one).
* @function
* @private
* @returns {String} Name of the current breakpoint.
*/
_getCurrentSize() {
let matched;
for (let i = 0; i < this.queries.length; i++) {
let query = this.queries[i];
if (window.matchMedia(query.value).matches) {
matched = query;
}
}
if (typeof matched === 'object') {
return matched.name;
} else {
return matched;
}
}
/**
* Activates the breakpoint watcher, which fires an event on the window whenever the breakpoint changes.
* @function
* @private
*/
_watcher() {
const listener = debounce(() => {
let newSize = this._getCurrentSize(), currentSize = this.current;
if (newSize !== currentSize) {
const event = new CustomEvent('changed:mediaquery', [newSize, currentSize]);
// Change the current media query
this.current = newSize;
// Broadcast the media query change on the window
window.dispatchEvent(event);
}
}, 200);
window.removeEventListener('resize', listener);
window.addEventListener('resize', listener);
}
// Thank you: https://github.com/sindresorhus/query-string
_parseStyleToObject(str) {
let styleObject = {};
if (typeof str !== 'string') {
return styleObject;
}
str = str.trim().slice(1, -1); // browsers re-quote string style values
if (!str) {
return styleObject;
}
styleObject = str.split('&').reduce(function (ret, param) {
let parts = param.replace(/\+/g, ' ').split('=');
let key = parts[0];
let val = parts[1];
key = decodeURIComponent(key);
// missing `=` should be `null`:
// http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
val = typeof val === 'undefined' ? null : decodeURIComponent(val);
if (!ret.hasOwnProperty(key)) {
ret[key] = val;
} else if (Array.isArray(ret[key])) {
ret[key].push(val);
} else {
ret[key] = [ret[key], val];
}
return ret;
}, {});
return styleObject;
}
}
export { MediaQuery };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment