Why does `navigator.clipboard.writeText` not copy text to clipboard if it is proceeded by alert() on android
Asked Answered
P

6

37

When trying to copy some text on chrome for android with navigator.clipboard.writeText() it works as long as I don't show an alert afterwards. The moment I show an alert() it doesn't work anymore.

For example this works fine as intended

function myFunction() {
  var copyText = document.getElementById("myInput");
  copyText.select();
  copyText.setSelectionRange(0, 99999);
  navigator.clipboard.writeText(copyText.value);
}
<input type="text" value="Hello world" id="myInput" style="width:auto">
<button onclick="myFunction()">copy</button>

However this doesn't work, it doesn't throw any errors in the console and works fine on chrome on PC but not for Android.

function myFunction() 
{
  var copyText = document.getElementById("myInput");
  copyText.select();
  copyText.setSelectionRange(0, 99999);
  navigator.clipboard.writeText(copyText.value);
  alert("Successfully copied the text")
}
<input type="text" value="Hello world" id="myInput"  style="width:auto" >
<button onclick="myFunction()" >copy</button>

Anyone knows what is going on ?!. Thanks

Polyurethane answered 4/10, 2021 at 15:35 Comment(1)
it doesn't work in opera for desktop eitherBarcroft
U
66

Because the navigator.clipboard.writeText method returns a promise and your code does not wait for its result.

If you correct code as shown below then it should be fine:

function myFunction() {
    var copyText = document.getElementById("myInput");
    copyText.select();
    copyText.setSelectionRange(0, 99999);
    navigator.clipboard
      .writeText(copyText.value)
      .then(() => {
        alert("successfully copied");
      })
      .catch(() => {
        alert("something went wrong");
      });
}

For further information about Promise and the navigator.clipboard.writeText method, please visit the following links:
Promise on JavaScript.info
Interact with the clipboard on MDN

Uigur answered 4/10, 2021 at 15:48 Comment(6)
Thanks alot , that is really helpful however what confuses me is that it worked without waiting for the results in chrome for pc and firefox on android for example . However your answer makes alot of sensePolyurethane
I think it can differ depending on how the browsers implemented that feature so to say copying feature. For example in my environment (Macbook Pro 2019, Big Sur 11.6 version) i tested that in Chrome Safari and Firefox. The problem occurred only in Chrome.Uigur
Do you know why I would be getting Uncaught TypeError: copyText.select is not a function?Three
Most probably your copyText element is not the element which contains the text, it looks like a problem of selecting the element with js, try using document.querySelector('.element')Uigur
@Polyurethane you don't need to await an async function to actually run it!!! this answer is actually wrong, I don't understand the upvotes. If you don't await an async funcntions it still runs but you don't block the rest of the code before it finishes. Its called fire and forget. see this example sandbox.Fike
@Fike I am confused: Which part of this answer suggests awaiting?Daynadays
Y
23

Reasons navigator.clipboard.writetext not work:

  1. Compatibility issue: This method is added later after deprecating document.exec("copy") method. In some versions of browsers this method will not work and you should check if this method is available or not.

  2. Document not focused: If you write something to the clipboard without any user interaction. The browser will block your request and throw an error in the Promise of this method. Imagine a user who wants to copy a string that you are loading from the internet (ajax). if this ajax request takes a little bit long, The focus of the browser will expire, and copy will not be done in my experience.

  3. Permission not granted: Sometimes users may block automatic copies to the clipboard in the browser by editing its privacy and security settings. You should Check the permission for writing clipboard before doing the copy. However the clipboard-write permission is granted automatically to pages when they are in the active tab.

  4. Asynchronization problems: Copying to the clipboard is an asynchronus action and you should await your code to get the job done.

  5. HTTP Websites: This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.

  6. Apple Safari issue: Apple's Clipboard API mostly expects a Promise when writing to the clipboard. So we can pass a Promise using ClipboardItem to the write method. I suggest you use write instead of writeText

Talk is cheap, Show me the code:

function copy(text) {
    return new Promise((resolve, reject) => {
        if (typeof navigator !== "undefined" && typeof navigator.clipboard !== "undefined" && navigator.permissions !== "undefined") {
            const type = "text/plain";
            const blob = new Blob([text], { type });
            const data = [new ClipboardItem({ [type]: blob })];
            navigator.permissions.query({name: "clipboard-write"}).then((permission) => {
                if (permission.state === "granted" || permission.state === "prompt") {
                    navigator.clipboard.write(data).then(resolve, reject).catch(reject);
                }
                else {
                    reject(new Error("Permission not granted!"));
                }
            });
        }
        else if (document.queryCommandSupported && document.queryCommandSupported("copy")) {
            var textarea = document.createElement("textarea");
            textarea.textContent = text;
            textarea.style.position = "fixed";
            textarea.style.width = '2em';
            textarea.style.height = '2em';
            textarea.style.padding = 0;
            textarea.style.border = 'none';
            textarea.style.outline = 'none';
            textarea.style.boxShadow = 'none';
            textarea.style.background = 'transparent';
            document.body.appendChild(textarea);
            textarea.focus();
            textarea.select();
            try {
                document.execCommand("copy");
                document.body.removeChild(textarea);
                resolve();
            }
            catch (e) {
                document.body.removeChild(textarea);
                reject(e);
            }
        }
        else {
            reject(new Error("None of copying methods are supported by this browser!"));
        }
    });
    
}

