demo · v149
Rich Text Export
A rich text editor that registers four clipboard formats — text/html, text/plain, text/csv, and application/json — all via ClipboardItem.delayed(). Only the format the paste target accepts is ever generated. No wasted serialization, no upfront cost.
Try it: Edit the table in the left panel. Click Deferred Copy then paste (Ctrl/Cmd+V) into each of the three paste targets on the right. Watch the callback log — only the format each target consumed triggers its generation callback.
Rich Table Editor
Paste Targets
HTML / Rich text
Click here, then Ctrl+V to paste rich HTML
Plain text
Click here, then Ctrl+V to paste plain text
JSON
Click here, then Ctrl+V to paste JSON
Format callback activity
| Format | Registered | Callback fired | Last paste cost |
|---|---|---|---|
| text/html | — | — | — |
| text/plain | — | — | — |
| text/csv | — | — | — |
| application/json | — | — | — |
Callback log
Ready. Edit the table and click Deferred Copy to begin.
Copies made0
Callbacks fired0
Callbacks avoided0
Cost avoided (est.)0 ms
// Rich text editor registering 4 formats — all deferred
async function deferredCopy(editorEl) {
const item = new ClipboardItem({
// text/html: serialise the contenteditable innerHTML
'text/html': ClipboardItem.delayed('text/html', async () => {
const html = '<table>' + editorEl.innerHTML + '</table>';
log('text/html callback fired — ' + html.length + ' chars');
return new Blob([html], { type: 'text/html' });
}),
// text/plain: strip tags for plain-text paste targets
'text/plain': ClipboardItem.delayed('text/plain', async () => {
const plain = editorEl.innerText;
log('text/plain callback fired — ' + plain.length + ' chars');
return new Blob([plain], { type: 'text/plain' });
}),
// text/csv: walk the table cells and build CSV
'text/csv': ClipboardItem.delayed('text/csv', async () => {
const csv = tableToCSV(editorEl);
log('text/csv callback fired');
return new Blob([csv], { type: 'text/csv' });
}),
// application/json: structured representation
'application/json': ClipboardItem.delayed('application/json', async () => {
const json = JSON.stringify(tableToJSON(editorEl), null, 2);
log('application/json callback fired');
return new Blob([json], { type: 'application/json' });
}),
});
await navigator.clipboard.write([item]);
// None of the callbacks above have fired yet — they only run on paste.
}
// Only the format the paste target accepts will trigger its callback.
// If you copy and never paste: zero callbacks fire, zero serialization cost.