Last active
April 18, 2023 04:50
-
-
Save Maximization/b68fcf4d08681c974d88d16d2beea829 to your computer and use it in GitHub Desktop.
A script that console.logs the computed sizes values of your image as you resize the window
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
<img class="testing" srcset="..." sizes="..."> | |
<script> | |
// On resize and img load events, logs: | |
// - `currentSrc` width (selected from `srcset`) | |
// - element width | |
// - parsed size value (selected from `sizes`) | |
// - a boolean indicating if the parsed size matches the element width | |
// There seem to be issues with `HTMLImageElement.natural{Width,Height}`, so we have to rely on this | |
// instead. | |
// https://bugs.chromium.org/p/chromium/issues/detail?id=760612 | |
const getSrcWidth = (src) => { | |
const t = new Image(); | |
t.src = src; | |
return t.width; | |
}; | |
const evalCalc = (calcStr) => { | |
const node = document.createElement('div'); | |
node.style.width = calcStr; | |
document.body.appendChild(node); | |
const { width } = window.getComputedStyle(node); | |
document.body.removeChild(node); | |
return width; | |
}; | |
observe = (imgEl) => { | |
const log = () => { | |
const currentSrcWidth = getSrcWidth(imgEl.currentSrc); | |
const elementWidth = imgEl.width; | |
const parsedSizeRaw = parseSizes(imgEl.sizes); | |
const parsedSize = evalCalc(parsedSizeRaw); | |
const doesParsedSizeMatchElementWidth = | |
Math.round(parseFloat(parsedSize)) === elementWidth; | |
console.log({ | |
currentSrcWidth, | |
elementWidth, | |
parsedSize, | |
doesParsedSizeMatchElementWidth, | |
}); | |
}; | |
log(); | |
imgEl.addEventListener('load', log); | |
window.addEventListener('resize', log); | |
}; | |
// https://raw.githubusercontent.com/albell/parse-sizes/master/parse-sizes.js | |
/* | |
* Sizes Parser | |
* | |
* By Alex Bell | MIT License | |
* | |
* Non-strict but accurate and lightweight JS Parser for the string value <img sizes="here"> | |
* | |
* Reference algorithm at: | |
* https://html.spec.whatwg.org/multipage/embedded-content.html#parse-a-sizes-attribute | |
* | |
* Most comments are copied in directly from the spec | |
* (except for comments in parens). | |
* | |
* Grammar is: | |
* <source-size-list> = <source-size># [ , <source-size-value> ]? | <source-size-value> | |
* <source-size> = <media-condition> <source-size-value> | |
* <source-size-value> = <length> | |
* http://www.w3.org/html/wg/drafts/html/master/embedded-content.html#attr-img-sizes | |
* | |
* E.g. "(max-width: 30em) 100vw, (max-width: 50em) 70vw, 100vw" | |
* or "(min-width: 30em), calc(30em - 15px)" or just "30vw" | |
* | |
* Returns the first valid <css-length> with a media condition that evaluates to true, | |
* or "100vw" if all valid media conditions evaluate to false. | |
* | |
*/ | |
function parseSizes(strValue) { | |
// (Percentage CSS lengths are not allowed in this case, to avoid confusion: | |
// https://html.spec.whatwg.org/multipage/embedded-content.html#valid-source-size-list | |
// CSS allows a single optional plus or minus sign: | |
// http://www.w3.org/TR/CSS2/syndata.html#numbers | |
// CSS is ASCII case-insensitive: | |
// http://www.w3.org/TR/CSS2/syndata.html#characters ) | |
// Spec allows exponential notation for <number> type: | |
// http://dev.w3.org/csswg/css-values/#numbers | |
var regexCssLengthWithUnits = | |
/^(?:[+-]?[0-9]+|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?(?:ch|cm|em|ex|in|mm|pc|pt|px|rem|vh|vmin|vmax|vw)$/i, | |
// (This is a quick and lenient test. Because of optional unlimited-depth internal | |
// grouping parens and strict spacing rules, this could get very complicated.) | |
regexCssCalc = /^calc\((?:[0-9a-z \.\+\-\*\/\(\)]+)\)$/i, | |
i, | |
unparsedSizesList, | |
unparsedSizesListLength, | |
unparsedSize, | |
lastComponentValue, | |
size; | |
// UTILITY FUNCTIONS | |
// ( Manual is faster than RegEx.) | |
// http://jsperf.com/whitespace-character/5 | |
function isSpace(c) { | |
return ( | |
c === '\u0020' || // space | |
c === '\u0009' || // horizontal tab | |
c === '\u000A' || // new line | |
c === '\u000C' || // form feed | |
c === '\u000D' | |
); // carriage return | |
} | |
// (Toy CSS parser. The goals here are: | |
// 1) expansive test coverage without the weight of a full CSS parser. | |
// 2) Avoiding regex wherever convenient. | |
// Quick tests: http://jsfiddle.net/gtntL4gr/3/ | |
// Returns an array of arrays.) | |
function parseComponentValues(str) { | |
var chrctr, | |
component = '', | |
componentArray = [], | |
listArray = [], | |
parenDepth = 0, | |
pos = 0, | |
inComment = false; | |
function pushComponent() { | |
if (component) { | |
componentArray.push(component); | |
component = ''; | |
} | |
} | |
function pushComponentArray() { | |
if (componentArray[0]) { | |
listArray.push(componentArray); | |
componentArray = []; | |
} | |
} | |
// (Loop forwards from the beginning of the string.) | |
while (true) { | |
chrctr = str.charAt(pos); | |
if (chrctr === '') { | |
// ( End of string reached.) | |
pushComponent(); | |
pushComponentArray(); | |
return listArray; | |
} else if (inComment) { | |
if (chrctr === '*' && str.charAt(pos + 1) === '/') { | |
// (At end of a comment.) | |
inComment = false; | |
pos += 2; | |
pushComponent(); | |
continue; | |
} else { | |
pos += 1; // (Skip all characters inside comments.) | |
continue; | |
} | |
} else if (isSpace(chrctr)) { | |
// (If previous character in loop was also a space, or if | |
// at the beginning of the string, do not add space char to | |
// component.) | |
if ( | |
(str.charAt(pos - 1) && isSpace(str.charAt(pos - 1))) || | |
!component | |
) { | |
pos += 1; | |
continue; | |
} else if (parenDepth === 0) { | |
pushComponent(); | |
pos += 1; | |
continue; | |
} else { | |
// (Replace any space character with a plain space for legibility.) | |
chrctr = ' '; | |
} | |
} else if (chrctr === '(') { | |
parenDepth += 1; | |
} else if (chrctr === ')') { | |
parenDepth -= 1; | |
} else if (chrctr === ',' && parenDepth === 0) { | |
pushComponent(); | |
pushComponentArray(); | |
pos += 1; | |
continue; | |
} else if (chrctr === '/' && str.charAt(pos + 1) === '*') { | |
inComment = true; | |
pos += 2; | |
continue; | |
} | |
component = component + chrctr; | |
pos += 1; | |
} | |
} | |
function isValidNonNegativeSourceSizeValue(s) { | |
if (regexCssLengthWithUnits.test(s) && parseFloat(s) >= 0) { | |
return true; | |
} | |
if (regexCssCalc.test(s)) { | |
return true; | |
} | |
// ( http://www.w3.org/TR/CSS2/syndata.html#numbers says: | |
// "-0 is equivalent to 0 and is not a negative number." which means that | |
// unitless zero and unitless negative zero must be accepted as special cases.) | |
if (s === '0' || s === '-0' || s === '+0') { | |
return true; | |
} | |
return false; | |
} | |
// When asked to parse a sizes attribute from an element, parse a | |
// comma-separated list of component values from the value of the element's | |
// sizes attribute (or the empty string, if the attribute is absent), and let | |
// unparsed sizes list be the result. | |
// http://dev.w3.org/csswg/css-syntax/#parse-comma-separated-list-of-component-values | |
unparsedSizesList = parseComponentValues(strValue); | |
unparsedSizesListLength = unparsedSizesList.length; | |
// For each unparsed size in unparsed sizes list: | |
for (i = 0; i < unparsedSizesListLength; i++) { | |
unparsedSize = unparsedSizesList[i]; | |
// 1. Remove all consecutive <whitespace-token>s from the end of unparsed size. | |
// ( parseComponentValues() already omits spaces outside of parens. ) | |
// If unparsed size is now empty, that is a parse error; continue to the next | |
// iteration of this algorithm. | |
// ( parseComponentValues() won't push an empty array. ) | |
// 2. If the last component value in unparsed size is a valid non-negative | |
// <source-size-value>, let size be its value and remove the component value | |
// from unparsed size. Any CSS function other than the calc() function is | |
// invalid. Otherwise, there is a parse error; continue to the next iteration | |
// of this algorithm. | |
// http://dev.w3.org/csswg/css-syntax/#parse-component-value | |
lastComponentValue = unparsedSize[unparsedSize.length - 1]; | |
// if (isValidNonNegativeSourceSizeValue(lastComponentValue)) { | |
if (true) { | |
size = lastComponentValue; | |
unparsedSize.pop(); | |
} else if (window.console && console.log) { | |
console.log('Parse error: ' + strValue); | |
continue; | |
} | |
// 3. Remove all consecutive <whitespace-token>s from the end of unparsed | |
// size. If unparsed size is now empty, return size and exit this algorithm. | |
// If this was not the last item in unparsed sizes list, that is a parse error. | |
if (unparsedSize.length === 0) { | |
if ( | |
i !== unparsedSizesListLength - 1 && | |
window.console && | |
console.log | |
) { | |
console.log('Parse error: ' + strValue); | |
} | |
return size; | |
} | |
// 4. Parse the remaining component values in unparsed size as a | |
// <media-condition>. If it does not parse correctly, or it does parse | |
// correctly but the <media-condition> evaluates to false, continue to the | |
// next iteration of this algorithm. | |
// (Parsing all possible compound media conditions in JS is heavy, complicated, | |
// and the payoff is unclear. Is there ever an situation where the | |
// media condition parses incorrectly but still somehow evaluates to true? | |
// Can we just rely on the browser/polyfill to do it?) | |
unparsedSize = unparsedSize.join(' '); | |
if (!window.matchMedia(unparsedSize).matches) { | |
continue; | |
} | |
// 5. Return size and exit this algorithm. | |
return size; | |
} | |
// If the above algorithm exhausts unparsed sizes list without returning a | |
// size value, return 100vw. | |
return '100vw'; | |
} | |
const [img] = document.getElementsByClassName('testing'); | |
img && observe(img); | |
</script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment