Skip to content

Instantly share code, notes, and snippets.

@tomhodgins
Last active August 26, 2020 21:08
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomhodgins/d7e8a16c93e47613e5625878f73363d5 to your computer and use it in GitHub Desktop.
Save tomhodgins/d7e8a16c93e47613e5625878f73363d5 to your computer and use it in GitHub Desktop.
Aspect-ratio polyfill using syntax `-eq-aspect-ratio: ;`
<h1>Aspect Ratio Polyfill</h1>
<h2>Demo</h2>
<div id=demo1>ratio alone</div>
<div id=demo2>width + ratio</div>
<div id=demo3>height + ratio</div>
<div id=demo4>width + height + ratio</div>
<div id=demo5>important means nothing</div>
<h2>Goal</h2>
<p>To create an aspect ratio polyfill for CSS using <code>-eq-aspect-ratio</code> as a property name</p>
<h2>Requirements</h2>
<ul>
<li>css selector (multiple separated with comma)
<li>optional whitespace
<li>{ character
<li>any amount of whitespace or other properties
<li>-eq-aspect-ratio
<li>any amount of whitespace
<li>: character
<li> any amount of whitespace
<li> digit with optional decimal and optional digits
<li>/ character
<li>digit with optional . and extra digits
<li>any amount of whitespace
<li>optional !important
<li>any amount of whitespace
<li>} character
</ul>
<p>Once we have a match for -eq-aspect-ratio we need to know the following things:</p>
<ul>
<li>the selector in question
<li>if there is a width or height present in the rule <strong>or</strong> if one is inherited from another rule
<li>the elements width and height as rendered on the page
<li>how frequently this needs to be applied to the element
</ul>
<h2>Logic</h2>
<p>In order to calculate and aspect ratio we require 2/3 of the following pieces of information:</p>
<ul>
<li>specified width
<li>specified height
<li>desired aspect ratio
</ul>
<p>However, if all three pieces are present there should be a clear order of precedence for which rule applies.</p>
<ul>
<li>ratio = calculated height
<li>width + ratio = calculated height
<li>height + ratio = calculated width
<li>width + height = natutal aspect ratio
<li>width + height + ratio = natural aspect ratio
<li>width + height + ratio !important = natural aspect ratio
</ul>
<h2>Syntax</h2>
<p><code>aspect ratio property</code> = <code>property name</code> : <code>width</code> / <code>height</code></p>
<p><code>property name</code> = -eq-aspect-ratio</p>
<p><code>width</code> = number with optional decmial + optional trailing digits</p>
<p><code>height</code> = number with optional decmial + optional trailing digits</p>
<h2>Names</h2>
<ul>
<li>rational
<li>classpect
</ul>
<style>
div {
background: lime;
margin: 1em 0;
}
#demo1 {
-eq-aspect-ratio: 16/9;
}
#demo2 {
width: 200px;
-eq-aspect-ratio: 16/9;
}
#demo3 {
height: 200px;
-eq-aspect-ratio: 16/9;
}
#demo4 {
width: 200px;
height: 200px;
-eq-aspect-ratio: 16/9;
}
#demo5 {
width: 200px;
height: 200px;
-eq-aspect-ratio: 16/9 !important;
}
</style>
<script>
var styles = [],
rules = [],
elements = []
function load(){
// Load <style> tags
var tag = document.querySelectorAll('style')
for (var i=0;i<tag.length;i++){
styles.push(tag[i].innerHTML)
}
// load <link> tags
var tag = document.querySelectorAll('link')
for (var i=0;i<tag.length;i++){
if (tag[i].getAttribute('rel') == 'stylesheet'){
if (tag[i].href){
(function(){
var xhr = new XMLHttpRequest
xhr.open('GET', tag[i].href, true)
xhr.send(null)
xhr.onload = function(){
styles.push(xhr.responseText)
parse()
}
})();
}
}
}
parse()
}
function parse(){
console.log(styles)
for (var i=0;i<styles.length;i++){
var css = new String(styles[i])
css = css.replace(/\n/g,'')
css = css.replace(/\}/g,'}\n')
css = css.replace(/\s*{/g,'{')
css = css.replace(/^\s*/gm,'')
css.replace(/^(.*?)\s?{.*-eq-aspect-ratio:\s*(\d*\.?\d+)\/(\d*\.?\d+)/gm,function(match,$1,$2,$3){
var rule = [$1,$2,$3]
rules.push(rule)
})
}
apply()
}
function apply(){
for (var i=0;i<rules.length;i++){
var tag = document.querySelectorAll(rules[i][0])
for (var j=0;j<tag.length;j++){
var natWidth = getRule(tag[j],'width') || false,
natHeight = getRule(tag[j],'height') || false,
currWidth = tag[j].offsetWidth,
currHeight = tag[j].offsetHeight,
rWidth = rules[i][1],
rHeight = rules[i][2]
if (!natWidth && !natHeight){
tag[j].style.height = currWidth / (rWidth/rHeight) + 'px'
}
if (natWidth && !natHeight){
tag[j].style.height = currWidth / (rWidth/rHeight) + 'px'
}
if (!natWidth && natHeight){
tag[j].style.width = currHeight * (rWidth/rHeight) + 'px'
}
}
}
}
function getRule(tag,query){
var sheets = document.styleSheets,
matchedRules = []
if (!tag.matches) {
tag.matches =
tag.matchesSelector
|| tag.mozMatchesSelector
|| tag.msMatchesSelector
|| tag.oMatchesSelector
|| webkitMatchesSelector
|| function(s){
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
m = matches.length
while (--m >= 0 && matches.item(m) !== this) {}
return m > -1
}
}
for (var i=0; i<sheets.length; i++) {
var css = sheets[i].rules || sheets[i].cssRules
for (var j=0; j<css.length; j++){
if (tag.matches(css[j].selectorText)){
if (css[j].cssText.indexOf(query) !== -1) {
return true
}
}
}
}
}
document.addEventListener('DOMContentLoaded',load)
window.addEventListener('resize',apply)
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment