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