206

Why is navigator.clipboard always undefined in the following snippet?

var clipboard = navigator.clipboard;
if (clipboard == undefined) {
    console.log('clipboard is undefined');
} else {
    clipboard.writeText('stuff to write').then(function() {
        console.log('Copied to clipboard successfully!');
    }, function() {
        console.error('Unable to write to clipboard. :-(');
    });
}

More on the clipboard API can be found here.

Chrome Version: 68.0.3440.106.

I'm sure this was working at some point, but no longer is. It's confusing because this table suggests that the Clipboard API is implemented in Chrome (has been for some time), but this table of specific API methods suggests that none of the methods of the API is supported??

3
  • 11
    Is the page served from a secure origin?
    – Josh Lee
    Commented Aug 13, 2018 at 12:16
  • 5
    Aaaaah @JoshLee.... that makes the difference... the origin is secured (accessible via https) but for some reason I was accessing it via http... when I access via https the clipboard object is available! This explains why I was convinced that it had been working previously. Perhaps I should forward all http to https. Thanks... if you submit an answer I'll mark it as correct.
    – drmrbrewer
    Commented Aug 13, 2018 at 12:54
  • I had the same issue, but the given solution given here does not work. Until I found that the text-area or text field may NOT be DISABLED!!
    – Harm
    Commented Mar 2, 2020 at 14:48

9 Answers 9

291

This requires a secure origin — either HTTPS or localhost (or disabled by running Chrome with a flag). Just like for ServiceWorker, this state is indicated by the presence or absence of the property on the navigator object.

https://developers.google.com/web/updates/2018/03/clipboardapi

This is noted in the spec with [SecureContext] on the interface: https://w3c.github.io/clipboard-apis/#dom-navigator-clipboard

You can check the state of window.isSecureContext to learn if that's the reason a feature is unavailable. Secure contexts | MDN

And yes, you should set up HSTS to make sure HTTP redirects to HTTPS.

4
  • 7
    BTW, the flag to disable TEMPORARILY https check is eg. --unsafely-treat-insecure-origin-as-secure=myexamplesite.com.
    – RikiRiocma
    Commented Feb 3, 2020 at 14:04
  • 36
    In chrome://flags, enable Insecure origins treated as secure and give it the origin you want.
    – Rokit
    Commented Dec 7, 2020 at 20:44
  • 1
    Doesn't work running chrome headlessly no matter what I throw at it
    – danronmoon
    Commented May 16, 2022 at 15:29
  • The advice to use HSTS in the context of the question is really bad advice. Commented Jan 25 at 15:24
148

You can write an all-in-one wrapper function.

  • if in secure context (https): use navigator clipboard api
  • if not: use the 'out of viewport hidden text area' trick
async function copyToClipboard(textToCopy) {
    // Navigator clipboard api needs a secure context (https)
    if (navigator.clipboard && window.isSecureContext) {
        await navigator.clipboard.writeText(textToCopy);
    } else {
        // Use the 'out of viewport hidden text area' trick
        const textArea = document.createElement("textarea");
        textArea.value = textToCopy;
            
        // Move textarea out of the viewport so it's not visible
        textArea.style.position = "absolute";
        textArea.style.left = "-999999px";
            
        document.body.prepend(textArea);
        textArea.select();

        try {
            document.execCommand('copy');
        } catch (error) {
            console.error(error);
        } finally {
            textArea.remove();
        }
    }
}

Usage:

try {
    await copyToClipboard("I'm going to the clipboard !");
    console.log('Text copied to the clipboard!');
} catch(error) {
    console.error(error);
}

PS: do not try it in a repl like jsfiddle/copeden/...  

