How to Generate Excel‑Based Posters in the Browser Without Crashing
This article walks through building a front‑end‑only tool that reads student data from Excel, creates customized poster images using SheetJS, html2canvas, and JSZip, and solves out‑of‑memory crashes by analyzing JavaScript heap usage, refactoring recursive code, and optimizing DOM cloning with ignoreElements.
1. Requirement Background
We received an urgent request to generate posters from student information and exam scores stored in an Excel file.
Excel data and the desired poster style are shown below:
Because the deadline is tight, the entire data parsing and poster generation must be implemented on the front end.
Key Technical Points
Use SheetJS and
XLSX.utils.sheet_to_jsonto convert Excel data to JSON.
Generate poster images with html2canvas , then obtain a Blob via
canvas.toBlob.
Package the images into a zip file using JSZip .
Import/export form configuration with
FileReader.readAsText(refer to MDN for details).
Overall Development Flow
Create a form to collect configurable text for each exam score.
Iterate over each row of the Excel file, generate an HTML template for the poster based on the form configuration.
Render the HTML template to an image and add the image data to a zip object.
After processing all rows, download the zip file.
Testing with 100 rows of data on our machine succeeded, and the operations team was satisfied.
2. Testing Issue
When the operations colleague tested the tool on their computer, the page crashed with an “Out Of Memory” error after processing more than 20 rows.
3. Problem Analysis
3.1 JavaScript Memory Issue
The JS heap keeps growing after each Excel row is processed because the JSZip object is never released until the download finishes.
Solution: split the zip into smaller batches (e.g., download every 10 rows) to free memory.
However, the memory growth was still excessive (≈20 MB for 20 images), so we used the browser’s Performance and Memory tools.
The snapshot showed continuous memory increase, and further inspection revealed recursive code that created a large call stack.
We replaced the recursion with an iterative loop, which stopped the heap growth.
3.2 DOM Issue
Even after fixing the JS side, the page still stalled when processing more than 40 rows. The bottleneck was html2canvas cloning the entire DOM, including the React root,
scriptand
linktags, which triggered additional network requests.
We limited the cloning to the target node and ignored unnecessary elements using the
ignoreElementsoption.
<code>const canvas = await html2canvas(root, {
imageTimeout: 10000,
ignoreElements: (ele) =>
ele.id === 'root' ||
ele.tagName.toUpperCase() === 'IFRAME' ||
ele.tagName.toUpperCase() === 'SCRIPT' ||
ele.tagName.toUpperCase() === 'LINK',
});
</code>After applying the filter, processing 1,000 rows completed in under 4 minutes without any crashes.
4. Summary
JSZip’s memory consumption still requires batch processing, but the batch size can be increased to 1,000 rows after the DOM cloning issue is resolved.
This case demonstrates that thorough analysis—both of JavaScript heap behavior and DOM manipulation—is essential before applying generic optimizations like batching.
When a page shows “Oops, it crashed!”, careful debugging is the key to a reliable solution.
Tencent IMWeb Frontend Team
IMWeb Frontend Community gathering frontend development enthusiasts. Follow us for refined live courses by top experts, cutting‑edge technical posts, and to sharpen your frontend skills.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.