Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to programmatically copy asynchronous dependent content to the clipboard following a click?

I'm trying to programmatically use the execCommand in Chrome (Build 43) to copy the result of an asynchronous JSONP request to the clipboard. Here is a snippet of the logic :

loadContent()

function loadContent(callback) {
  $.getJSON('http://www.randomtext.me/api/lorem/p-5/10-20?&callback=myFunc',function(result){
    console.log('result=',result.text_out);
    $("#container").html(result.text_out);
    if (callback) {
      callback();
    }
  });
}

function copyAjax() {

 loadContent(copy);

}

function copy() {
  var copyDivText = $('#container').text();
  console.log('copyDivText=',copyDivText);
  executeCopy(copyDivText);
}

document.addEventListener("DOMContentLoaded", function(){
      document.getElementById("copy").onclick = copy;
    });


document.addEventListener("DOMContentLoaded", function(){
      document.getElementById("copyAjax").onclick = copyAjax;
    });


// Copy text as text
function executeCopy(text) {
    var input = document.createElement('textarea');
    document.body.appendChild(input);
    input.value = text;
    input.focus();
    input.select();
    document.execCommand('Copy');
    input.remove();
}

I know that starting build 43 of Chrome you code use the execCommand with clipboard. The problem, however is that you need to do it in within the execution of a user originated event (In which the permissions are elevated). This is a similar restriction that ZeroClipboard flash based solution has. Except from getting an answer that it is not possible (which is what I ponder about now), these are the other options I thought of doing as last resort(warning, they are all Hail Mary Passes):

  1. Since JSONP cannot be synchronous, turn it to something that uses regular AJAX call and make sure that AJAX call is synchronous within the execution context of the user event. This goes against my deeply rooted belief we should not do synchronous XHR calls since it degrades user experience.
  2. As the user approaches with the mouse to the copy button, we preemptively send the server request and hope it's fast enough before the user clicks the button. This is an obvious race condition which might not part of the time and will not work for certain when the use wants to do a Ctrl/Command-C instead of clicking the copy button.
  3. Perform a two step process. One click to trigger the call, when the content is available, show a message the content is available and press another click on the msg area to copy to clipboard. It does not seem like the best UX interaction ever. I've created this example with this alternative.Triggering a click programmatically does not constitute a user issues event.
  4. There might be a way to create a simple Chrome extension, and let the user set the permission for that extension to copy to the clipboard. This involves but the end user having to install and extension and change local browser settings. Not sure that many users will be capable/willing to do so.

I've already looked into Stackoverflow questions such as this, but they do not address an asynchronous scenario. Please let me know if you can find any other workable solution (or a tweak on the existing one).

like image 420
Gabriel Kohen Avatar asked Jun 08 '15 14:06

Gabriel Kohen


1 Answers

This is working timeout approach based on your snippet:

HTML:

<div id="container">
Enter Text To Copy</br>
<textarea id="clipboard"></textarea>
</div>
<input type="button" value="Copy" id="copy"/>

JS:

var timeout = 600; // timeout based on ajax response time
var loaded = false;

function loadContent() {
  loaded = false;
  $.getJSON('http://codepen.io/gkohen/pen/QbvoQW.js',function(result){
    document.getElementById("clipboard").value = result.lorem;
    loaded = true;
  });
}

// Copy text as text
function copy() {
  clipboard = document.getElementById("clipboard");
  if (!loaded || clipboard.value.length == 0) {
    alert("Ajax timeout! TIP: Try to increase timeout value.");
    return;
  }

  clipboard.focus();
  clipboard.select();

  if (document.execCommand('Copy'))
    alert("Successfuly coppied to clipboard!");

  // set defaults
  clipboard.value = "";
  loaded = false;
}

document.addEventListener("DOMContentLoaded", function(){
  document.getElementById("copy").onmousedown = loadContent;
  document.getElementById("copy").onclick = function() {
    setTimeout(copy, timeout); // wait for ajax
  }
});

The main issue is execCommand specification. There are some restrictions about security and trusted actions. So you have to make event calling copy and ajax call aparte. This can be done dirty way - by fixed timeout (code above) or proper way - by breakable sleep. New sleep feature is mentioned here and maybe can be modified to breakable variant via clearTimeout, but I did not try.

like image 53
bigless Avatar answered Oct 15 '22 07:10

bigless