Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Working "Copy to Clipboard" function doesn't work when called in bootstrap modal

Objective: to copy text in a bootstrap modal to the clipboard.

JS:

$(document).ready(function(){
  $(document).on('click','#copy-btn', function(){

        // var value = $('#error-message').html();

        // using a static value, just to eliminate any question
        // about what should be copied.
        copytext('kilroy tested this');  

    })
});

function copytext(text) {
    var textField = document.createElement('textarea');
    textField.innerText = text;
    document.body.appendChild(textField);
    textField.select();
    document.execCommand('copy');
    textField.remove();
    console.log('should have copied ' + text); // but it refuses to do so when a modal is used!
}

This Works:

When I try this without a bootstrap modal popup, kilroy tested this is copied to my clipboard:

<button type="button" class="btn btn-success" id="copy-btn">Copy</button>

This Doesn't:

But... when I move the <button> into the modal, nothing gets copied to the clipboard, even though the console reports "should have copied kilroy tested this".

<!-- AJAX Error Popup -->
<div class="modal fade" id="ajax-error" tabindex="-1" role="dialog" aria-labelledby="errorModal" aria-hidden="true">
  <div class="modal-dialog">
    <div class="modal-content">
      <div class="modal-header modal-header-danger">
        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
        <h4 class="modal-title" id="errorModal">Error Detected!</h4>
      </div>

      <div class="modal-body" id="error-message"><!-- AJAX message inserted here --></div>

      <div class="modal-footer">

        <button type="button" class="btn btn-success" id="copy-btn">Copy</button>

        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
      </div>
    </div>
  </div>
</div>

I'm at a loss for any other way to troubleshoot this--

  • I've demonstrated that the copytext() function works,
  • I've eliminated any question about the source of what should be copied, and
  • I've demonstrated that copy-btn is being called when the button is clicked (copytext() is being called and logging to the console).

The only thing left is some wonkiness about the bootstrap modal.

Using jquery 2.1.1 and bootstrap 3.3.6

Open to any ideas or workarounds :)

like image 463
Tim Morton Avatar asked Jan 05 '18 22:01

Tim Morton


2 Answers

document.execCommand('copy'); functionality depends on trusted events. If a event needs to be trusted then the target element should also have a proper focus.

Try setting the focus on the textElement and set it to the modal after you remove it from the text element. this should solve the problem

function copytext(text) {
    var textField = document.createElement('textarea');
    textField.innerText = text;
    document.body.appendChild(textField);
    textField.select();
    textField.focus(); //SET FOCUS on the TEXTFIELD
    document.execCommand('copy');
    textField.remove();
    console.log('should have copied ' + text); 
    ajax-error.focus(); //SET FOCUS BACK to MODAL
}
like image 110
karthick Avatar answered Nov 13 '22 13:11

karthick


In short: as the modal has tabindex="-1" the .focus() will only work in Chrome. For a cross-browser solution, you have to insert the textarea into the DOM as a descendant of the modal.

The key is, that the textarea has to be the document.activeElement when the copy command is executed. In other words, it has to have focus. This could be achieved by calling .focus() on it, however in your specific case the click event happens within a modal with a tabindex="-1" already in focus. At the time of writing in this scenario the .focus() method will work in Chrome only. In other browsers tabindex="-1" will prevent the textarea from getting focused, unless it is a descendant node of the modal.

Therefore the solution below creates the textarea when it always can have focus, as a sibling of the clicked element:

$(document).ready(function(){
    $(document).on('click', '#copy-btn', function(){
        copytext('dferenc tested this', this);  
    })
});

function copytext(text, context) {
    var textField = document.createElement('textarea');
    textField.innerText = text;

    if (context) {
        context.parentNode.insertBefore(textField, context);
    } else {
        document.body.appendChild(textField);
    }

    textField.select();
    document.execCommand('copy');
    // Let `.remove()` also work with older IEs
    textField.parentNode.removeChild(textField);
    console.log('should have copied ' + text);
}
like image 24
dferenc Avatar answered Nov 13 '22 14:11

dferenc