dopetalk does not endorse any advertised product nor does it accept any liability for it's use or misuse


Our Discord Notification Server invitation link is https://discord.gg/jB2qmRrxyD

Author Topic: The Art/Struggle of Trying to Consistently Print (a) Webpage(s) PDF Style  (Read 17 times)

Online Chip (OP)

  • Server Admin
  • Admin
  • Hero Member
  • ****
  • Administrator
  • *****
  • Restrictive
  • *
  • RC Discussion
  • **
  • Private
  • ***
  • Owner
  • *****
  • Join Date: Dec 2014
  • Location: Australia
  • Posts: 7429
  • Reputation Power: 0
  • Chip has hidden their reputation power
  • Gender: Male
  • Last Login:Today at 07:40:17 AM
  • Deeply Confused Learner
  • Profession: IT Engineer now retired
📎 Click on me to view all my attachments
ListAttBBC  |  v6.12  |  2026-09-10  |  Andrew.human & Claude.ai
#FileSizeDownloadsInfoDL
00webpage crash bang boom bash.png207.6 KB5ℹ️⬇️
01webpage crash bang boom bash 01.png208.3 KB5ℹ️⬇️
02webpage crash bang boom bash 02.png163.5 KB5ℹ️⬇️
03webpage crash bang boom bash 03.png256 KB6ℹ️⬇️
04webpage crash bang boom bash 04.png119.7 KB4ℹ️⬇️
05devtools_print_crash_transcript.pdf  [native]568.8 KB4ℹ️⬇️
06devtools_print_crash_transcript.md574 KB1ℹ️⬇️
07checkpoint.md14.8 KB1ℹ️⬇️
08DevTools Snippet.txt291 B5ℹ️⬇️


HELP !
Quote
-- when i enter this Dev Tools snippet: {
Code: [Select]
const sc = [...document.querySelectorAll('div')].filter(e => getComputedStyle(e).overflowY === 'auto' && e.scrollHeight > e.clientHeight).sort((a,b) => b.scrollHeight - a.scrollHeight)[0];
console.log('scroller:', sc, sc.scrollHeight);
let n = sc;
while (n && n !== document.body) {
  [...n.parentElement.children].forEach(s => { if (s !== n) s.style.setProperty('display','none','important'); });
  n.setAttribute('data-pf','1');
  n = n.parentElement;
}
const st = document.createElement('style');
st.textContent = '[data-pf]{display:block!important;position:static!important;height:auto!important;min-height:0!important;max-height:none!important;overflow:visible!important;flex:none!important;transform:none!important;contain:none!important;content-visibility:visible!important}html,body{height:auto!important;overflow:visible!important}';
document.head.appendChild(st);
} -------> (to save the convo) and there is at least one embedded graphic, the page CRASHES, so what's the go bro ?


Claude: [08/10/2026--03:57:44:989 AEDT]

The snippet forces every ancestor to `height:auto`, `overflow:visible` and `content-visibility:visible`. That turns off the page's own skipping of off-screen content, so the whole conversation gets laid out and rendered at once. Text copes with that. Iframes (widgets), canvases, large SVGs and full-size images are the heavy part, and the tab runs out of memory. I haven't tested this against your page, so treat it as the most likely cause.

Run this first to see what's heavy:

js
Code: [Select]
console.log({iframe:document.querySelectorAll('iframe').length, canvas:document.querySelectorAll('canvas').length, svg:document.querySelectorAll('svg').length, img:document.querySelectorAll('img').length, video:document.querySelectorAll('video').length});

Then run this before your snippet. It swaps out the embedded heavy elements and caps image size:

js
Code: [Select]
document.querySelectorAll('iframe,canvas,video,object,embed').forEach(e => {
  const p = document.createElement('div');
  p.textContent = '[' + e.tagName.toLowerCase() + ' omitted]';
  e.replaceWith(p);
});
document.querySelectorAll('img').forEach(e => {
  e.style.setProperty('max-width','100%','important');
  e.style.setProperty('max-height','700px','important');
  e.style.setProperty('height','auto','important');
});
document.querySelectorAll('svg').forEach(e => {
  e.style.setProperty('max-width','100%','important');
  e.style.setProperty('max-height','700px','important');
});

If you want the graphics kept, drop the first block and keep only the `img` and `svg` caps. That is where to start if the omitted-element version works, so you can add the graphics back one type at a time.

