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.
$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>
Add a configurable sticky banner to a Confluence page
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.
Generate a list of labels from all spaces leading to corresponding content pages, organized in alphabetical order.
Add a configurable floating panel to a Confluence page
Show an expandable page tree for a selected parent page (defaults to the current page)
Based on CQL it shows a table with: Page title, Author, Updated, Status
Macro that dynamically lists all its child pages in a table format
Show filtered issues and their relations
Shows page creation date
Macro that will show a "Delete me" message only for editors (in the Page Edit mode)