diff --git a/Makefile b/Makefile
index 40fac21..89877b8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,6 @@
.PHONY: build
build:
+ cp formats.png docz/docs/img/formats.png
cd docz; npx -y pnpm build; cd ..
rm -rf docs
mv docz/build/ docs
@@ -7,7 +8,7 @@ build:
.PHONY: serve
serve:
- cd docs; python -mSimpleHTTPServer; cd -
+ cd docs; python -mSimpleHTTPServer || python3 -mhttp.server; cd -
.PHONY: index
index: readme ## Rebuild site
diff --git a/docz/docs/04-getting-started/03-demos/06-clipboard.md b/docz/docs/04-getting-started/03-demos/06-clipboard.md
index 647143a..ef379ca 100644
--- a/docz/docs/04-getting-started/03-demos/06-clipboard.md
+++ b/docz/docs/04-getting-started/03-demos/06-clipboard.md
@@ -14,7 +14,7 @@ XLS (both '97-2004 and '95), and SpreadsheetML 2003.
Not all Clipboard APIs offer access to all clipboard types.
-## Browser
+## Browser Reading (paste)
Clipboard data can be read from a `paste` event, accessible from the event
`clipboardData` property:
@@ -26,7 +26,7 @@ document.onpaste = function(e) {
/* parse */
var wb = XLSX.read(str, {type: "string"});
/* DO SOMETHING WITH wb HERE */
-}
+};
```
`getData` accepts one argument: the desired MIME type. Chrome 103 supports:
@@ -62,7 +62,7 @@ function Clipboard() {
/* generate CSV for each "first worksheet" */
var result = ws_arr.map(ws => XLSX.utils.sheet_to_csv(ws));
setCSVs(result);
- }
+ };
}, []);
return (
@@ -72,6 +72,68 @@ function Clipboard() {
{csvs[2] && (
Data from clipboard RTF (text/rtf) {csvs[2]}
)}
{csvs.every(x => !x) && Copy data in Excel, click here, and paste (Control+V)}
>
- )
+ );
+}
+```
+
+## Browser Writing (copy)
+
+Clipboard data can be written from a `copy` event, accessible from the event
+`clipboardData` property:
+
+```js
+document.oncopy = function(e) {
+ /* get HTML of first worksheet in workbook */
+ var str = XLSX.write(wb, {type: "string", bookType: "html"});
+ /* set HTML clipboard data */
+ e.clipboardData.setData('text/html', str);
+
+ /* prevent the browser from copying the normal data */
+ e.preventDefault();
+};
+```
+
+`setData` accepts two arguments: MIME type and new data. Chrome 103 supports:
+
+| MIME type | Data format |
+|:-------------|:---------------------------|
+| `text/plain` | TSV (tab separated values) |
+| `text/html` | HTML |
+
+Browsers do not currently support assigning to the `text/rtf` clipboard type.
+
+### Live Demo
+
+This demo creates a simple workbook from the following HTML table:
+
+
+
SheetJS
Clipboard
Demo
+
bookType
RTF
+
source
HTML Table
+
+
+Create a new file in Excel then come back to this window. Select the text
+below and copy (Control+C for Windows, Command+C for Mac). Go back to the
+excel
+
+```jsx live
+function Clipboard() {
+ /* Set up copy handler */
+ React.useEffect(async() => {
+ document.oncopy = function(e) {
+ /* generate workbook from table */
+ var wb = XLSX.utils.table_to_book(document.getElementById("srcdata"));
+ /* get HTML of first worksheet in workbook */
+ var str = XLSX.write(wb, {type: "string", bookType: "html"});
+ /* set HTML clipboard data */
+ e.clipboardData.setData('text/html', str);
+ /* prevent the browser from copying the normal data */
+ e.preventDefault();
+ };
+ }, []);
+
+ return (
+ Select this text, copy (Control+C), and paste in Excel
+ );
}
```
diff --git a/docz/docs/04-getting-started/03-demos/07-headless.md b/docz/docs/04-getting-started/03-demos/07-headless.md
new file mode 100644
index 0000000..23a04f3
--- /dev/null
+++ b/docz/docs/04-getting-started/03-demos/07-headless.md
@@ -0,0 +1,215 @@
+---
+sidebar_position: 7
+---
+
+# Headless Automation
+
+Headless automation involves controlling "headless browsers" to access websites
+and submit or download data. It is also possible to automate browsers using
+custom browser extensions.
+
+The [SheetJS standalone script](../../installation/standalone) can be added to
+any website by inserting a `SCRIPT` tag. Headless browsers usually provide
+utility functions for running custom snippets in the browser and passing data
+back to the automation script.
+
+## Use Case
+
+This demo focuses on exporting table data to a workbook. Headless browsers do
+not generally support passing objects between the browser context and the
+automation script, so the file data must be generated in the browser context
+and sent back to the automation script for saving in the filesystem. Steps:
+
+1) Launch the headless browser and load the target webpage.
+
+2) Add the standalone SheetJS build to the page in a `SCRIPT` tag.
+
+3) Add a script to the page (in the browser context) that will:
+
+- Make a workbook object from the first table using `XLSX.utils.table_to_book`
+- Generate the bytes for an XLSB file using `XLSX.write`
+- Send the bytes back to the automation script
+
+4) When the automation context receives data, save to a file
+
+This demo exports data from .
+
+:::note
+
+It is also possible to parse files from the browser context, but parsing from
+the automation context is more performant and strongly recommended.
+
+:::
+
+## Puppeteer
+
+Puppeteer enables headless Chromium automation for NodeJS. Releases ship with
+an installer script. Installation is straightforward:
+
+```bash
+npm i https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz puppeteer
+```
+
+Binary strings are the favored data type. They can be safely passed from the
+browser context to the automation script. NodeJS provides an API to write
+binary strings to file (`fs.writeFileSync` using encoding `binary`).
+
+To run the example, after installing the packages, save the following script to
+`SheetJSPuppeteer.js` and run `node SheetJSPuppeteer.js`. Steps are commented:
+
+```js title="SheetJSPuppeteer.js"
+const fs = require("fs");
+const puppeteer = require('puppeteer');
+(async () => {
+ /* (1) Load the target page */
+ const browser = await puppeteer.launch();
+ const page = await browser.newPage();
+ page.on("console", msg => console.log("PAGE LOG:", msg.text()));
+ await page.setViewport({width: 1920, height: 1080});
+ await page.goto('https://sheetjs.com/demos/table');
+
+ /* (2) Load the standalone SheetJS build from the CDN */
+ await page.addScriptTag({ url: 'https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js' });
+
+ /* (3) Run the snippet in browser and return data */
+ const bin = await page.evaluate(() => {
+ /* NOTE: this function will be evaluated in the browser context.
+ `page`, `fs` and `puppeteer` are not available.
+ `XLSX` will be available thanks to step 2 */
+
+ /* find first table */
+ var table = document.body.getElementsByTagName('table')[0];
+
+ /* call table_to_book on first table */
+ var wb = XLSX.utils.table_to_book(table);
+
+ /* generate XLSB and return binary string */
+ return XLSX.write(wb, {type: "binary", bookType: "xlsb"});
+ });
+
+ /* (4) write data to file */
+ fs.writeFileSync("SheetJSPuppeteer.xlsb", bin, { encoding: "binary" });
+
+ await browser.close();
+})();
+```
+
+## Playwright
+
+Playwright presents a unified scripting framework for Chromium, WebKit, and
+other browsers. It draws inspiration from Puppeteer. In fact, the example
+code is almost identical!
+
+```bash
+npm i https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz playwright
+```
+
+To run the example, after installing the packages, save the following script to
+`SheetJSPlaywright.js` and run `node SheetJSPlaywright.js`. Import divergences
+from the Puppeteer example are highlighted below:
+
+```js title="SheetJSPlaywright.js"
+const fs = require("fs");
+// highlight-next-line
+const { webkit } = require('playwright'); // import desired browser
+(async () => {
+ /* (1) Load the target page */
+ // highlight-next-line
+ const browser = await webkit.launch(); // launch desired browser
+ const page = await browser.newPage();
+ page.on("console", msg => console.log("PAGE LOG:", msg.text()));
+ // highlight-next-line
+ await page.setViewportSize({width: 1920, height: 1080}); // different name :(
+ await page.goto('https://sheetjs.com/demos/table');
+
+ /* (2) Load the standalone SheetJS build from the CDN */
+ await page.addScriptTag({ url: 'https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js' });
+
+ /* (3) Run the snippet in browser and return data */
+ const bin = await page.evaluate(() => {
+ /* NOTE: this function will be evaluated in the browser context.
+ `page`, `fs` and the browser engine are not available.
+ `XLSX` will be available thanks to step 2 */
+
+ /* find first table */
+ var table = document.body.getElementsByTagName('table')[0];
+
+ /* call table_to_book on first table */
+ var wb = XLSX.utils.table_to_book(table);
+
+ /* generate XLSB and return binary string */
+ return XLSX.write(wb, {type: "binary", bookType: "xlsb"});
+ });
+
+ /* (4) write data to file */
+ fs.writeFileSync("SheetJSPlaywright.xlsb", bin, { encoding: "binary" });
+
+ await browser.close();
+})();
+```
+
+
+## PhantomJS
+
+PhantomJS is a headless web browser powered by WebKit. Standalone binaries are
+available at
+
+:::warning
+
+This information is provided for legacy deployments. PhantomJS development has
+been suspended and there are known vulnerabilities, so new projects should use
+alternatives. For WebKit automation, new projects should use Playwright.
+
+:::
+
+Binary strings are the favored data type. They can be safely passed from the
+browser context to the automation script. PhantomJS provides an API to write
+binary strings to file (`fs.write` using mode `wb`).
+
+To run the example, save the following script to `SheetJSPhantom.js` in the same
+folder as `phantomjs.exe` or `phantomjs` and run
+
+```
+./phantomjs SheetJSPhantom.js ## macOS / Linux
+.\phantomjs.exe SheetJSPhantom.js ## windows
+```
+
+The steps are marked in the comments:
+
+```js title="SheetJSPhantom.js"
+var page = require('webpage').create();
+page.onConsoleMessage = function(msg) { console.log(msg); };
+
+/* (1) Load the target page */
+page.open('https://sheetjs.com/demos/table', function() {
+
+ /* (2) Load the standalone SheetJS build from the CDN */
+ page.includeJs("https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js", function() {
+
+ /* (3) Run the snippet in browser and return data */
+ var bin = page.evaluateJavaScript([ "function(){",
+
+ /* find first table */
+ "var table = document.body.getElementsByTagName('table')[0];",
+
+ /* call table_to_book on first table */
+ "var wb = XLSX.utils.table_to_book(table);",
+
+ /* generate XLSB file and return binary string */
+ "return XLSX.write(wb, {type: 'binary', bookType: 'xlsb'});",
+ "}" ].join(""));
+
+ /* (4) write data to file */
+ require("fs").write("SheetJSPhantomJS.xlsb", bin, "wb");
+
+ phantom.exit();
+ });
+});
+```
+
+:::caution
+
+PhantomJS is very finicky and will hang if there are script errors. It is
+strongly recommended to add verbose logging and to lint scripts before use.
+
+:::
diff --git a/docz/docs/04-getting-started/03-demos/index.md b/docz/docs/04-getting-started/03-demos/index.md
index 8e9d930..a142e57 100644
--- a/docz/docs/04-getting-started/03-demos/index.md
+++ b/docz/docs/04-getting-started/03-demos/index.md
@@ -41,7 +41,7 @@ The demo projects include small runnable examples and short explainers.
- [`NetSuite SuiteScript`](./netsuite)
- [`SalesForce Lightning Web Components`](./salesforce)
- [`Excel JavaScript API`](./excel)
-- [`Headless Browsers`](https://github.com/SheetJS/SheetJS/tree/master/demos/headless/)
+- [`Headless Automation`](./headless)
- [`Other JavaScript Engines`](https://github.com/SheetJS/SheetJS/tree/master/demos/altjs/)
- [`"serverless" functions`](https://github.com/SheetJS/SheetJS/tree/master/demos/function/)
- [`Databases and Key/Value Stores`](https://github.com/SheetJS/SheetJS/tree/master/demos/database/)
diff --git a/docz/docs/06-solutions/01-input.md b/docz/docs/06-solutions/01-input.md
index 9f39707..459f6cc 100644
--- a/docz/docs/06-solutions/01-input.md
+++ b/docz/docs/06-solutions/01-input.md
@@ -818,13 +818,17 @@ is missing or no options are specified, the default name `Sheet1` is used.
#### Examples
+The [Headless Demo](../getting-started/demos/headless) includes examples of
+server-side spreadsheet generation from HTML TABLE elements using headless
+Chromium ("Puppeteer") and other browsers ("Playwright")
+
Here are a few common scenarios (click on each subtitle to see the code):
HTML TABLE element in a webpage (click to show)
```html
-
+
@@ -897,95 +901,6 @@ chrome.runtime.onMessage.addListener(function(msg, sender, cb) {
-
- Server-Side HTML Tables with Headless Chrome (click to show)
-
-The [`headless` demo](https://github.com/SheetJS/SheetJS/tree/master/demos/headless/) includes a complete demo to convert HTML
-files to XLSB workbooks. The core idea is to add the script to the page, parse
-the table in the page context, generate a `base64` workbook and send it back
-for further processing:
-
-```js
-const XLSX = require("xlsx");
-const { readFileSync } = require("fs"), puppeteer = require("puppeteer");
-
-const url = `https://sheetjs.com/demos/table`;
-
-/* get the standalone build source (node_modules/xlsx/dist/xlsx.full.min.js) */
-const lib = readFileSync(require.resolve("xlsx/dist/xlsx.full.min.js"), "utf8");
-
-(async() => {
- /* start browser and go to web page */
- const browser = await puppeteer.launch();
- const page = await browser.newPage();
- await page.goto(url, {waitUntil: "networkidle2"});
-
- /* inject library */
- await page.addScriptTag({content: lib});
-
- /* this function `s5s` will be called by the script below, receiving the Base64-encoded file */
- await page.exposeFunction("s5s", async(b64) => {
- const workbook = XLSX.read(b64, {type: "base64" });
-
- /* DO SOMETHING WITH workbook HERE */
- });
-
- /* generate XLSB file in webpage context and send back result */
- await page.addScriptTag({content: `
- /* call table_to_book on first table */
- var workbook = XLSX.utils.table_to_book(document.querySelector("TABLE"));
-
- /* generate XLSX file */
- var b64 = XLSX.write(workbook, {type: "base64", bookType: "xlsb"});
-
- /* call "s5s" hook exposed from the node process */
- window.s5s(b64);
- `});
-
- /* cleanup */
- await browser.close();
-})();
-```
-
-
-
-
- Server-Side HTML Tables with Headless WebKit (click to show)
-
-The [`headless` demo](https://github.com/SheetJS/SheetJS/tree/master/demos/headless/) includes a complete demo to convert HTML
-files to XLSB workbooks using [PhantomJS](https://phantomjs.org/). The core idea
-is to add the script to the page, parse the table in the page context, generate
-a `binary` workbook and send it back for further processing:
-
-```js
-var XLSX = require('xlsx');
-var page = require('webpage').create();
-
-/* this code will be run in the page */
-var code = [ "function(){",
- /* call table_to_book on first table */
- "var wb = XLSX.utils.table_to_book(document.body.getElementsByTagName('table')[0]);",
-
- /* generate XLSB file and return binary string */
- "return XLSX.write(wb, {type: 'binary', bookType: 'xlsb'});",
-"}" ].join("");
-
-page.open('https://sheetjs.com/demos/table', function() {
- /* Load the browser script from the UNPKG CDN */
- page.includeJs("https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js", function() {
- /* The code will return an XLSB file encoded as binary string */
- var bin = page.evaluateJavaScript(code);
-
- var workbook = XLSX.read(bin, {type: "binary"});
- /* DO SOMETHING WITH workbook HERE */
-
- phantom.exit();
- });
-});
-```
-
-
-
NodeJS HTML Tables without a browser (click to show)
diff --git a/docz/docs/06-solutions/05-output.md b/docz/docs/06-solutions/05-output.md
index 80f9d1f..523f2da 100644
--- a/docz/docs/06-solutions/05-output.md
+++ b/docz/docs/06-solutions/05-output.md
@@ -245,28 +245,34 @@ The [`extendscript` demo](../getting-started/demos/extendscript) includes a more
-
- PhantomJS (Headless Webkit) File Generation (click to show)
+The [`headless` demo](../getting-started/demos/headless) includes complete
+examples of converting HTML TABLE elements to XLSB workbooks using Puppeteer
+and other headless automation tools.
-The [`headless` demo](https://github.com/SheetJS/SheetJS/tree/master/demos/headless/) includes a complete demo to convert HTML
-files to XLSB workbooks using [PhantomJS](https://phantomjs.org/). PhantomJS
-`fs.write` supports writing files from the main process but has a different
-interface from the NodeJS `fs` module:
+Headless browsers may not have access to the filesystem, so `XLSX.writeFile`
+may fail. It is strongly recommended to generate the file bytes in the browser
+context, send the bytes to the automation context, and write from automation.
+
+Puppeteer and Playwright are NodeJS modules that support binary strings:
```js
-var XLSX = require('xlsx');
-var fs = require('fs');
+/* from the browser context */
+var bin = XLSX.write(workbook, { type:"binary", bookType: "xlsb" });
-/* generate a binary string */
-var bin = XLSX.write(workbook, { type:"binary", bookType: "xlsx" });
-/* write to file */
-fs.write("test.xlsx", bin, "wb");
+/* from the automation context */
+fs.writeFileSync("SheetJSansHead.xlsb", bin, { encoding: "binary" });
```
-Note: The section ["Processing HTML Tables"](./input#processing-html-tables) shows how
-to generate a workbook from HTML tables in a page in "Headless WebKit".
+PhantomJS `fs.write` supports writing files from the main process. The mode
+`wb` supports binary strings:
-
+```js
+/* from the browser context */
+var bin = XLSX.write(workbook, { type:"binary", bookType: "xlsb" });
+
+/* from the automation context */
+fs.write("SheetJSansHead.xlsb", bin, "wb");
+```
diff --git a/docz/docs/07-csf/07-features/index.md b/docz/docs/07-csf/07-features/index.md
index a4eee48..51c9d1e 100644
--- a/docz/docs/07-csf/07-features/index.md
+++ b/docz/docs/07-csf/07-features/index.md
@@ -426,9 +426,9 @@ explicitly flagged. Combining the two checks yields a simple function:
```js
function wb_has_macro(wb/*:workbook*/)/*:boolean*/ {
- if(!!wb.vbaraw) return true;
- const sheets = wb.SheetNames.map((n) => wb.Sheets[n]);
- return sheets.some((ws) => !!ws && ws['!type']=='macro');
+ if(!!wb.vbaraw) return true;
+ const sheets = wb.SheetNames.map((n) => wb.Sheets[n]);
+ return sheets.some((ws) => !!ws && ws['!type']=='macro');
}
```
diff --git a/docz/docs/08-api/09-utilities.md b/docz/docs/08-api/09-utilities.md
index 5e6ada9..0740ba3 100644
--- a/docz/docs/08-api/09-utilities.md
+++ b/docz/docs/08-api/09-utilities.md
@@ -9,18 +9,27 @@ The `sheet_to_*` functions accept a worksheet and an optional options object.
The `*_to_sheet` functions accept a data object and an optional options object.
+The `sheet_add_*` functions accept worksheet, data, and optional options.
+
The examples are based on the following worksheet:
-```
-XXX| A | B | C | D | E | F | G |
----+---+---+---+---+---+---+---+
- 1 | S | h | e | e | t | J | S |
- 2 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
- 3 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
-```
+
+
S
h
e
e
t
J
S
+
1
2
5
6
7
+
2
3
6
7
8
+
3
4
7
8
9
+
4
5
6
7
8
9
0
+
+
### Array of Arrays Input
+**Create a worksheet from an array of arrays**
+
+```js
+var ws = XLSX.utils.aoa_to_sheet(aoa, opts);
+```
+
`XLSX.utils.aoa_to_sheet` takes an array of arrays of JS values and returns a
worksheet resembling the input data. Numbers, Booleans and Strings are stored
as the corresponding styles. Dates are stored as date or numbers. Array holes
@@ -34,19 +43,23 @@ other values are stored as strings. The function takes an options argument:
|`sheetStubs` | false | Create cell objects of type `z` for `null` values |
|`nullError` | false | If true, emit `#NULL!` error cells for `null` values |
-
- Examples (click to show)
-
-To generate the example sheet:
+The example worksheet can be generated with:
```js
var ws = XLSX.utils.aoa_to_sheet([
- "SheetJS".split(""),
- [1,2,3,4,5,6,7],
- [2,3,4,5,6,7,8]
+ ["S", "h", "e", "e", "t", "J", "S"],
+ [ 1, 2, , , 5, 6, 7],
+ [ 2, 3, , , 6, 7, 8],
+ [ 3, 4, , , 7, 8, 9],
+ [ 4, 5, 6, 7, 8, 9, 0]
]);
```
-
+
+**Add data from an array of arrays to an existing worksheet**
+
+```js
+XLSX.utils.sheet_add_aoa(ws, aoa, opts);
+```
`XLSX.utils.sheet_add_aoa` takes an array of arrays of JS values and updates an
existing worksheet object. It follows the same process as `aoa_to_sheet` and
@@ -71,22 +84,7 @@ accepts an options argument:
| (default) | Start from cell A1 |
-
- Examples (click to show)
-
-Consider the worksheet:
-
-```
-XXX| A | B | C | D | E | F | G |
----+---+---+---+---+---+---+---+
- 1 | S | h | e | e | t | J | S |
- 2 | 1 | 2 | | | 5 | 6 | 7 |
- 3 | 2 | 3 | | | 6 | 7 | 8 |
- 4 | 3 | 4 | | | 7 | 8 | 9 |
- 5 | 4 | 5 | 6 | 7 | 8 | 9 | 0 |
-```
-
-This worksheet can be built up in the order `A1:G1, A2:B4, E2:G4, A5:G5`:
+The example worksheet can be built up in the order `A1:G1, A2:B4, E2:G4, A5:G5`:
```js
/* Initial row */
@@ -102,10 +100,14 @@ XLSX.utils.sheet_add_aoa(ws, [[5,6,7], [6,7,8], [7,8,9]], {origin:{r:1, c:4}});
XLSX.utils.sheet_add_aoa(ws, [[4,5,6,7,8,9,0]], {origin: -1});
```
-
-
### Array of Objects Input
+**Create a worksheet from an array of objects**
+
+```js
+var ws = XLSX.utils.json_to_sheet(aoo, opts);
+```
+
`XLSX.utils.json_to_sheet` takes an array of objects and returns a worksheet
with automatically-generated "headers" based on the keys of the objects. The
default column order is determined by the first appearance of the field using
@@ -119,37 +121,69 @@ default column order is determined by the first appearance of the field using
|`skipHeader` | false | If true, do not include header row in output |
|`nullError` | false | If true, emit `#NULL!` error cells for `null` values |
-- All fields from each row will be written. If `header` is an array and it does
- not contain a particular field, the key will be appended to the array.
+:::caution
+
+All fields from each row will be written! `header` hints at a particular order
+but is not exclusive. To remove fields from the export, filter the data source.
+
+Some data sources have special options to filter properties. For example,
+MongoDB will add the `_id` field when finding data from a collection:
+
+```js
+const aoo_with_id = await coll.find({}).toArray();
+const ws = XLSX.utils.json_to_sheet(aoo_with_id); // includes _id column
+```
+
+This can be filtered out through the `projection` property:
+
+```js
+const aoo = await coll.find({}, {projection:{_id:0}}).toArray(); // no _id !
+const ws = XLSX.utils.json_to_sheet(aoo);
+```
+
+If a data source does not provide a filter option, it can be filtered manually:
+
+```js
+const aoo = data.map(obj => Object.fromEntries(Object.entries(obj).filter(r => headers.indexOf(r[0]) > -1)));
+```
+
+:::
+
+- If `header` is an array, missing keys will be added in order of first use.
- Cell types are deduced from the type of each value. For example, a `Date`
object will generate a Date cell, while a string will generate a Text cell.
- Null values will be skipped by default. If `nullError` is true, an error cell
corresponding to `#NULL!` will be written to the worksheet.
-
- Examples (click to show)
-
-The original sheet cannot be reproduced using plain objects since JS object keys
+The example sheet cannot be reproduced using plain objects since JS object keys
must be unique. After replacing the second `e` and `S` with `e_1` and `S_1`:
```js
var ws = XLSX.utils.json_to_sheet([
- { S:1, h:2, e:3, e_1:4, t:5, J:6, S_1:7 },
- { S:2, h:3, e:4, e_1:5, t:6, J:7, S_1:8 }
+ { S:1, h:2, , , t:5, J:6, S_1:7 },
+ { S:2, h:3, , , t:6, J:7, S_1:8 }
+ { S:3, h:4, , , t:7, J:8, S_1:9 }
+ { S:4, h:5, e:6, e_1:7, t:8, J:9, S_1:0 }
], {header:["S","h","e","e_1","t","J","S_1"]});
```
-Alternatively, the header row can be skipped:
+Alternatively, a different set of unique headers can be used with `skipHeader`:
```js
var ws = XLSX.utils.json_to_sheet([
{ A:"S", B:"h", C:"e", D:"e", E:"t", F:"J", G:"S" },
- { A: 1, B: 2, C: 3, D: 4, E: 5, F: 6, G: 7 },
- { A: 2, B: 3, C: 4, D: 5, E: 6, F: 7, G: 8 }
+ { A: 1, B: 2, , , E: 5, F: 6, G: 7 },
+ { A: 2, B: 3, , , E: 6, F: 7, G: 8 }
+ { A: 3, B: 4, , , E: 7, F: 8, G: 9 },
+ { A: 4, B: 5, C: 6, D: 7, E: 8, F: 9, G: 0 },
], {header:["A","B","C","D","E","F","G"], skipHeader:true});
```
-
+**Add data from an array of objects to an existing worksheet**
+
+```js
+XLSX.utils.sheet_add_json(ws, aoo, opts);
+```
`XLSX.utils.sheet_add_json` takes an array of objects and updates an existing
worksheet object. It follows the same process as `json_to_sheet` and accepts
@@ -175,22 +209,7 @@ an options argument:
| (default) | Start from cell A1 |
-
- Examples (click to show)
-
-Consider the worksheet:
-
-```
-XXX| A | B | C | D | E | F | G |
----+---+---+---+---+---+---+---+
- 1 | S | h | e | e | t | J | S |
- 2 | 1 | 2 | | | 5 | 6 | 7 |
- 3 | 2 | 3 | | | 6 | 7 | 8 |
- 4 | 3 | 4 | | | 7 | 8 | 9 |
- 5 | 4 | 5 | 6 | 7 | 8 | 9 | 0 |
-```
-
-This worksheet can be built up in the order `A1:G1, A2:B4, E2:G4, A5:G5`:
+This example worksheet can be built up in the order `A1:G1, A2:B4, E2:G4, A5:G5`:
```js
/* Initial row */
@@ -214,10 +233,49 @@ XLSX.utils.sheet_add_json(ws, [
], {header: ["A", "B", "C", "D", "E", "F", "G"], skipHeader: true, origin: -1});
```
-
+:::note
+
+If the `header` option is an array, `sheet_add_json` and `sheet_to_json` will
+append missing elements.
+
+This design enables consistent header order across calls:
+
+```jsx live
+function SheetJSHeaderOrder() {
+ /* Use shared header */
+ const header = [];
+ const ws1 = XLSX.utils.json_to_sheet([ {C: 2, D: 3}, ], {header});
+ XLSX.utils.sheet_add_json(ws1, [ {D: 1, C: 4}, ], {header, origin: -1, skipHeader: true});
+
+ /* only use header in first call */
+ const ws2 = XLSX.utils.json_to_sheet([ {C: 2, D: 3}, ], {header:[]});
+ XLSX.utils.sheet_add_json(ws2, [ {D: 1, C: 4}, ], {origin: -1, skipHeader: true});
+
+ return (
+ Objects
+ {"\n[\n { C: 2, D: 3 },\n { D: 1, C: 4 } // different key order\n]\n"}
+ Worksheet when same `header` array is passed to `sheet_add_json`
+
+ New contents of `header`
+ {JSON.stringify(header)}
+
+ Worksheet when no `header` property is passed to `sheet_add_json`
+
+ )
+}
+```
+
+:::
### HTML Table Input
+**Create a worksheet or workbook from an HTML DOM TABLE**
+
+```js
+var ws = XLSX.utils.table_to_sheet(elt, opts);
+var wb = XLSX.utils.table_to_book(elt, opts);
+```
+
`XLSX.utils.table_to_sheet` takes a table DOM element and returns a worksheet
resembling the input table. Numbers are parsed. All other data will be stored
as strings.
@@ -235,29 +293,47 @@ Both functions accept options arguments:
|`display` | false | If true, hidden rows and cells will not be parsed |
-
- Examples (click to show)
-
-To generate the example sheet, start with the HTML table:
-
-```html
-
-
S
h
e
e
t
J
S
-
1
2
3
4
5
6
7
-
2
3
4
5
6
7
8
-
-```
-
-To process the table:
+To generate the example sheet, assuming the table has ID `sheetjs`:
```js
var tbl = document.getElementById('sheetjs');
-var wb = XLSX.utils.table_to_book(tbl);
+var ws = XLSX.utils.table_to_sheet(tbl);
```
-
-Note: `XLSX.read` can handle HTML represented as strings.
+:::note
+`table_to_book` and `table_to_sheet` act on HTML DOM elements. Starting from
+an HTML string, there are two parsing approaches:
+
+A) Table Phantasm: create a DIV with the desired HTML.
+
+```js
+/* create element from the source */
+var elt = document.createElement("div");
+elt.innerHTML = html_source;
+document.body.appendChild(elt);
+
+/* generate worksheet */
+var ws = XLSX.utils.table_to_sheet(elt.getElementsByTagName("TABLE")[0]);
+
+/* remove element */
+document.body.removeChild(elt);
+```
+
+B) Raw HTML: use `XLSX.read` to read the text in the same manner as CSV.
+
+```js
+var wb = XLSX.read(html_source, { type: "string" });
+var ws = wb.Sheets[wb.SheetNames[0]];
+```
+
+:::
+
+**Add data from a HTML DOM TABLE to an existing worksheet**
+
+```js
+XLSX.utils.sheet_add_dom(ws, elt, opts);
+```
`XLSX.utils.sheet_add_dom` takes a table DOM element and updates an existing
worksheet object. It follows the same process as `table_to_sheet` and accepts
@@ -282,53 +358,74 @@ an options argument:
| (default) | Start from cell A1 |
-
- Examples (click to show)
+A common use case for `sheet_add_dom` involves adding multiple tables to a
+single worksheet, usually with a few blank rows in between each table:
-A small helper function can create gap rows between tables:
+![Multi-Table Export in Excel](pathname:///files/multitable.png)
-```js
-function create_gap_rows(ws, nrows) {
- var ref = XLSX.utils.decode_range(ws["!ref"]); // get original range
- ref.e.r += nrows; // add to ending row
- ws["!ref"] = XLSX.utils.encode_range(ref); // reassign row
+```jsx live
+function MultiTable() {
+ const headers = ["Table 1", "Table2", "Table 3"];
+
+ /* Callback invoked when the button is clicked */
+ const xport = React.useCallback(async () => {
+ /* This function creates gap rows */
+ function create_gap_rows(ws, nrows) {
+ var ref = XLSX.utils.decode_range(ws["!ref"]); // get original range
+ ref.e.r += nrows; // add to ending row
+ ws["!ref"] = XLSX.utils.encode_range(ref); // reassign row
+ }
+
+ /* first table */
+ const ws = XLSX.utils.aoa_to_sheet([[headers[0]]]);
+ XLSX.utils.sheet_add_dom(ws, document.getElementById('table1'), {origin: -1});
+ create_gap_rows(ws, 1); // one row gap after first table
+
+ /* second table */
+ XLSX.utils.sheet_add_aoa(ws, [[headers[1]]], {origin: -1});
+ XLSX.utils.sheet_add_dom(ws, document.getElementById('table2'), {origin: -1});
+ create_gap_rows(ws, 2); // two rows gap after second table
+
+ /* third table */
+ XLSX.utils.sheet_add_aoa(ws, [[headers[2]]], {origin: -1});
+ XLSX.utils.sheet_add_dom(ws, document.getElementById('table3'), {origin: -1});
+
+ /* create workbook and export */
+ const wb = XLSX.utils.book_new();
+ XLSX.utils.book_append_sheet(wb, ws, "Export");
+ XLSX.writeFile(wb, "SheetJSMultiTablexport.xlsx");
+ });
+
+ return (
+ <>
+
+ {headers[0]}
+
+
A2
B2
+
A3
B3
+
+ {headers[1]}
+
+
A6
B6
C6
+
A7
B7
C7
+
+
+ {headers[2]}
+
+
A11
B11
+
A12
B12
+
+ >
+ );
}
-
-/* first table */
-var ws = XLSX.utils.table_to_sheet(document.getElementById('table1'));
-create_gap_rows(ws, 1); // one row gap after first table
-
-/* second table */
-XLSX.utils.sheet_add_dom(ws, document.getElementById('table2'), {origin: -1});
-create_gap_rows(ws, 3); // three rows gap after second table
-
-/* third table */
-XLSX.utils.sheet_add_dom(ws, document.getElementById('table3'), {origin: -1});
```
-
-
-### Formulae Output
-
-`XLSX.utils.sheet_to_formulae` generates an array of commands that represent
-how a person would enter data into an application. Each entry is of the form
-`A1-cell-address=formula-or-value`. String literals are prefixed with a `'` in
-accordance with Excel.
-
-
- Examples (click to show)
-
-For the example sheet:
-
-```js
-> var o = XLSX.utils.sheet_to_formulae(ws);
-> [o[0], o[5], o[10], o[15], o[20]];
-[ 'A1=\'S', 'F1=\'J', 'D2=4', 'B3=3', 'G3=8' ]
-```
-
-
### Delimiter-Separated Output
+```js
+var csv = XLSX.utils.sheet_to_csv(ws, opts);
+```
+
As an alternative to the `writeFile` CSV type, `XLSX.utils.sheet_to_csv` also
produces CSV output. The function takes an options argument:
@@ -351,26 +448,35 @@ produces CSV output. The function takes an options argument:
Using `XLSX.write` with type `string` will also skip the mark.
-
- Examples (click to show)
+Starting from the example worksheet:
-For the example sheet:
+```jsx live
+function SheetJSCSVTest() {
+ var ws = XLSX.utils.aoa_to_sheet([
+ ["S", "h", "e", "e", "t", "J", "S"],
+ [ 1, 2, , , 5, 6, 7],
+ [ 2, 3, , , 6, 7, 8],
+ [ 3, 4, , , 7, 8, 9],
+ [ 4, 5, 6, 7, 8, 9, 0]
+ ]);
+ return (
);
+}
+```
+
+**UTF-16 Text Output**
```js
-> console.log(XLSX.utils.sheet_to_csv(ws));
-S,h,e,e,t,J,S
-1,2,3,4,5,6,7
-2,3,4,5,6,7,8
-> console.log(XLSX.utils.sheet_to_csv(ws, {FS:"\t"}));
-S h e e t J S
-1 2 3 4 5 6 7
-2 3 4 5 6 7 8
-> console.log(XLSX.utils.sheet_to_csv(ws,{FS:":",RS:"|"}));
-S:h:e:e:t:J:S|1:2:3:4:5:6:7|2:3:4:5:6:7:8|
+var txt = XLSX.utils.sheet_to_txt(ws, opts);
```
-
-
-#### UTF-16 Unicode Text
The `txt` output type uses the tab character as the field separator. If the
`codepage` library is available (included in full distribution but not core),
@@ -378,8 +484,13 @@ the output will be encoded in `CP1200` and the BOM will be prepended.
`XLSX.utils.sheet_to_txt` takes the same arguments as `sheet_to_csv`.
+
### HTML Output
+```js
+var html = XLSX.utils.sheet_to_html(ws, opts);
+```
+
As an alternative to the `writeFile` HTML type, `XLSX.utils.sheet_to_html` also
produces HTML output. The function takes an options argument:
@@ -390,18 +501,31 @@ produces HTML output. The function takes an options argument:
|`header` | | Override header (default `html body`) |
|`footer` | | Override footer (default `/body /html`) |
-
- Examples (click to show)
+Starting from the example worksheet:
-For the example sheet:
+```jsx live
+function SheetJSHTML() {
+ var ws = XLSX.utils.aoa_to_sheet([
+ ["S", "h", "e", "e", "t", "J", "S"],
+ [ 1, 2, , , 5, 6, 7],
+ [ 2, 3, , , 6, 7, 8],
+ [ 3, 4, , , 7, 8, 9],
+ [ 4, 5, 6, 7, 8, 9, 0]
+ ]);
+ return (