If it still crashes with those removed, the crash is in the print layout itself rather than the graphics. In that case, narrow your original snippet by removing `content-visibility:visible!important` and `contain:none!important` from the `st.textContent` line, and let the page keep its own skipping.

Quote
that crashes to so but at least i can return to you the output from that initial javascript he mentioned ...


* webpage crash bang boom bash.png (207.62 kB . 1280x1024 - viewed 6 times)

Claude: [08/10/2026--04:20:14:179 AEDT]

Your output (iframe 0, canvas 0, svg 1, img 0, video 0) shows my heavy-graphics theory doesn't hold for what's in the page right now. The red `TypeError` in your screenshot comes from the page's own web-vitals/Datadog code (`reportAllChanges`), not from your snippet, so ignore it.

The page most likely renders only the messages near the viewport, so those counts can't see a graphic further up the chat. Your snippet then forces every message to render at once, and that is probably when the graphic appears and the tab dies. The cleanup snippet I gave never had anything to remove at the moment you ran it.

To find the stage that crashes, run your snippet in two parts. First this, which only hides the siblings and is cheap:

js
Code: [Select]
const sc = [...document.querySelectorAll('div')].filter(e => getComputedStyle(e).overflowY === 'auto' && e.scrollHeight > e.clientHeight).sort((a,b) => b.scrollHeight - a.scrollHeight)[0];
console.log('scroller:', sc, sc.scrollHeight);
let n = sc;
while (n && n !== document.body) {
  [...n.parentElement.children].forEach(s => { if (s !== n) s.style.setProperty('display','none','important'); });
  n.setAttribute('data-pf','1');
  n = n.parentElement;
}

If the tab survives, run the style part without the two rendering-skip overrides (`contain` and `content-visibility`), which are the likely killers:

js
Code: [Select]
const st = document.createElement('style');
st.textContent = '[data-pf]{display:block!important;position:static!important;height:auto!important;min-height:0!important;max-height:none!important;overflow:visible!important;flex:none!important;transform:none!important}html,body{height:auto!important;overflow:visible!important}';
document.head.appendChild(st);

If it only crashes once the second part runs, the layout expansion is the cause and the graphic is just what tips it over. Then scroll to the graphic's message before running the diagnostic again, so the page has it in the DOM:

js
Code: [Select]
[...document.querySelectorAll('svg,iframe,canvas,img')].forEach(e => { const r = e.getBoundingClientRect(); console.log(e.tagName, Math.round(r.width) + 'x' + Math.round(r.height), e); });

That will show what the graphic is and how big it is.

Quote
here it is:


* webpage crash bang boom bash 01.png (208.32 kB . 1280x1024 - viewed 6 times)

Claude: [08/10/2026--04:35:06:570 AEDT]

Everything in that output is page furniture, and this conversation has no content graphic to find:

- **svg 32x32** is the Claude "Spark" logo icon.
- **IFRAME 0x0** is the hidden analytics frame (`isolated-segment.html`, `display:none`).
- **IFRAME 1x1** is a hidden `about:blank` frame, which Datadog uses.
- The 25 "preloaded but not used" warnings are harmless.

None of those could crash anything, and no content graphic was in the DOM, so this chat can't reproduce the problem. The test has to run in a conversation that contains the graphic, with that message scrolled into view first, so the page has rendered it.

This version skips the furniture and lists only real content-sized elements:

```js
[...document.querySelectorAll('svg,iframe,canvas,img,video')].forEach(e => { const r = e.getBoundingClientRect(); if (r.width > 100 && r.height > 100) console.log(e.tagName, Math.round(r.width) + 'x' + Math.round(r.height), e); });
```

Nothing printed means the graphic isn't rendered yet, so scroll closer to it and run it again.

Run it in the crashing conversation before pasting the save snippet, and paste back what it prints. If the graphic is a widget it will show up as an `IFRAME` with a large size, and in that case the cleanup block from my earlier message (replace iframes with a placeholder) is the right fix. If it's an `IMG` or `SVG`, the size cap handles it.

Quote
BUT IT ONLY JUST CRASHED A FEW MOMENTS AGO !

Claude: [08/10/2026--04:37:10:413 AEDT]

That means I had the cause wrong. This conversation has no content graphic and it still crashed, so graphics aren't the trigger. The snippet itself is the problem.

