Skip to content

Instantly share code, notes, and snippets.

@stewartknapman
Last active May 15, 2017 03:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stewartknapman/52b174aa62a6fb46c70a to your computer and use it in GitHub Desktop.
Save stewartknapman/52b174aa62a6fb46c70a to your computer and use it in GitHub Desktop.
Media queries but for elements rather than the window.
/*
Pollyfill for window.getComputedStyle()
*/
if (!window.getComputedStyle) {
window.getComputedStyle = function(el, pseudo) {
this.el = el;
this.getPropertyValue = function(prop) {
var re = /(\-([a-z]){1})/g;
if (prop == 'float') prop = 'styleFloat';
if (re.test(prop)) {
prop = prop.replace(re, function () {
return arguments[2].toUpperCase();
});
}
return el.currentStyle[prop] ? el.currentStyle[prop] : null;
};
return this;
};
}
/*
Utilities
*/
var u = {
ready: function (callback, ctx) {
if (typeof callback !== 'function') return;
if (document.readyState !== "loading") {
callback.apply(ctx);
} else {
document.addEventListener("DOMContentLoaded", function () {
callback.apply(ctx);
});
}
},
each: function (arr, callback, ctx) {
for (var i = 0; i < arr.length; i++) {
ctx = ctx || arr[i];
callback.apply(ctx, [arr[i], i]);
}
},
debounce: function (callback, wait) {
var timeout, timestamp, args;
wait = wait || 100;
var later = function() {
var last = new Date().getTime() - timestamp;
if (last < wait && last >= 0) {
timeout = setTimeout(later, wait - last);
} else {
timeout = null;
callback.apply(ctx, args);
}
};
return function () {
ctx = this;
args = arguments;
timestamp = new Date().getTime();
if (!timeout) timeout = setTimeout(later, wait);
};
}
};
/*
Element Queries Main
*/
var eqElementSelector = '[data-element-query]';
var ElementQuery = function (eqEle, html) {
this.eqEle = eqEle;
this.html = html;
this.queries = this.getQueries(eqEle.dataset.elementQuery);
this.addEventListeners();
this.calcElementQueries();
};
ElementQuery.prototype.addEventListeners = function () {
var _this = this;
window.addEventListener('resize', u.debounce(function () {
_this.calcElementQueries();
}));
};
ElementQuery.prototype.calcElementQueries = function () {
this.emptyCurrentQueries();
u.each(this.queries, function (query) {
this.calcElementQuery(query);
}, this);
this.applyQueries();
};
ElementQuery.prototype.calcElementQuery = function (query) {
var offsetValue = this.getOffsetValue(query.dimension);
var computedValue = this.getComputedValue(query.value, query.unit);
if (query.limit === 'min') {
if (offsetValue >= computedValue) {
this.currentQueries[query.dataKey].push(query);
}
} else if (query.limit === 'max') {
if (offsetValue <= computedValue) {
this.currentQueries[query.dataKey].push(query);
}
}
};
ElementQuery.prototype.getOffsetValue = function (dimension) {
if (dimension === 'width') {
return this.eqEle.offsetWidth;
} else if (dimension === 'height') {
return this.eqEle.offsetHeight;
}
};
ElementQuery.prototype.getComputedValue = function (value, unit) {
var computedValue,
baseFontSize;
switch (unit) {
case 'rem':
baseFontSize = this.getFontSize(this.html);
computedValue = baseFontSize * value;
break;
case 'em':
baseFontSize = this.getFontSize(this.eqEle);
computedValue = baseFontSize * value;
break;
default:
computedValue = value;
}
return computedValue;
};
ElementQuery.prototype.applyQueries = function () {
for (var dataKey in this.currentQueries) {
if (this.currentQueries.hasOwnProperty(dataKey)) {
var data = [];
var dataSet = this.currentQueries[dataKey];
u.each(dataSet, function (query) {
data.push(query.value + query.unit);
});
this.eqEle.dataset[dataKey] = data.join(' ');
}
}
}
/* Initial Setup */
ElementQuery.prototype.emptyCurrentQueries = function () {
this.currentQueries = {
minWidth: [],
maxWidth: [],
minHeight: [],
maxHeight: []
};
};
ElementQuery.prototype.getQueries = function (queryStr) {
var queries = [],
queryStrs = queryStr.split(',');
u.each(queryStrs, function (query) {
var queryObj = this.getQueryObject(query);
if (queryObj) {
queries.push(queryObj);
}
}, this);
return queries;
};
ElementQuery.prototype.getQueryObject = function (queryStr) {
var queryMatch = queryStr.match(/(min|max)-(width|height):\s?(\d*)(px|rem|em)/);
if (queryMatch) {
return {
limit: queryMatch[1],
dimension: queryMatch[2],
value: parseFloat(queryMatch[3]),
unit: queryMatch[4],
dataKey: this.getDataKey(queryMatch[1], queryMatch[2])
}
} else {
return false;
}
};
ElementQuery.prototype.getFontSize = function (ele) {
return parseFloat(window.getComputedStyle(ele, null).getPropertyValue('font-size'));
};
ElementQuery.prototype.getDataKey = function (limit, dimension) {
return limit + dimension.charAt(0).toUpperCase() + dimension.slice(1);
};
/* Wrapper for multiple objects */
var ElementQueries = function () {
var html = document.querySelector('html');
var eqElements = document.querySelectorAll(eqElementSelector);
u.each(eqElements, function (eqEle) {
new ElementQuery(eqEle, html);
});
};
/*
Run this sucker
*/
u.ready(function () {
new ElementQueries();
});
<!-- Break points work with min and max, width and height, and use either px, em or rem -->
<div class="my-ele" data-element-query="min-width: 10em, min-width: 30em">
...
</div>
.my-ele {
background-color: red;
&[data-min-width~="10em"] {
background-color: green;
}
&[data-min-width~="30em"] {
background-color: blue;
}
}
@stewartknapman
Copy link
Author

stewartknapman commented May 15, 2017

Edit 2017/05/15: Made into a standalone file.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment