Advanced Table — Confluence Cloud Macro
This Confluence user macro enhances all tables from $body using DOM parsing and replaces them with interactive Grid.js components.
It supports sorting, filtering, pagination, and CSV export with customizable delimiter (csvDelimiter param).
All tables are processed by default without the need for a separate toggle.
Icons like the export button are rendered using Iconify Iconify CDN for a clean UI.
Grid.js styles are loaded from CDN and tables are injected dynamically into the live Confluence page.
User Parameters
Enable sorting
Turn on column sorting
Add row numbers
Insert a numbering column as the first column
Pagination limit
Set the maximum number of rows per page
Show search filter
Display a search input for quick filtering
Enable CSV export
Allow downloading a table as a CSV file
CSV Delimiter
Choose the character used to separate CSV values
Template
$body
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/gridjs/dist/theme/mermaid.min.css" />
<script src="https://cdn.jsdelivr.net/npm/gridjs/dist/gridjs.umd.js"></script>
<script src="https://code.iconify.design/3/3.1.1/iconify.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', () => {
const EXPORT_ICON = "ph:download";
// User parameters
const macroBody = `$body`;
const enableSorting = "$!parameters['enableSorting']" === "true";
const autoNumber = "$!parameters['addRowNumbers']" === "true";
const allowExport = "$!parameters['enableCSVExport']" === "true";
const showFilter = "$!parameters['showSearchFilter']" === "true";
const paginationLimit = parseInt("$!parameters['paginationLimit']") || 15;
const csvDelimiter = "$!parameters['cSVDelimiter']";
// Parse $body value to extract tables
const parser = new DOMParser();
const doc = parser.parseFromString(macroBody, 'text/html');
const allTables = Array.from(doc.querySelectorAll('table'));
const liveTables = document.querySelectorAll('table[ac\\:local-id]');
allTables.forEach((table, idx) => {
const allTrs = Array.from(table.querySelectorAll('tr'));
// Extract header row (first row with <th>)
const headerRow = allTrs.find(tr => tr.querySelector('th'));
const headerCells = Array.from(headerRow?.querySelectorAll('th') || [])
.map(th => th.textContent.trim());
// Extract data rows
const dataRows = allTrs
.filter(tr => tr !== headerRow)
.map(tr => Array.from(tr.querySelectorAll('td')).map(td => td.textContent.trim()));
const numberedData = autoNumber
? dataRows.map((row, i) => [i + 1, ...row])
: dataRows;
const columns = [
...(autoNumber ? [{
name: '#',
width: '40px',
sort: false
}] : []),
...headerCells.map(col => ({
name: col,
sort: enableSorting
}))
];
// Create Grid.js container
const wrapper = document.createElement('div');
wrapper.style.width = '95%';
const liveTable = liveTables[idx];
if (!liveTable) return;
liveTable.replaceWith(wrapper);
// Render Grid.js
const grid = new gridjs.Grid({
columns,
data: numberedData,
sort: enableSorting,
search: showFilter,
pagination: {
limit: paginationLimit
},
width: '100%'
});
grid.render(wrapper);
// CSV export
if (allowExport) {
setTimeout(() => {
const btn = document.createElement('button');
btn.innerHTML = '<span class="iconify" data-icon="' + EXPORT_ICON + '"></span>';
btn.title = 'Download CSV';
btn.style.cssText = `
border: none;
background: none;
cursor: pointer;
font-size: 20px;
float: right;
`;
btn.addEventListener('click', () => {
const csv = convertToCSV(numberedData, csvDelimiter);
downloadBlob(csv, 'table-export-' + (idx + 1) + '.csv', 'text/csv;charset=utf-8;');
});
const searchWrap = wrapper.querySelector('.gridjs-search');
if (searchWrap) {
const spanWrap = document.createElement('span');
spanWrap.className = 'gridjs-search';
spanWrap.style.cssText = 'display: inline-block; width: 100%;';
searchWrap.parentNode.replaceChild(spanWrap, searchWrap);
spanWrap.appendChild(searchWrap);
spanWrap.appendChild(btn);
} else {
wrapper.prepend(btn);
}
}, 100);
}
});
// CSV conversion utility
function convertToCSV(arr, delimiter) {
return arr.map(row =>
row.map(cell =>
'"' + String(cell || '').replace(/"/g, '""') + '"'
).join(delimiter)
).join('\n');
}
// Trigger file download
function downloadBlob(content, filename, contentType) {
const blob = new Blob([content], { type: contentType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
});
</script>
Recommended Macros
Parses a table containing cost and currency columns, converts each row's value into a selected result currency, and appends a footer row showing the total sum. Useful for tracking multi-currency expenses and reporting totals in a unified currency.
Add a configurable sticky banner to a Confluence page
Macro that dynamically lists all its child pages in a table format
Generate a list of labels from all spaces leading to corresponding content pages, organized in alphabetical order.
Macro that will show a "Delete me" message only for editors (in the Page Edit mode)
The content of this macro will not go to the printing page
Content that is hidden only if a user is in a particular group
Space Information macro by space Id