Copy rich text to the clipboard

I've been experimenting with a tool for generating the content for a weekly Substack newsletter by querying the Datasette API for my blog and assembling HTML for the last week of content.

I haven't started sending this out yet, but I figured out how to write rich text to the clipboard as part of my initial prototype.

Substack allows you to paste in rich text (e.g. copied-and-pasted rendered HTML), so it's useful to be able to programatically add rich text to the user's clipboard in order to conveniently paste into Substack.

Initially I tried to get this working using the new Clipboard.write(), but I spotted this warning on the Interact with the clipboard page of MDN:

However, while navigator.clipboard.readText() and navigator.clipboard.writeText() work on all browsers, navigator.clipboard.read() and navigator.clipboard.write() do not. For example, on Firefox at the time of writing, navigator.clipboard.read() and navigator.clipboard.write() are not fully implemented, such that to:

This is a bit tough to read, but the TLDR version is that for rich text copying in Firefox the .write() method doesn't work properly yet.

I actually pasted the above code into ChatGPT as a clue and got it to write me the following code, which I then tidied up and added the document.body.appendChild() and document.body.removeChild() lines (it failed without them):

function copyRichText(html) {
    const htmlContent = html;
    // Create a temporary element to hold the HTML content
    const tempElement = document.createElement("div");
    tempElement.innerHTML = htmlContent;
    document.body.appendChild(tempElement);
    // Select the HTML content
    const range = document.createRange();
    range.selectNode(tempElement);
    // Copy the selected HTML content to the clipboard
    const selection = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(range);
    document.execCommand("copy");
    selection.removeAllRanges();
    document.body.removeChild(tempElement);
}

In an Observable notebook

I used this to add a "copy" button to my Observable notebook like this:

Object.assign(html`<button>Copy rich text newsletter to clipboard`, {
  onclick: () => {
    const htmlContent = newsletterHTML;
    // Create a temporary element to hold the HTML content
    const tempElement = document.createElement("div");
    tempElement.innerHTML = htmlContent;
    document.body.appendChild(tempElement);
    // Select the HTML content
    const range = document.createRange();
    range.selectNode(tempElement);
    // Copy the selected HTML content to the clipboard
    const selection = window.getSelection();
    selection.removeAllRanges();
    selection.addRange(range);
    document.execCommand("copy");
    selection.removeAllRanges();
    document.body.removeChild(tempElement);
  }
})

This depends on some other cell defining newsletterHTML as a string of HTML.

Here's the notebook that uses that.

Created 2023-03-10T22:30:07-08:00 · Edit