clipboard copy does not work in jquery ajax succes

2020-04-10 11:02发布

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');
  }
});

6条回答
做个烂人
2楼-- · 2020-04-10 11:22

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');
}
查看更多
看我几分像从前
3楼-- · 2020-04-10 11:28

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.

查看更多
Luminary・发光体
4楼-- · 2020-04-10 11:29

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.

查看更多
混吃等死
5楼-- · 2020-04-10 11:36

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);});
查看更多
家丑人穷心不美
6楼-- · 2020-04-10 11:43

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>

查看更多
我欲成王,谁敢阻挡
7楼-- · 2020-04-10 11:48
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.

查看更多
登录 后发表回答