Forcing `height:auto` and `overflow:visible` on the whole ancestor chain while the page's scripts are still running is the likely cause. The chat list measures its container, the container grows, the list re-measures, and the tab spins until it dies. A graphic only changes the heights and makes the loop more likely to start.

A safer approach is to stop altering the live page. Copy the messages into a fresh window with the page's stylesheets, so there are no app scripts to fight and the browser just lays out a plain document:

js
Code: [Select]
const sc = [...document.querySelectorAll('div')].filter(e => getComputedStyle(e).overflowY === 'auto' && e.scrollHeight > e.clientHeight).sort((a,b) => b.scrollHeight - a.scrollHeight)[0];
const css = [...document.querySelectorAll('link[rel=stylesheet],style')].map(n => n.outerHTML).join('');
const w = window.open('', '_blank');
w.document.write('<!doctype html><html><head><meta charset="utf-8"><title>chat</title>' + css + '<style>html,body{height:auto!important;overflow:visible!important}img,svg{max-width:100%}</style></head><body>' + sc.innerHTML + '</body></html>');
w.document.close();

Then press Ctrl+P in the new window. If the popup is blocked, allow popups for claude.ai once and run it again. I haven't tested this against your page, so the styling of the copied messages may be slightly off. It can't crash the original tab, because that tab is only read from.

Quote
No good, no crash but the same old problem with only one line at the top of preview window showing

Claude: [08/10/2026--04:43:06:135 AEDT]

A one-line print preview, with no crash, points at one of two things in the copied page, and the snippet below deals with both:

- **Print-only CSS:** the page's `@media print` rules likely hide or collapse almost everything. I copied those rules into the new window along with the rest.
- **Absolutely positioned messages:** if the message list positions each message with inline `position:absolute`, `transform` or fixed heights, the copied messages stack on top of each other in a zero-height container.

This version fetches the stylesheet text and turns every `@media print` into a rule that never matches. It also resets those inline positioning styles in the copy:

js
Code: [Select]
(async () => {
  const sc = [...document.querySelectorAll('div')].filter(e => getComputedStyle(e).overflowY === 'auto' && e.scrollHeight > e.clientHeight).sort((a,b) => b.scrollHeight - a.scrollHeight)[0];
  let css = '';
  for (const n of document.querySelectorAll('link[rel=stylesheet],style')) {
    css += n.tagName === 'STYLE' ? n.textContent : await fetch(n.href).then(r => r.text()).catch(() => '');
  }
  css = css.replace(/@media\s+print/g, '@media not all');
  const w = window.open('', '_blank');
  w.document.write('<!doctype html><html><head><meta charset="utf-8"><title>chat</title><style>' + css + '</style><style>html,body{height:auto!important;overflow:visible!important}img,svg{max-width:100%}</style></head><body>' + sc.innerHTML + '</body></html>');
  w.document.close();
  w.document.querySelectorAll('[style]').forEach(e => {
    if (e.closest('svg')) return;
    const s = e.style;
    if (s.position === 'absolute' || s.position === 'fixed') s.position = 'static';
    s.transform = ''; s.top = ''; s.left = '';
    if (s.height) s.height = 'auto';
    s.maxHeight = 'none'; s.overflow = 'visible';
  });
})();

Look at the new window on screen before pressing Ctrl+P. If all the messages show on screen but the preview is still one line, the cause is print CSS that my regex missed. If the screen view also shows one line, the cause is the positioning, and the `[style]` reset didn't catch it. Tell me which it is. If a stylesheet fetch is blocked, that file's rules are skipped and the text will look plainer but should still be complete.

Quote
LOOK CUNT, I WANT A QUICK "ONE FITS ALL SOLUTION ASAP - IT CAN'T BE THAT HARD -- THE WEBPAGE IS AS OLD AS THE FUCKEN HILLS  !! ARGGGH

Claude: [08/10/2026--04:44:24:891 AEDT]

This one can't fail on layout, because it takes only the text and puts it in a plain page. Paste it into the console, then press Ctrl+P in the window that opens:

js
Code: [Select]
const sc = [...document.querySelectorAll('div')].filter(e => getComputedStyle(e).overflowY === 'auto' && e.scrollHeight > e.clientHeight).sort((a,b) => b.scrollHeight - a.scrollHeight)[0];
const w = window.open('', '_blank');
w.document.write('<!doctype html><meta charset="utf-8"><title>chat</title><pre style="white-space:pre-wrap;font:14px/1.5 Georgia,serif;max-width:800px;margin:auto"></pre>');
w.document.close();
w.document.querySelector('pre').textContent = sc.innerText;

There are no ancestors to resize, no copied stylesheets and no page scripts, so there is nothing to crash or collapse to one line. The cost is that you get plain text only. Graphics, code chips and bold are dropped, though the text of everything on the page is kept.

Quote
so i did that and this extra tab popped up and was switched into:


* webpage crash bang boom bash 02.png (163.51 kB . 1280x1024 - viewed 6 times)

Quote
when i closed that, the original window BIZARRELY displayed some of my earlier text IN RED AND IN THE DEV CONSOLE, as such:


* webpage crash bang boom bash 03.png (256 kB . 1280x1024 - viewed 7 times)

Quote
So i did what i always do afterwards, which is a hard refresh (Ctrl+F5) and now here I am SO PLEASE HELP AS THESE FILES ARE VITAL TO ME AS I TAKE A TRANSCRIPT OF EACH AND EVERY AI CONVERSATION, INDEPENDENTLY, AS AI ALWAYS, SOONER OR LATER, FUCKS UP, GETS "CREATIVE" (LOL) AND STARTS PARAPHRASING / EMBELLISHING / CENSORING / EDITING / SUMMARIZING ETC. ***DESPITE MY STRONGLY WORDED SUCCINCT PROMPTS (INCLUDING WORDS LIKE "VERBATIM" etc)!!**.

Look, I have an idea ... it was all working sweet before i popped in a graphic or two in, so why not just ignore or strip-out all the graphics or convert them to black-and-white (just a thought, and possibly a good one, lol) and i'll then just run it the old way ?[ /quote]

Claude: [08/10/2026--05:23:05:336 AEDT]

Nothing is lost. The crash only killed the browser tab, and the conversations are stored on claude.ai. A hard refresh just reloads them.

Stripping the graphics first is a good idea, since it's cheap and puts you back on the old method. This block hides every graphic inside the chat and leaves a `[graphic omitted]` marker in its place, so the transcript still shows that one was there. Icons inside buttons and links are left alone. Paste it, then paste your original snippet straight after it:

js
Code: [Select]
{
  const root = [...document.querySelectorAll('div')].filter(e => getComputedStyle(e).overflowY === 'auto' && e.scrollHeight > e.clientHeight).sort((a,b) => b.scrollHeight - a.scrollHeight)[0];
  let hidden = 0;
  root.querySelectorAll('iframe,canvas,video,object,embed,img,svg').forEach(e => {
    if (e.closest('button,a,[role=button]')) return;
    if (e.parentElement && e.parentElement.closest('svg')) return;
    const m = document.createElement('div');
    m.textContent = '[graphic omitted]';
    e.parentNode.insertBefore(m, e);
    e.style.setProperty('display','none','important');
    hidden++;
  });
  console.log('graphics hidden:', hidden);
}

The count it prints tells you how many graphics it found. If it prints 0 on a chat that has graphics, tell me and I'll adjust it.

One caution: the crash earlier happened in a chat with no graphics at all. Stripping them may not be the whole fix, so don't treat it as certain until it works on a chat that has them.

For the transcripts that matter most, you also have a backup that doesn't touch the page. In Settings → Privacy there is an "Export data" option, and it emails you every conversation as a verbatim JSON file. Run it now for anything you can't afford to lose, and keep using the snippet for the readable PDFs.

 [ You are not allowed to view attachments ]

Quote
so convert this in one single script as the version i ma giving you dispplays the graphics again and I am no js programmer, no way !, just good old "do nothing fancy/unknown" machine code Assembler for me.

Code: [Select]
{
  const root = [...document.querySelectorAll('div')].filter(e => getComputedStyle(e).overflowY === 'auto' && e.scrollHeight > e.clientHeight).sort((a,b) => b.scrollHeight - a.scrollHeight)[0];
  let hidden = 0;
  root.querySelectorAll('iframe,canvas,video,object,embed,img,svg').forEach(e => {
    if (e.closest('button,a,[role=button]')) return;
    if (e.parentElement && e.parentElement.closest('svg')) return;
    const m = document.createElement('div');
    m.textContent = '[graphic omitted]';
    e.parentNode.insertBefore(m, e);
    e.style.setProperty('display','none','important');
    hidden++;
  });
  console.log('graphics hidden:', hidden);
}

