clipboard copy does not work in jquery ajax success method

I want to copy a card number into the clipboard so that I can paste it in Notepad. The code which I got from the internet works very well if tried in the developer toolbar of the browser. However if I add that code into my Javascript file and run the project then it does not work. Following is the code:

$.ajax({
  type: "POST",
  url: '@Url.Action("CopyToClipboard", "MyAccountSurface")',
  data: {
    controlId: controlId
  },
  dataType: 'json',
  success: function(data) {
    var $temp = $("<input>");
    $("body").append($temp);
    $temp.val(data.CardNumber).select();
    document.execCommand("copy");
    $temp.remove();
    alert('copied successfully');
  }
});

Answers:

Answer

Updated:

User interaction is mandatory to execute document.execCommand. So in your case it is not possible to copy the text from AJAX Response. It is the security measure that browsers agreed upon.

Refer W3C API

Copy and cut commands triggered through a scripting API will only affect the contents of the real clipboard if the event is dispatched from an event that is trusted and triggered by the user, or if the implementation is configured to allow this.


A workaround with user interaction

Steps added:

  • Place a text box far away from webpage using relative position.
  • Add a button in a disabled state. Once data is available re-enable the button.
  • On button click you will be able to perform document.execCommand since you are directly interacting with browser (Hence no security issue as mentioned in API)

$(document).ready(function() {
  $.ajax({
    url: 'https://jsonplaceholder.typicode.com' + '/posts/1',
    method: 'GET' 
  }).then(function(data) {
    console.log(data);
    $('#toBeCopied').val(data.title);
    $("#copyIt").attr('disabled', null);
  });

  
});
function copyToClipboard(){
    var $temp = $("<input />");
    $("body").append($temp);
    $temp.val($("#toBeCopied").val()).select();
    var result = false;
    try {
        result = document.execCommand("copy");
    } catch (err) {
        console.log("Copy error: " + err);
    }
    $temp.remove();
  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<input type="text" value="dummy" id="toBeCopied" style="display:none; position: relative; left: -1000px;">
<b>Below button will be enabled once the data is available from AJAX</b>
<button id="copyIt" onclick="copyToClipboard()" disabled>Copy To Clipboard</button>

Answer

If you want to copy into clipboard on click with Ajax

Element, which you are going to click has few events: mousedown and click. And they are triggered in this order. It means that you can send ajax request in first one and work with result in last one and in this case you will not have security problems.

Let me share working example:

  $link.on("mousedown", function() {
    var url = $(this).data("url");
    var $temp = $("<input id='copy_container' style='position:fixed;left:-200px;'>");

    $.ajax({
      url: url,
      dataType: "json",
      success: function (data) {
        $("body").append($temp);
        $temp.val(data.text);
      }
    })
  })

  $link.on("click", function() {
    setTimeout(function() {
      var $input = $("input#copy_container");
      if ($input.length && $input.val().length > 0) {
        $input.select();
        document.execCommand("copy");
        $input.remove();
      }
    }, 100)
  })

You need this timeout with 100ms to wait for ajax response. It can be improved like you want.

Fixed and negative position - I think you know why we need it.

Answer

Well, what are you copying? document.execCommand("copy"); requires something to be selected(highlighted) in the first place.

I think in your example, select follows .val(). But in order for this to work you need to be selecting an element, not it's value.

$temp.val(data.CardNumber);
$temp.select();

copied = document.execCommand("copy");
$temp.remove();

if(copied){
    alert('copied successfully');
}else{
    alert('something went wrong');
}
Answer
var response;

// recursively using setTimeout to wait for response 
var recCopy = function (){
    if(response){
        copy(response); 
        return;
    }     
    else {
        setTimeout(recCopy,2000); // for e.g. 2ms
    }
}

function copy(value) {
    var tempInput = document.createElement("input");
    tempInput.style = "position: absolute; left: -1000px; top: -1000px";
    tempInput.value = value;
    document.body.appendChild(tempInput);
    tempInput.select();
    try {
            var successful = document.execCommand('copy',false,null);
            var msg = successful ? 'successful' : 'unsuccessful';
            console.log('Copying text command was ' + msg);
        } catch (err) {
            alert('Oops, unable to copy to clipboard');
        }
    document.body.removeChild(tempInput);
}

$('.copyToClipboard').on('mousedown',function (e){
        $.ajax({
          url: "https://www.example.com/xyz",
          data: {
             formData :formData
          },
          type : 'post',
          success: function (data) {     
            response = data;  // on success, update the response
          } 
        }) 
});

$('.copyToClipboard').on('click',function (e){
    recCopy();
});

ajax call and copying response need to be handled separately (for e.g click and mousedown events have been handled here).
setTimeout can be recursively used to check for response. Once response is received, copy to clipboard can be executed.

Answer

As this is not a user interaction, it will not work.

The workarounds which we can implement is getting the data from the ajax call as soon as the mouseenter in the area where user wants to copy the data & place the data in some textarea or input box.

And, on the click event we can copy the data in the clipboard.

//mouseenter event of a button

$("#jq-copy-txt").on('mouseenter', function() {
    $.ajax({
      url: 'url',
      method: 'GET'
    }).then(function(data) {
       let copyFrom = document.getElementById("jq-cpy-txt-area"); 
        document.body.appendChild(copyFrom);
        copyFrom .textContent = data.title;
    });
});

// click event fired by user

$("#jq-copy-txt").on('click', function() {

   var fn = function() {
      let copyFrom = document.getElementsByTagName("textarea")[0];
      copyFrom.select();
      document.execCommand("copy");
    };
    setTimeout(fn, 1000);});
Answer

What @Anton said was good but it is all bad practice because You are depending on the server to give You a response at a given time which is bad, You can see in all the big websites that have complicated backend that they just put it in an HTML object so the user could copy it via ctrl+c or via button click, I would've done it a lil bit differently than Antons way

$('#btn').on('click', function(e){

    var url = 'Your-link.com';
    var $temp = $("<input id='copy_container' 
        style='position:fixed;left:-200px;'>");
    $.ajax({
      type: "POST",
      url: url,
      success: function(res) {
        $("body").append($temp);
        $temp.val(res);
      },
      error: function() {
         //handle error and do something
      }
    });
    setTimeout(function() {
        var $input = $("input#copy_container");
        if ($input.length && $input.val().length > 0) {
            $input.select();
            document.execCommand("copy");
            $input.remove();
        }
    }, 500)
});   

this way You dont need to reuse event listeners, yet like I said before its far from perfect. Better put it in a HTML element shown to the user.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.