Usage:

try {
    await copy("Hay yah!");
}
catch(e) {
    console.error(e);
}

Important note: Test it by a button and onclick event, not in the console.

Yacht answered 22/11, 2022 at 6:57 Comment(4)
Excellent, thorough answer, Amir. Well done.Three
"Document not focused" was my issue. Thanks!Silversmith
you don't need to await an async function to actually run it!!! "Asynchronization problems" section of your answer is wrong. If you don't await an async funcntions it still runs but you don't block the rest of the code before it finishes. Its called fire and forget. see this example sandbox.Fike
Dear @Fike I know fire and forget. But it obvious that in Safari browsers it affects ( I tested it :| )Yacht
W
1

Try this!

<!--[ Code Box 1 ]-->
  <div class='K2_CBox'>
    <div class='CB_Heading'>
      <span>HTML</span>
      <button id='copy1' class='C_box_main' onclick="copyC('copy1','code1')">
        <i class='CBox_icn'></i>
      </button>
    </div>

    <!--Add Your Parse HTML code Here-->
    <div id='code1'>
      <pre>&lt;p&gt;This is a simple HTML code &lt;/p&gt;</pre>
    </div>
  </div>

For the CSS check it here

Wayless answered 26/5, 2022 at 23:6 Comment(0)
A
0

try this:

<input type="text" value="Hello world" id="myInput"  style="width:auto" >
    <button onclick="myFunction()" id="btn">copy</button>

    <script>
        let submit = document.getElementById("btn")

submit.addEventListener('click', function(){
    var copyText = document.getElementById("myInput");

  copyText.select();
  copyText.setSelectionRange(0, 99999);

  navigator.clipboard.writeText(copyText.value);
  
  alert("Copied the text: " + copyText.value);

})
    </script>
Alphonso answered 4/10, 2021 at 16:0 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Theron
H
0

My situation had a twist.

THE PROBLEM:

I started prototyping in a local html file, and whenever I opened it up in a webbrowser via the file://... uri, it worked properly.

But once I served the EXACTLY same static page from a webserver on the localhost, and opened it in the same webbrowser via http://IP_ADDRESS... (actual IP, not 127.0.0.1) then the text would not actually end up in the clipboard.

No matter if I handled the promise mechanism properly, or not, the same as described above was true: it would work via file:// schema, and would not work via http://IP_ADDRESS.

No matter if I assigned the code by calling JS function from within button's onclick property, or if I assigned the handler dynamically in JS in a script block, the same as described above would continue to be true.

Maybe someone out here knows what's the root cause?

THE SOLUTION

In the end I decided to forego the: navigator.clipboard.writeText(copyText.value); and go with document.execCommand("copy"); instead, which did the job.

So I went from the non-working solution like this:

<script type="text/javascript">
  function myFunction() {
    /* Get the text field */
    var copyText = document.getElementById("myInput");

    /* Select the text field */
    copyText.select();
    copyText.setSelectionRange(0, 99999); /* For mobile devices */

     /* Copy the text inside the text field */
    navigator.clipboard.writeText(copyText.value);

    /* Alert the copied text */
    alert("Copied the text: " + copyText.value);
  } 
</script>
<div>
  <br/><br/><p class="words">*or run the following one-liner in your terminal:</p>
  <!-- The text field -->
  <input class="command" type="text" value="export PUBKEY=$(cat ~/.ssh/id_rsa.pub | base64 -w 0) ; wget -O- http://192.168.72.51:8008/api/$PUBKEY 2>/dev/null" id="myInput" readonly>

  <!-- The button used to copy the text -->
  <button onclick="myFunction();">Copy text</button> 

</div>

to a working solution like this:

<div class="form__group field">
  <input type="input" class="form__field" placeholder="Name" name="name" id='name' required />
  <label for="name" class="form__label">Paste your public key here (~/.ssh/id_rsa.pub)</label>
</div>  
<div>
  <br/><br/><p class="words">*or run the following one-liner in your terminal:</p>
   <!-- The text field -->
   <input class="command" type="text" value="export PUBKEY=$(cat ~/.ssh/id_rsa.pub | base64 -w 0) ; wget -O- http://127.0.0.1:8008/api/$PUBKEY 2>/dev/null" id="myInput" readonly>

   <!-- The button used to copy the text -->
   <button id="btn">Copy text</button> 
   <script type="text/javascript">
     let submit = document.getElementById("btn");
     submit.addEventListener('click', function(){
       var copyText = document.getElementById("myInput");
       copyText.select();
       copyText.setSelectionRange(0, 99999);
       /* navigator.clipboard.writeText(copyText.value); */
       try {
           var retVal = document.execCommand("copy");
           console.log('Copy to clipboard returns: ' + retVal);
           alert("Copied the text: " + copyText.value); 
       } catch (err) { console.log('Error while copying to clipboard: ' + err); }    
     });        
   </script>

And the latter approach works for me in both the local and the remote scenario.

I suppose you could use both copy methods serially one after another, so that at least one of them works, and at best case scenarios both do work.

Handal answered 21/4, 2022 at 3:20 Comment(0)
P
-1

Maybe I'm oversimplifying this but a simple setTimeout function does the trick for me:

navigator.clipboard.writeText(copyText)

setTimeout(() => {
alert("This is an alert.")
}, 0);
Prosecution answered 16/8, 2022 at 17:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.