Last active
August 26, 2020 21:08
-
-
Save tomhodgins/d7e8a16c93e47613e5625878f73363d5 to your computer and use it in GitHub Desktop.
Aspect-ratio polyfill using syntax `-eq-aspect-ratio: ;`
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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