Skip to content

Instantly share code, notes, and snippets.

@fgnass
Created January 15, 2025 14:02
Show Gist options
  • Save fgnass/4afc16577636d66fff4e0336e9de790a to your computer and use it in GitHub Desktop.
Save fgnass/4afc16577636d66fff4e0336e9de790a to your computer and use it in GitHub Desktop.
Generate sizes attributes for all responsive images on a page.
// Quick helper to generate sizes attributes for your responsive images.
// It loads the current page into an invisible iframe and resizes it to the different breakpoints,
// where it measures the images and converts their width to vw.
// Usage: Open the HTML page you want to add sizes attributes to in your browser,
// paste this code in the console and hit return. The result will be logged to the console.
(async () => {
const sizesData = new Map();
const sizeThreshold = 0.1; // Threshold for grouping similar sizes (in vw)
// Define breakpoints
const breakpoints = [640, 768, 1024, 1280, 1536];
const viewportWidths = [];
// Also probe at +/- 1px
breakpoints.forEach((bp) => {
viewportWidths.push(bp - 1, bp, bp + 1);
});
// Create an iframe to load the site
const iframe = document.createElement('iframe');
iframe.style.position = 'fixed';
iframe.style.top = 0;
iframe.style.left = 0;
iframe.style.width = '100%';
iframe.style.height = '100vh';
iframe.style.visibility = 'hidden'; // Keep iframe hidden
document.body.appendChild(iframe);
// Load the current page into the iframe
iframe.src = window.location.href;
await new Promise((resolve) => {
iframe.onload = resolve;
});
const doc = iframe.contentDocument || iframe.contentWindow.document;
console.log("Starting measurements...");
// Function to measure and record sizes
const measureSizes = (viewportWidth) => {
iframe.style.width = `${viewportWidth}px`;
const images = doc.querySelectorAll('img');
images.forEach((img) => {
const containerWidth = img.parentElement.offsetWidth;
const percentage = parseFloat(((containerWidth / viewportWidth) * 100).toFixed(2));
const key = img.src || img.outerHTML;
if (!sizesData.has(key)) {
sizesData.set(key, []);
}
const sizesArray = sizesData.get(key);
if (!sizesArray.some((item) => item.viewport === viewportWidth)) {
sizesArray.push({ viewport: viewportWidth, size: percentage });
}
});
};
// Iterate through viewport widths and measure
for (let i = 0; i < viewportWidths.length; i++) {
const width = viewportWidths[i];
console.log(`Measuring at width: ${width}px (${i + 1}/${viewportWidths.length})`);
measureSizes(width);
await new Promise((resolve) => setTimeout(resolve, 100)); // Wait for layout to stabilize
}
// Remove the iframe
iframe.remove();
console.log("Measurement complete. Processing results...");
// Group images by identical `sizes` attributes
const groupedData = new Map();
sizesData.forEach((sizesArray, key) => {
// Filter and group sizes by skipping insignificant changes
const filteredSizes = [];
for (let i = 0; i < sizesArray.length; i++) {
if (
i === 0 ||
Math.abs(sizesArray[i].size - sizesArray[i - 1].size) >= sizeThreshold
) {
filteredSizes.push(sizesArray[i]);
}
}
// Consolidate ranges for clean output
const sizesString = filteredSizes
.map((entry, index) => {
if (index === 0 || filteredSizes[index - 1].viewport + 1 !== entry.viewport) {
return `(max-width: ${entry.viewport}px) ${entry.size}vw`;
}
return null; // Skip redundant entries
})
.filter(Boolean)
.join(', ');
if (!groupedData.has(sizesString)) {
groupedData.set(sizesString, []);
}
groupedData.get(sizesString).push(key);
});
// Format and output the grouped results
let output = '';
groupedData.forEach((keys, sizesString) => {
output += `sizes="${sizesString}"\n`;
keys.forEach((key) => {
output += ` - ${key}\n`;
});
output += '\n';
});
console.log("Results:");
console.log(output); // Log the output for easy copy-paste
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment