Recently I've been working at print-specific styling for my webpages and I'm running into interesting problems where Javascript is concerned.

TLDR; Triggering custom print state when javascript is involved is problematic.

I got assigned to take a React app and make it very print-friendly. The pages are mostly reports so lots of tables and graphs. So I'm covering things like:

  • Removing buttons like save/export, or other call-to-actions, on print
  • Replacing drop-downs with headers on print
  • Resizing text for print (things that look great on the screen are often not sized appropriately for print)
  • Resizing table columns or hiding/showing columns based on print to make them fit. (no side-scroll on print)

Those things above are all tedious but simple. They can be accomplished with basic style changes using `@media print` queries in my LESS files or applying the proper bootstrap3 classes.

Now I'm getting into more complicated changes like:

  • Taking dynamically scaled elements like SVG charts/graphs and making them look right on print. The types of scaling which happens via JS, not CSS.
  • Turning a paginated table into one which fetches more/all results. In the UI you may want only the top 10 items per page, but for print, it is helpful to have the exhaustive list or top 100.
  • I have header status boxes which resize the font based on its contents. So `$10` appears in a larger font than `$10,000` so that they can sit in the same size boxes on a dashboard.

This latter group is not so easy. It looks like Browser Manufacturers haven't put much focus on adding events/hooks into the browsers print modal events. As far as I can tell older versions of IE & Firefox have events, but they're unreliable.

In one particular example, I am using ReCharts which offers a "ResponsiveContainer" component which will resize a chart based on the size of its parent container using javascript. This is a known issue. The problem is, when the print modal opens, javascript execution in that window is frozen and so it cannot run the code to handle the chart resize. So it's the exact same size it was on screen, even if, through print styles, I bump its parent container to 100% of the width of the page. The chart gets stuck at whatever size it was pre-print modal.

I've identified a few ways to handle this, none of which are great IMO:

  1. Render the chart component twice, nested in 2 different divs. One which is shown for print, the other for the screen. The one for print has a hard-coded width, the other uses the responsive container. PROS: It requires no custom JS to be executed. The user can use the browsers CMD+P, FILE->PRINT to print. CONS: This is gross b/c for every chart on the screen it is rendered twice, and only one is shown at a time. This means a decent bit of overhead, especially on pages with many charts.
  2. Have my own "Print" button/icon on the screen which the user must click to trigger a set of custom pre/post print hooks. So the user clicks "Print", I run some PRE JS setting the page into a PRINT state, calling the window.print() method, and then using the returned promise to trigger my POST JS unsetting the PRINT state and returning the page to normal. PROS: This gives me the ability to cascade the PRE-PRINT property into my different components and let them handle setting themself up for print. The components have complete control of doing their own custom print state. CONS: The user cannot use CMD+P or FILE>Print. If they do my JS won't be aware of the print being triggered. If a component needs to make an ASYNC request say to fetch more table rows, it's going to cause me to have to add a delay to the `window.print()` firing. Otherwise, it'll fire before the results are added.
  3. GUT all JS driven responsive design. I could make it so the chart containers are all set-width columns, and let the other containers resize around them. I could remove the dynamic resizing of text based on its content. PROS: Reduces complexity around print. It doesn't require any special page state to be used. Users can print via browsers built-in mechanisms. CONS: UI/UX will not be as good. Screens and paper are not the same widths and so making it work for only 1 will result in the other not looking as good. For instance, it may be desirable to have a chart be 100% of the width of the screen and 100% of the printed pages width. Without a dynamic sizing, this isn't possible.

Now when I run into a problem that doesn't have a great Stackoverflow answer, I generally start questioning why I'm a unique snowflake. It's usually b/c I've made a misguided architecture decision and I need to make some changes. In this case, I don't know how I could do things differently outside of avoiding any javascript-driven size adjustments which would mean avoiding responsive design to some extent.

If you were in my shoes what would you do? Have you faced this problem before? Let me know!