|
| 1 | +# Copy rich text to the clipboard |
| 2 | + |
| 3 | +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. |
| 4 | + |
| 5 | +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. |
| 6 | + |
| 7 | +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. |
| 8 | + |
| 9 | +Initially I tried to get this working using the new [Clipboard.write()](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/write), but I spotted this warning on the [Interact with the clipboard](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboard) page of MDN: |
| 10 | + |
| 11 | +> 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: |
| 12 | +> |
| 13 | +> - work with images use `browser.clipboard.setImageData()` to write images to the clipboard and `document.execCommand("paste")` to paste images to a webpage. |
| 14 | +> - write rich content (such as, HTML, rich text including images, etc.) to the clipboard, use `document.execCommand("copy")` or `document.execCommand("cut")`. Then, either `navigator.clipboard.read()` (recommended) or `document.execCommand("paste")` to read the content from the clipboard. |
| 15 | +
|
| 16 | +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. |
| 17 | + |
| 18 | +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): |
| 19 | + |
| 20 | +```javascript |
| 21 | +function copyRichText(html) { |
| 22 | + const htmlContent = html; |
| 23 | + // Create a temporary element to hold the HTML content |
| 24 | + const tempElement = document.createElement("div"); |
| 25 | + tempElement.innerHTML = htmlContent; |
| 26 | + document.body.appendChild(tempElement); |
| 27 | + // Select the HTML content |
| 28 | + const range = document.createRange(); |
| 29 | + range.selectNode(tempElement); |
| 30 | + // Copy the selected HTML content to the clipboard |
| 31 | + const selection = window.getSelection(); |
| 32 | + selection.removeAllRanges(); |
| 33 | + selection.addRange(range); |
| 34 | + document.execCommand("copy"); |
| 35 | + selection.removeAllRanges(); |
| 36 | + document.body.removeChild(tempElement); |
| 37 | +} |
| 38 | +``` |
| 39 | +## In an Observable notebook |
| 40 | + |
| 41 | +I used this to add a "copy" button to my Observable notebook like this: |
| 42 | +```javascript |
| 43 | +Object.assign(html`<button>Copy rich text newsletter to clipboard`, { |
| 44 | + onclick: () => { |
| 45 | + const htmlContent = newsletterHTML; |
| 46 | + // Create a temporary element to hold the HTML content |
| 47 | + const tempElement = document.createElement("div"); |
| 48 | + tempElement.innerHTML = htmlContent; |
| 49 | + document.body.appendChild(tempElement); |
| 50 | + // Select the HTML content |
| 51 | + const range = document.createRange(); |
| 52 | + range.selectNode(tempElement); |
| 53 | + // Copy the selected HTML content to the clipboard |
| 54 | + const selection = window.getSelection(); |
| 55 | + selection.removeAllRanges(); |
| 56 | + selection.addRange(range); |
| 57 | + document.execCommand("copy"); |
| 58 | + selection.removeAllRanges(); |
| 59 | + document.body.removeChild(tempElement); |
| 60 | + } |
| 61 | +}) |
| 62 | +``` |
| 63 | +This depends on some other cell defining `newsletterHTML` as a string of HTML. |
| 64 | + |
| 65 | +Here's [the notebook](https://observablehq.com/d/81869763464a0735) that uses that. |
0 commit comments