VOOZH about

URL: https://dev.to/petr_savenok/cleaning-up-web-pages-for-screenshots-a-practical-puppeteer-guide-16c1

⇱ Cleaning up web pages for screenshots — a practical Puppeteer guide - DEV Community


If you've ever taken a screenshot of a real-world webpage with Puppeteer, you've seen the ugly truth: cookie banners, sticky headers frozen at weird positions, collapsed FAQ accordions, and "Subscribe!" popups.

Here are four small Puppeteer helpers I use to get clean, presentable screenshots. Each is one function, drop-in ready.

1. Kill cookie banners

async function hideCookieBanners(page, customSelectors = []) {
 await page.evaluate((selectors) => {
 // Known cookie-banner elements
 ['CookieReportsPanel', 'CookieReportsOverlay', 'CookieReportsBannerAZ']
 .forEach(id => document.getElementById(id)?.remove());

 document.querySelectorAll('[class*="wscr"],[id*="CookieReport"]')
 .forEach(el => el.remove());

 // Reset body scroll locks the banner often sets
 document.body.style.overflow = '';
 document.documentElement.style.overflow = '';

 // User-defined selectors (if they know their site)
 selectors.forEach(sel => {
 try {
 document.querySelectorAll(sel).forEach(el => {
 el.style.setProperty('display', 'none', 'important');
 });
 } catch {}
 });
 }, customSelectors);
}

2. Hide arbitrary elements

async function hideElements(page, selectors) {
 if (!selectors.length) return;
 await page.evaluate(sels => {
 sels.forEach(sel => {
 try {
 document.querySelectorAll(sel).forEach(el => {
 el.style.setProperty('display', 'none', 'important');
 });
 } catch {}
 });
 }, selectors);
}

Example: hide chat widgets, "cookies" bars, floating "Get started" buttons:

await hideElements(page, [
 '#intercom-container',
 '.crisp-client',
 '[class*="newsletter-popup"]'
]);

3. Unfix sticky headers

Sticky headers repeat on every "screen" of a fullPage screenshot — ugly. Convert them to position: relative:

async function unfixSticky(page, selectors) {
 await page.evaluate(sels => {
 sels.forEach(sel => {
 try {
 document.querySelectorAll(sel).forEach(el => {
 el.style.setProperty('position', 'relative', 'important');
 ['top', 'bottom', 'left', 'right', 'z-index']
 .forEach(p => el.style.setProperty(p, 'auto', 'important'));
 el.style.setProperty('width', '100%', 'important');
 });
 } catch {}
 });
 }, selectors);
}

4. Expand FAQs / accordions

Most FAQs use classes like _active or attributes like open. Give the function a CSS selector + action + value:

async function expandAccordions(page, pairs) {
 if (!pairs.length) return;
 await page.evaluate(rules => {
 rules.forEach(({ selector, action, value }) => {
 try {
 document.querySelectorAll(selector).forEach(el => {
 if (action === 'class') {
 el.classList.add(value || '_active');
 // Also unhide next sibling (common FAQ pattern)
 const next = el.nextElementSibling;
 if (next) {
 next.style.display = 'block';
 next.style.maxHeight = 'none';
 next.removeAttribute('hidden');
 }
 } else if (action === 'attribute') {
 const [name, val = ''] = value.split('=');
 el.setAttribute(name, val);
 } else if (action === 'style') {
 value.split(';').forEach(rule => {
 const [p, v] = rule.split(':');
 if (p && v) {
 el.style.setProperty(p.trim(), v.trim(), 'important');
 }
 });
 }
 });
 } catch {}
 });
 }, pairs);

 // Wait for CSS transitions
 await new Promise(r => setTimeout(r, 400));
}

Example usage:

await expandAccordions(page, [
 { selector: '.faq__question', action: 'class', value: '_active' },
 { selector: 'details', action: 'attribute', value: 'open=true' },
 { selector: '.accordion-body', action: 'style', value: 'display: block' }
]);

Putting it together

await page.goto(url, { waitUntil: 'domcontentloaded' });
await hideCookieBanners(page);
await hideElements(page, ['.chat-widget']);
await unfixSticky(page, ['.sticky-nav']);
await expandAccordions(page, [{ selector: '.faq__q', action: 'class', value: '_open' }]);
await page.screenshot({ path: 'clean.png', fullPage: true });

Where I use this

All four helpers ship in production on Site2PDF — a website-to-PDF tool I'm building. Users paste a URL, tick what to hide/expand, and get a clean archive.

If you're doing any kind of web scraping or automated screenshots, grab these — they'll save you a weekend of CSS archaeology.