4
  • 4
    cool, a little concisely: textArea.style.position = "absolute"; textArea.style.opacity = 0; and I think the focus call is unnecessary
    – NanoNova
    Commented Mar 13, 2021 at 7:53
  • 8
    any other way since the document.exeCommand has been deprecated
    – Colasanto
    Commented Sep 13, 2021 at 14:49
  • 1
    Thank you for explaining https context; this was a mystery blocker in other examples.
    – Kalnode
    Commented Jan 20, 2022 at 18:47
  • Unfortunately I keep getting DOMException: Document is not focused. :( Calling it from chrome code snippets.
    – Kamajabu
    Commented Mar 12, 2022 at 18:30
16

Try this:

if (typeof (navigator.clipboard) == 'undefined') {
    console.log('navigator.clipboard');
    var textArea = document.createElement("textarea");
    textArea.value = linkToGo;
    textArea.style.position = "fixed";  //avoid scrolling to bottom
    document.body.appendChild(textArea);
    textArea.focus();
    textArea.select();

    try {
        var successful = document.execCommand('copy');
        var msg = successful ? 'successful' : 'unsuccessful';
        toastr.info(msg);
    } catch (err) {
        toastr.warning('Was not possible to copy te text: ', err);
    }

    document.body.removeChild(textArea)
    return;
}
navigator.clipboard.writeText(linkToGo).then(function () {
    toastr.info(`successful!`);
}, function (err) {
    toastr.warning('unsuccessful!', err);
});
1
  • 1
    document.execCommand('copy'); is depricated
    – key
    Commented Apr 7, 2022 at 9:34
6

In localhost, the clipboard is blocked by the chrome browser. You check this by going to the following path

Chrome > settings > privacy and Security > site settings > View permissions and data stored across sites then click on your localhost URL which will mentation on the page and check the permission of the clipboard

6

A minimal solution for copying tooltips when HTTPS is not yet available and the solution with document.execCommand('copy') does not work. But it requires that the user selects and copies by hand what is displayed in the alert.

function copyToClipboard(text) {
    if(navigator.clipboard) {
        navigator.clipboard.writeText(text);
    }
    else{
        alert(text);
    }
}
7
  • I'm using chrome v105, cannot select alert text :(
    – leverglowh
    Commented Sep 13, 2022 at 15:04
  • Hello, I just tested it on Ubuntu 22.04 and it worked : Version 105.0.5195.102. It seems strange Chrome would remove this feature. Commented Sep 13, 2022 at 17:43
  • Maybe you're using Chrome on smartphone where selecting text can be cumbersome ? Commented Sep 13, 2022 at 17:44
  • 1
    It's probably something related to this bug support.google.com/chrome/thread/9959602/… my text is in fact pretty long, I used a prompt instead :)
    – leverglowh
    Commented Sep 14, 2022 at 16:28
  • How does alert() function serve for this purpose?
    – Si8
    Commented Aug 10, 2023 at 11:33
4
const copy = (text: string) => {
  if (navigator.clipboard) {
    navigator.clipboard.writeText(text)
  } else {
    const input = document.createElement('textarea')
    input.value = text
    document.body.appendChild(input)
    input.select()
    document.execCommand('copy')
    document.body.removeChild(input)
  }
}
2

This solutions works at the moment (it includes cross browser support, error handling + clean up).

https://stackoverflow.com/a/33928558/318380

3
  • Seem does not work anymore
    – key
    Commented Apr 7, 2022 at 9:32
  • Beautiful, it tries 3 automatic methods, if they fail, it displays a manual alert. It can be improved with a bit of reworking to your needs, but overall it is the best solution I have seen so far (searching for an hour) Commented Apr 11, 2022 at 19:16
  • Link-only answers are not considered valuable on SO. If that answer is correct the question should be flagged as a duplicate.
    – isherwood
    Commented Jun 24, 2022 at 18:43
0

as mentioned early "Clipboard API" only works in secure context.

in prod environment you need enable https, in order to works.

How to use in localhost:

If you want to enable for your local dev env, you need enable secure context first:

Enable Insecure origins treated as securein Chrome:

use this url in chrome:

chrome://flags/#Insecure-origins-treated-as-secure

then add your local domains and enable:

enter image description here

maybe you need re-start chrome

then when your visit your site allow copy

enter image description here

now you able to use:

navigator.clipboard.readText().then(pastedData => {
        console.log(pastedData);
});
-4

you can use :

change the :

navigator.clipboard.writeText("Content")

to : navigator['clipboard'].writeText("Content") instead.

3
  • 2
    The change you suggested is not a change. Both line of code are identical. They do the same thing. It's just a different way to instruct the browser to do the the same thing.
    – 0xC0DEGURU
    Commented Jan 16, 2023 at 9:51
  • but in my case it's work Commented Apr 15, 2023 at 12:51
  • its probably working for you because your testing it via https. as mentioned by the above answer, this is cause by an unsecure (http) page. Commented Jul 3, 2023 at 12:15

Not the answer you're looking for? Browse other questions tagged or ask your own question.