Why Isn’t Clipboard.write() Copying My RichText/HTML?

Having issue about copying HTML to the clipboard in JavaScript using Clipboard.write()? So did I, and this write up follows how I solved the issue.

Why Isn’t Clipboard.write() Copying My RichText/HTML?

I was working on bookmarklets one afternoon and instead of using the now deprecated document.execCommand("copy") I wanted to try the new Clipboard.write() call being introduced into the web standards.

However, some of what I wanted to copy was formatted HTML with the code looking something like:

const richTextDiv = document.getElementById("richTextDiv");

const clipboardItem = new ClipboardItem({
	"text/html": new Blob(
		[richTextDiv.outerHTML],
		{ type: "text/html" }
	),
});

navigator.clipboard.write([clipboardItem]);

On running this code nothing ended up on the clipboard. Making sure the API did work on Chrome 98 (the latest at the time) I tested the Clipboard.writeText() call, which worked as expected for plaintext.  

An afternoon worth of reading StackOverflow, the W3 spec, release notes, and a look into clipboard-polyfill I realised that all examples (bar one Safari one) also included a fallback text/plain version. Then digging deeper into the spec:

[...] Implementations should create alternate text/html and text/plain clipboard formats [...]

This means the following at the time of writing this:

  • If the MIME Type of the text is text/html then also have a fallback of type text/plain.
  • That the clipboard can have multiple MIME Types of the same data 🤯

To then fix the code above, I would need to grab both the outerHTML of the div and the text representation - for this case it can just be innerText:

const richTextDiv = document.getElementById("richTextDiv");

const clipboardItem = new ClipboardItem({
	"text/plain": new Blob(
		[richTextDiv.innerText],
		{ type: "text/plain" }
	),
	"text/html": new Blob(
		[richTextDiv.outerHTML],
		{ type: "text/html" }
	),
});

navigator.clipboard.write([clipboardItem]);

And this works as expected. If you want to know more, you can dig into this raw looking repo for more examples:

GitHub - nikouu/Web-API-Clipboard.write-Examples: Examples using the Web API Clipboard.write() function specifically for the MIME Type plain/html.
Examples using the Web API Clipboard.write() function specifically for the MIME Type plain/html. - GitHub - nikouu/Web-API-Clipboard.write-Examples: Examples using the Web API Clipboard.write() fun...

I hope this helped save you an afternoon of staunch debugging. If you have any feedback around this changing API and what I might be doing oddly, feel free to check my about page to see how to contact me.

Edit 1:
Looking at the Chromium Android unit tests around the clipboard, it seems to use newHtmlText() which requires a plaintext version of the HTML being copied as well.

Edit 2:
The Chromium unit test for the async clipboard writing API also has both plain and HTML.

Edit 3:
Even by default the browser will populate at least text and HTML on the clipboard. The following was via manually copying some HTML text:

Using C# to view the clipboard properties.

Thank you to this StackOverflow answer by Panagiotis Kanavos for helping me understand the clipboard magic for HTML.