I want clicking my Chrome extension’s browser action button to copy some text, but the methods used on websites don’t work.
(I realize there are some similar questions, however they answer copying text in injected scripts or browser action popup scripts, not browser action scripts.)
manifest.json:
{
"name": "Example",
"version": "0.0.0",
"manifest_version": 2,
"permissions": ["clipboardWrite"],
"browser_action": {"default_title": "Copy some text"},
"background": {
"scripts": ["events.js"],
"persistent": false
}
}
events.js:
chrome.browserAction.onClicked.addListener(_ => {
copy(new Date().toISOString().slice(0, 19))
})
// Define copy() here.
Don’t define copy and hope it’s defined globally as in the console.
It isn’t.
function copy(text) {
navigator.clipboard.writeText(text)
}
This fails with the error message “DOMException: Document is not focused”.
function copy(text) {
focus()
navigator.clipboard.writeText(text)
}
This behaves the same as approach 2.
function copy(text) {
const ta = document.createElement('textarea')
ta.value = text
ta.select()
document.execCommand('copy')
ta.remove()
}
This fails without an error message.
Inject a copying script into the active tab. I didn’t code this one because it would break if there are no accessible active tabs, tabs messed with their globals, JS is paused on the active tab, etc, and would also need excessive permissions.
function copy(text) {
open('copy.html?' + encodeURIComponent(text), '', 'width=1,height=1')
}
(Setting width and height forces opening a window, not a tab, in order to preserve the user’s tab selection and reduce visual impact.)
copy.html:
<!doctype html>
<meta charset="utf-8">
<title>Copying…</title>
<div></div>
<script src="copy.js"></script>
copy.js:
(async _ => {
await navigator.clipboard.writeText(decodeURIComponent(location.search.slice(1)))
close()
})()
This works but isn’t great because it’s visually glitchy, slow, and circuitous.
The textarea element should be added to the live DOM first e.g. to document.body and focused because execCommand operates on document.activeElement. You can hide the textarea so it doesn't flicker.
function copy(text) {
const ta = document.createElement('textarea');
ta.style.cssText = 'opacity:0; position:fixed; width:1px; height:1px; top:0; left:0;';
ta.value = text;
document.body.appendChild(ta);
ta.focus();
ta.select();
document.execCommand('copy');
ta.remove();
}
Might wanna set a few more CSS props to none or 0 like border/padding/margin just in case.
@wOxxOm’s answer is correct for manifest v2, but manifest v3 unfortunately made it even worse. The manifest v3 solution:
{
"manifest_version": 3,
"name": "Copy on Action Example",
"version": "1.0.0",
"permissions": ["clipboardWrite", "offscreen"],
"background": {"service_worker": "events.js"},
"action": {"default_title": "Copy “Hello, world!”"}
}
chrome.action.onClicked.addListener((tab) => {
copy('Hello, world!');
});
async function copy(text) {
await chrome.offscreen.createDocument({
url: chrome.runtime.getURL('copy.html'),
reasons: ['CLIPBOARD'],
justification: 'Required by Chrome to copy text.',
});
chrome.runtime.sendMessage(text);
}
<!doctype html>
<title>Copy – Copy on Action Example</title>
<textarea id="ta"></textarea>
<script src="copy.js"></script>
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
const ta = document.querySelector('#ta');
ta.value = message;
ta.select();
document.execCommand('copy');
close();
});
copy and navigator.clipboard.writeText are unavailable, even if you try to focus the document. Chrome seems to be planning on supporting navigator.clipboard.writeText in background pages at some point, maybe, so you might want to test it when you read this and leave a comment if it’s been added.document.execCommand method no longer works in it. The chrome.offscreen API was added in 2023 to fill this gap.chrome.runtime.getContexts to see if it’s still around.{command: 'copy', text: 'some text'}, to allow sendMessage to be used for other commands, too.document.execCommand is actually deprecated, but I guess we’re meant to care about that about as much as Chrome does.chrome.offscreen docsIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With