This content originally appeared on Stefan Judis Web Development and was authored by Stefan Judis
I discovered a web development magic trick from Cyrus Roshan today. It took me a good 20 minutes to figure out how the trick works, and I learned some things about JavaScript clipboard handling and MIME types. Sounds intriguing? Read on!
Try the magic tricks yourself; I'll wait. 😉
If you didn't try it, here's the flow:
- You're tasked to click an ASCII art play card. The card's characters are automatically copied to your clipboard using JavaScript.
- Then, a new screen tells you to paste the copied ASCII card into a textarea (which works as expected).
- Next, you're told to open a new Google Doc and paste your clipboard content into it (the ASCII art playing card).
- The just copied ASCII art now includes a new line telling you to paste the same content into the URL bar, and boom! 🪄 You just pasted Cyrus' Twitter profile URL.
Whoot? That's magic! 🤯
After creating fifteen different Google documents wondering if Cyros somehow injects JavaScript into Google Docs (which he doesn't), I figured out how this trick works.
Cyros' page leverages a nifty feature of the JavaScript Clipboard API (navigator.clipboard
), and like every magic trick, once you know how it works, it's stupidly simple.
If you've been doing web development long enough, you might remember the document.execCommand('copy')
command. This old way to interact with the clipboard is now deprecated and replaced by the Clipboard API. The newer API has the single purpose of interacting with the clipboard and works asynchronously. Yay!
But does the Clipboard API work everywhere today? At first look, navigator.clipboard
seems to be cross-browser supported...
... but watch out! Looking deeper into it, you'll find out that just because navigator.clipboard
is available, it doesn't mean all functionality is available.
How to place plain text in the clipboard
Putting text into the clipboard is straightforward using the API. Here's an example.
await navigator.clipboard.writeText(
"That's some cool copied text, isn't it?"
);
Click the button below and paste the new clipboard content into the input fields to confirm it works.
[Interactive component: visit the article to see it...]
writeText
covers many standard use cases, but it's not what the magic trick uses. Let's dig deeper!
How to write different MIME types to the clipboard
As seen, placing text on the clipboard is quickly done. But how would you handle images or other text formats such as richtext or HTML? Is it possible to put these into the clipboard with JavaScript, too?
There's another method available to put content in the clipboard – clipboard.write
.
await navigator.clipboard.write([
new ClipboardItem({
'text/plain': new Blob(["That's some cool plain text, isn't it?"], {
type: 'text/plain',
}),
}),
]);
clipboard.write
doesn't accept strings but ClipboardItems
. The main difference between the two methods is that if you want to put anything but plain text into the clipboard, you must define the matching MIME type using a ClipboardItem
.
It's more code to write but still a decent experience in my opinion. Sweet!
Unfortunately, neither navigator.clipboard.write
nor the global ClipboardItem
property is defined in Firefox at the time of writing (both are behind the dom.events.asyncClipboard.clipboardItem
flag).
66 | 66 | 79 | 87* | Non | 13.1 | 13.4 | 9.0 | 66 |
I didn't do the research but if you're looking for a cross-browser solution to place things other than text in the clipboard, I'm sure some libraries have you covered.
And here's annother example to play with. It looks the same as the previous one, but now it uses navigator.clipboard.write
.
Fill up your clipboard!
[Interactive component: visit the article to see it...]
Can you already imagine how the magic trick works now that you've seen some code?
That's right; the trick is based on different content MIME types. Input fields and textareas handle pasted plain text just fine, but there are obviously other available MIME types.
A clipboard could hold types of image/gif
, image/jpeg
, text/rtf
, the good old text/html
, and all sorts of fanciness.
And thanks to the Clipboard API, you're in control of the MIME type and can even store text and images in the same write operation.
And it's not only a single operation; it's even a single clipboard entry.
navigator.clipboard.write([
new ClipboardItem({
'text/plain': new Blob(["That's some cool plain text, isn't it?"], {
type: 'text/plain',
}),
'text/html': new Blob(
[
'<div style="/* some styles */">Oh yeah - text/html!</div>',
],
{
type: 'text/html',
}
),
}),
]);
The example above shows how to put different content as plain text and HTML into your clipboard. 😲
Now it's only a matter of where you paste the content to see this magic in action.
A div
with an contentEditable
attribute can accept and render HTML. 😲 If you paste content with the MIME type text/html
into it, it will render it just fine.
To prove it, hit the button below and see what happens when you paste it into the input fields and the editable div
.
[Interactive component: visit the article to see it...]
Cyrus' trick uses this functionality.
Initially, the magic trick puts plain text into the clipboard, but later on, it stores a ClipboardItem
with multiple MIME types. text/plain
holds his Twitter profile URL and text/html
includes the ASCII art card. Google Docs then renders the pasted HTML, whereas the URL bar renders the plain text.
If you're using MIME types other than text, it's good to provide a text/plain
fallback if your target doesn't understand a particular MIME type.
How to inspect your clipboard
While I was debugging the magic trick, I discovered that inspecting your clipboard isn't straightforward on MacOS. Even though the Finder provides a way to look at what's in the clipboard (Finder > Edit > Show clipboard
), it always shows the plain text entry.
I built a quick clipboard inspector using the Clipboard API's read
methods. And here it became very interesting.
Unfortunately, it's the same story of Firefox not supporting complex clipboard interactions (it's behind another flag – dom.events.asyncClipboard.read
) and even though Safari supports navigator.clipboard.write
it has a surprise for us.
86 | 86 | 79 | 90* | Nope | 13.1 | 13.4 | 12.0 | 84 |
MDN explains to use navigator.read
as follows:
try {
const permission = await navigator.permissions.query({ name: 'clipboard-read' });
if (permission.state === 'denied') {
throw new Error('Not allowed to read clipboard.');
}
const clipboardContents = await navigator.clipboard.read();
for (const item of clipboardContents) {
// do things with the clipboard entries
}
} catch (error) {
console.error(error.message);
}
It works fine in Chromiums, but it turns out that Safari doesn't support navigator.permissions
. 🤦♂️
This means you have to check if navigator.permissions
is available, too. And if it is, ask for permissions and if not, try to use navigator.clipboard.read
anyways.
In this case, Safari shows a little "Paste" mini permission dialog. If you don't click it, navigator.clipboard.read
will throw an exception. Ufff...
Here's a summary on how to use navigator.clipboard.read
:
- For Chromiums you should use the Permissions API.
- You can't read the clipboard content using Firefox.
- In Safari you just have to try it and see if it works.
Have fun with it below.
[Interactive component: visit the article to see it...]
Side note: not all clipboard content is accessible
Inspecting and accessing text-based clipboard content seemed to work fine in Chromiums. But if I copy an image from the MacOS Finder navigator.clipboard.read
doesn't like that either and throws a No valid data on clipboard
exception.
So, if you're planning to use navigator.clipboard.read
, you have to feature detect the Permissions API and also make sure to try/catch
all your read
calls.
This little magic trick became quite a journey. But here's what I learned:
- The Clipboard API allows you to write multiple entries in different MIME types to the clipboard.
- Using the Clipboard API is still a pain if you're targeting all major browsers.
- Not everything in your clipboard is accessible via JavaScript.
If you want to learn more there's also a good article on the async Clipboard API on web.dev
and with this, happy pasting! 👋
Reply to Stefan
This content originally appeared on Stefan Judis Web Development and was authored by Stefan Judis
Stefan Judis | Sciencx (2022-04-27T22:00:00+00:00) A clipboard magic trick – how to use different MIME types with the Clipboard API (#note). Retrieved from https://www.scien.cx/2022/04/27/a-clipboard-magic-trick-how-to-use-different-mime-types-with-the-clipboard-api-note/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.