document.querySelectorAll('*').forEach(el => {
  const s = getComputedStyle(el);
  if (s.overflowY !== 'visible' || s.position === 'fixed' || el.scrollHeight > el.clientHeight) {
    el.style.overflow = 'visible';
    el.style.height = 'auto';
    el.style.maxHeight = 'none';
  }
});
document.head.appendChild(Object.assign(document.createElement('style'),{textContent:'@media print{*{overflow:visible!important;height:auto!important;max-height:none!important}}'}))



Claude: [08/10/2026--05:46:29:941 AEDT]

Here is one script. Your second part is unchanged. The change is that the graphics are now hidden by a stylesheet rule instead of per-element, so when the page redraws it can't bring them back. That redraw is the likely reason they reappeared. The scroller is found first, before your loop sets overflow to visible, because the loop would otherwise stop it being found.

{
  const root = [...document.querySelectorAll('div')].filter(e => getComputedStyle(e).overflowY === 'auto' && e.scrollHeight > e.clientHeight).sort((a,b) => b.scrollHeight - a.scrollHeight)[0];
  root.setAttribute('data-gx','1');
}
document.querySelectorAll('*').forEach(el => {
  const s = getComputedStyle(el);
  if (s.overflowY !== 'visible' || s.position === 'fixed' || el.scrollHeight > el.clientHeight) {
    el.style.overflow = 'visible';
    el.style.height = 'auto';
    el.style.maxHeight = 'none';
  }
});
document.head.appendChild(Object.assign(document.createElement('style'),{textContent:'[data-gx] iframe,[data-gx] canvas,[data-gx] video,[data-gx] object,[data-gx] embed,[data-gx] img,[data-gx] svg:not(button svg):not(a svg){display:none!important}@media print{*{overflow:visible!important;height:auto!important;max-height:none!important}}'}))

Paste it as one block, then use Ctrl+P as usual. There are no [graphic omitted] markers any more, because a stylesheet rule can't draw text in place of an element. If a graphic still shows, tell me what it looks like and I'll add its type to the hide list.

... and finally, take a thorough checkpoint.md, should my proposal fail or should we ever have to revisit or reopen this conversation.


Claude: [08/10/2026--06:19:55:412 AEDT]

`checkpoint.md` is delivered. It follows your three-part format (Problem & Resolution, Risk Analysis, Code Change with a diff). It also covers what to do if the proposal fails, the artefacts, and the open items.

The scripts and the diff in it were checked programmatically against the originals, so they match exactly. The attempt-by-attempt table marks which results you reported and which never came back. Three findings matter most for reopening this:

- **Crash cause:** the crash also happened in a chat with no content graphic, so graphics are not shown to be the cause.
- **Untested hypothesis:** the layout-forcing theory is a hypothesis only.
- **Unconfirmed result:** the final script's result hasn't been reported back yet.

If it fails, the first-listed next step is to note which symptom you hit (crash, one-line preview, graphics showing, or missing messages), because the next fix differs for each. If messages are missing, no print snippet can capture them. In that case the next step is the "Export data" route, and I'd then write a JSON-to-MD/PDF converter in the style used here. That needs a small sample of the export.

For the next chapter, paste `checkpoint.md` and the transcript into a fresh conversation.


« Last Edit: Today at 03:47:41 AM by Chip »
friendly
0
funny
0
informative
0
agree
0
disagree
0
like
0
dislike
0
No reactions
No reactions
No reactions
No reactions
No reactions
No reactions
No reactions
Our Discord Server invitation link is https://discord.gg/jB2qmRrxyD

Tags:
 


dopetalk does not endorse any advertised product nor does it accept any liability for it's use or misuse





TERMS AND CONDITIONS

In no event will d&u or any person involved in creating, producing, or distributing site information be liable for any direct, indirect, incidental, punitive, special or consequential damages arising out of the use of or inability to use d&u. You agree to indemnify and hold harmless d&u, its domain founders, sponsors, maintainers, server administrators, volunteers and contributors from and against all liability, claims, damages, costs and expenses, including legal fees, that arise directly or indirectly from the use of any part of the d&u site.


TO USE THIS WEBSITE YOU MUST AGREE TO THE TERMS AND CONDITIONS ABOVE


Founded December 2014
SimplePortal 2.3.6 © 2008-2014, SimplePortal