61

I'm trying to copy to clipboard without using Flash, I plan to fall back onto Flash with the use of ZeroClipboard if the browser is incompatible with the javascript approach.

I have an onClick listener for the button which looks like:

$(buttonWhereActionWillBeTriggered).click(function(){ 
    var copyDiv = document.getElementById(inputContainingTextToBeCopied);
    copyDiv.focus();
    document.execCommand('SelectAll');
    document.execCommand("Copy", false, null);
}

and an input field as follows:

<input type="text" name="Element To Be Copied" id="inputContainingTextToBeCopied" value="foo"/>

This currently works as expected but the design requires that the field containing the text to be copied is invisible. I have tried both setting type="hidden" and style="display: none" neither of which have succeeded. Both result in the button selecting the whole page and copying the whole content to the user's clipboard.
I'm relatively confident the cause is not browser-based but just in case, I am testing on Chrome (Version 43.0.2357.134 (64-bit)) on Mac OS X 10.10.4.

Is there a way that I can maintain the functionality of when the < input> is visible whilst hiding it? or if not an alternate route I can take?


I'm aware of similar questions, none of which address my problem, either from being too old, not actually using Javascript, or not fitting the particular scenario. Here's a good answer for anyone having similar, less specific issues.

Andhi Irawan
  • 456
  • 8
  • 15
Aaron Critchley
  • 2,335
  • 2
  • 19
  • 32
  • 9
    Did you try opacity: 0 ? – DavidDomain Jul 23 '15 at 16:48
  • @DavidDomain I've just tried it and it works, which is awesome, thank you! But unfortunately it still leaves the seethrough div and moves the design, if a better solution doesn't come up I'll go with this and set the input field to 1px x 1px. If you'd like to answer I will happily accept if nothing better comes up. Thanks again! – Aaron Critchley Jul 23 '15 at 16:55
  • 2
    What if you use jQuery to show() the input, then call copy, then immediately hide() it again? – pokkanome Jul 23 '15 at 16:58
  • 1
    @pokkanome I took a very similar route to this, I'll post an answer soon, thank you! – Aaron Critchley Jul 23 '15 at 17:10
  • I use style="position: absolute; top: -999px; left: -999px" – Adrian Hum Aug 12 '21 at 01:05

14 Answers14

105

Here's my solution that doesn't use jQuery:

function setClipboard(value) {
    var tempInput = document.createElement("input");
    tempInput.style = "position: absolute; left: -1000px; top: -1000px";
    tempInput.value = value;
    document.body.appendChild(tempInput);
    tempInput.select();
    document.execCommand("copy");
    document.body.removeChild(tempInput);
}
<!DOCTYPE html>
<html>
<head>
<title>Set Clipboard</title>
</head>
<body>
    <button onclick="setClipboard('foo loves bar')">Set Clipboard</button>
</body>
</html>
Dan Stevens
  • 6,392
  • 10
  • 49
  • 68
  • 1
    it worked for me Dan, thank you so much. I was searching so many codes for more than 2 hours, no one worked for me. but your code is good. small and effective. thanks again. – Brij Jun 01 '17 at 09:05
  • Hello, I just ran into this and have been looking for a solution like this for hours. If the code is good without jQuery, is there any external link/calling that is needed in the head for this script? because right now I am using a google jQuery file at https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js Will I need to reference any other for this to work? Thank you! – Rookie Recruits Sep 15 '17 at 05:42
  • My snippet uses standard Javascript and should work as-is in a modern web browser. I'd check the browser compatibility for the `execComand` function for your target browser (most do support it): https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand#Browser_compatibility – Dan Stevens Sep 29 '17 at 08:44
  • Great Solution, worked for me too, this one needs to be on top. – samir Sep 18 '18 at 06:35
  • 3
    one enhancement if you need to preserve newlines is to create a "textarea" instead of an "input", and in my case the value comes from a hidden
     (so newlines are preserved)
    – jpw Mar 22 '19 at 01:55
  • short and useful (THE BEST) – mohammad asghari Feb 21 '20 at 10:33
  • 1
    Oh my god, thank you so much. This integrated perfectly into my code and is very compact! – thefailagent Nov 01 '20 at 04:06
  • Works great on desktop but doesn't work on mobile unfortunately. – Tarquin Mar 27 '21 at 05:18
  • `tempInput.style = "position: absolute; left: -1000px; top: -1000px"` is incorrect, as `style` is read only. Use `tempInput.style.position = 'absolute'`, etc. – ffxsam Jun 29 '21 at 16:21
  • My man, you are the lord. Thank you Jesus! – VPetrovic Jan 31 '23 at 15:45
58

2019 - was still looking for an answer without offscreen stuff.

What I did is first change the input text field to type="text", copy the text and then change it back to type="hidden". That works well.

<input id="dummy" name="dummy" type="hidden">

<script>
var copyText = document.getElementById("dummy");
copyText.type = 'text';
copyText.select();
document.execCommand("copy");
copyText.type = 'hidden';
</script>
Rick
  • 806
  • 7
  • 15
41

--Update--

Document.execCommand()

[1] Before Firefox 41, clipboard capability needed to be enabled in the user.js preference file. See A brief guide to Mozilla preferences for more information. If the command wasn't supported or enabled, execCommand was raising an exception instead of returning false. In Firefox 41 and later, clipboard capability is enabled by default in any event handler that is able to pop-up a window (semi-trusted scripts).

Since Firefox version 41 Document.execCommand() now works. So no need to use a fallback anymore.


Since browsers seem to behave differently when it comes to clipboard access, it took me a while to get my head around it.

It's pretty similar to your solution, but the difference is to create a temporary element and fill it with the input value. That way we can keep the input's display property set to none.

There is also a workaround for IE which uses window.clipboardData.

Firefox would not let me access the clipboard at all. So I had to add a prompt to let users manually copy the input value. Sure a prompt is ugly, but you could just use a modal like a window, which would do the same.

Since this seems to be a knotty thing, I am on Win7 (64 bit) and tested in

Chrome - Version 43.0.2357.134 m

IE - Version 11.0.9600.17914

and Firefox is irrelevant because it would not let me access it anyway.

var copyBtn   = $("#copy-btn"),
    input     = $("#copy-me");

function copyToClipboardFF(text) {
  window.prompt ("Copy to clipboard: Ctrl C, Enter", text);
}

function copyToClipboard() {
  var success   = true,
      range     = document.createRange(),
      selection;

  // For IE.
  if (window.clipboardData) {
    window.clipboardData.setData("Text", input.val());        
  } else {
    // Create a temporary element off screen.
    var tmpElem = $('<div>');
    tmpElem.css({
      position: "absolute",
      left:     "-1000px",
      top:      "-1000px",
    });
    // Add the input value to the temp element.
    tmpElem.text(input.val());
    $("body").append(tmpElem);
    // Select temp element.
    range.selectNodeContents(tmpElem.get(0));
    selection = window.getSelection ();
    selection.removeAllRanges ();
    selection.addRange (range);
    // Lets copy.
    try { 
      success = document.execCommand ("copy", false, null);
    }
    catch (e) {
      copyToClipboardFF(input.val());
    }
    if (success) {
      alert ("The text is on the clipboard, try to paste it!");
      // remove temp element.
      tmpElem.remove();
    }
  }
}

copyBtn.on('click', copyToClipboard);
#copy-me {
    display:none
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" name="Element To Be Copied" id="copy-me" value="foo loves bar"/>
<button id="copy-btn">Copy</button><br/><br/>
<textarea placeholder="paste here"></textarea>
Andhi Irawan
  • 456
  • 8
  • 15
DavidDomain
  • 14,976
  • 4
  • 42
  • 50
  • 2
    This is enormous, just move the element offscreen and copy it using a execCommand function... Check my answer at the bottom. – Mister SirCode Jul 01 '19 at 07:47
20

Thanks to the help of @DavidDomain, I found a somewhat hacky, but functional approach.

Firstly, I moved the input way off screen and modified some properties, resulting in this:

<input type="text" name="Element To Be Copied" id="inputContainingTextToBeCopied" value="foo" style="display:none; position: relative; left: -10000px;"/>

display:none was added after the following modifications to the js

After that, the comment from @Pokkanome let me into modifying the onClick function like so:

$(buttonWhereActionWillBeTriggered).click(function(){ 
    var copyDiv = document.getElementById(inputContainingTextToBeCopied);
    copyDiv.style.display = 'block';
    copyDiv.focus();
    document.execCommand('SelectAll');
    document.execCommand("Copy", false, null);
    copyDiv.style.display = 'none';
}

I'm unsure if it's possible to copy from a hidden div using this method, which would make sense in terms of browser security as giving unquestioned access to the clipboard is going to be somewhat risky. The approach taken had the same intended result though.

Aaron Critchley
  • 2,335
  • 2
  • 19
  • 32
9

I have a less outdated Solution here:

Using this script, you can copy your data. It's much smaller than the past scripts provided.

What the script does is use an Input that is hidden off the side of the Screen either with CSS or Inline-Styles, and then selects it quickly and runs the copy command.

function copyFunc() {
  var copyText = document.getElementById("copyInp");
  copyText.select();
  document.execCommand("copy"); //this function copies the text of the input with ID "copyInp"
}
<input type="text" value="StuffYaWantCopied" id="copyInp" style="position:absolute;left:-1000px;top:-1000px;">
  <a onclick="copyFunc()" style="cursor:cell;">
     Click here to Copy!
  </a>

For Bonus, I made a small Clipboard API that can select Elements Dynamically and retrieve Text from them using Contenteditable Divs and Dynamic Variables: https://codepen.io/SkylerSpark/pen/OJJqxWX

Also, go see Ciprians Answer below about using the new Permissions API to send text directly to the clipboard with permissions allowed by the user, it's not fully supported, but it's available in the latest browsers (I know it actually works in chrome right now, but I haven't tested any other browsers): https://stackoverflow.com/a/58099014/11165703

Andhi Irawan
  • 456
  • 8
  • 15
Mister SirCode
  • 1,575
  • 14
  • 31
  • same with my idea, just hide the input element on screen using css :) – aldrien.h Jul 16 '19 at 00:51
  • 1
    @aldrien.h you do know that if you hide an element with display: hidden, or anything similar, it will not be possible to select it? I hope you do. So really unless you want to add more CSS by making every part of the element "hidden" with transparent colors, then your idea wont work. – Mister SirCode Jul 17 '19 at 22:56
  • i mean, hide on screen using css, like styles above, absolute position with negative values to top & left, not display:none, `execCommand` will not work on hidden element. :) – aldrien.h Jul 18 '19 at 02:32
  • 1
    literally a copy of @Dan Stevens answer just put the styling into the element and not in the script. – VeenarM Nov 26 '19 at 00:22
  • 1
    @VeenarM Heres the thing you need to learn, In stackoverflow, if information is more viable, it doesnt matter if code is "Similar"... There are billions of copies of the code I added here, but to be honest, Im not a rep-sniffing copier, I wrote it on my own :P Also my script is much shorter than dans, not to be Competitive, but its the truth. – Mister SirCode Nov 26 '19 at 12:35
  • Also I added a basic Clipboard API as a bonus, Happy Mr. VeenarM? :D Just try to be a little bit more respectful to people here. Stackoverflow isnt about Anger and Disrespect, its about learning :) – Mister SirCode Nov 26 '19 at 12:40
7

An alternative workaround that works in all browser is, instead of hiding the element, you can set its opacity as 0 with absolute position.

#copy-me {
    position: absolute;
    opacity: 0;
}
Dinesh M
  • 1,026
  • 8
  • 23
  • 1
    Thanks. This is more useful for me, Solution with `absolute` position and `-1000px` for `top` and `bottom` made scroll for me. – SirSaleh Dec 24 '19 at 10:01
7

You can simply use opacity:0.00000000000001 to hide the input tag, then use javascript to copy the hidden text to clipboard

function myFunction() {
  var copyText = document.getElementById("myInput");
  copyText.select();
  copyText.setSelectionRange(0, 99999)
  document.execCommand("copy");
  alert("Text copied successfully");
}
<input type="text" value="===your text here===" id="myInput" style="opacity:0.00000000000001">
<button onclick="myFunction()">Copy</button>
Mohamed Haseem
  • 362
  • 3
  • 7
6

You can use window.navigator for this:

navigator.clipboard.writeText('this will be copied to the clipboard');
Tim
  • 231
  • 1
  • 3
  • 6
2

What works to me was:

<div>
  <a class="copyBtn">Copy</a>
  <input class="d-none" value="teste">
</div>

and:

$('.copyBtn').on('click', function(e) {
  e.preventDefault();
  var input = $(this).parent().find(".dirVal");
  $(input).removeClass("d-none");
  input.select();

  document.execCommand('copy');
  $(input).addClass("d-none");
  callNotify("Parabéns!", "Caminho copiado para área de transferência!", "success");
});
1

What about using this: https://developer.mozilla.org/en-US/docs/Web/API/Clipboard/writeText

navigator.clipboard.writeText("<empty clipboard>").then(function() {
  /* clipboard successfully set */
}, function() {
  /* clipboard write failed */
});
Ciprian
  • 3,066
  • 9
  • 62
  • 98
  • This depends upon the cutting-edge [Permissions API](https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API), so may not be widely supported at this time. Glad to see browser developers are looking for proper ways to support Clipboard so we're not relying on the 'hacks' in this topic. – Dan Stevens Oct 04 '19 at 10:29
1

just do it!

.blind {
    overflow: hidden;
    position: absolute;
    clip: rect(0 0 0 0);
    width: 1px;
    height: 1px;
    margin: -1px;
}
<textarea id="copy" class="blind">
your copy text here!
</textarea>
copyClipboard(document.getElementById('copy'));

function copyClipboard(el) {
  el.select();
  window.document.execCommand('copy');
}

https://gist.github.com/seunggabi/7ae53c100d647cb19c48047cff9b7019

seunggabi
  • 1,699
  • 12
  • 12
1

Two approaches that worked for me:

Approach 1: In this approach, the input element is initially hidden, and the copy function unhides it, selects and copies the text then hides it again.

<script>
  function copyLinkToClipboardViaHiddenField() {
    el = document.querySelector("#input");
    el.style.display = "block";
    var copyText = document.querySelector("#input");
    copyText.select();
    document.execCommand("copy");
    el.style.display = "none";
    document.getElementById("copylink").innerText = "copied OK!";
    document.getElementById("copylink").href = "";
    document.getElementById("copylink").style.color = "black";
    document.getElementById("copylink").style.textDecoration = "none";
    return false; // prevents click doing anything
  }

</script>
<input id="input"
       type="text"
       style="display: none;"
       value="https://www.example.org/ViaHiddenField"/>
<a href="javascript:void(0)"
   onclick="return copyLinkToClipboardViaHiddenField()"
   id="copylink"
   style="text-decoration: underline;color: blue;">copy link ViaHiddenField</a>

Approach 2: In this approach, a new input element is created and appended to the bottom of the document body, its value is copied, then the element is removed.

<script>
  function copyLinkToClipboardViaAppendElement() {
    el = document.createElement("input");
    el.setAttribute('type', 'text');
    el.setAttribute('value', "https://www.example.org/ViaAppendElement");
    document.body.appendChild(el)
    el.select();
    console.log(el)
    document.execCommand("copy");
    el.parentNode.removeChild(el);
    document.getElementById("copylink").innerText = "copied OK!";
    document.getElementById("copylink").href = "";
    document.getElementById("copylink").style.color = "black";
    document.getElementById("copylink").style.textDecoration = "none";
    return false; // prevents click doing anything;
  }

</script>
<a href="javascript:void(0)"
   onclick="return copyLinkToClipboardViaAppendElement()"
   id="copylink"
   style="text-decoration: underline;color: blue;">copy link ViaAppendElement</a>
Duke Dougal
  • 24,359
  • 31
  • 91
  • 123
1

To copy the text to clibboard it has to be visible. So I created an input type=text but set style display:none and used jquery to perform show() and hide() around copying.

<input type=text id='me' value='bla' style='display:none;'>
<button onclick='
$("#me").show(); 
var copyText = document.getElementById("me"); 
copyText.select();
copyText.setSelectionRange(0, 99999);   
document.execCommand("copy"); 
$("#me").hide(); 
alert("Link copied to clipboard");
'></button> 
Desiree
  • 11
  • 1
-1

Here's an easy, though hacky answer to this that seems to work for me. Rather than using display: none; use this:

height: 0px;
width: 0px;
overflow: hidden;
position: absolute;

This allows for selection while still hiding the textarea and not affecting the design.

Luke
  • 648
  • 7
  • 15
  • Im not going to downvote, because this works. But i will say, when your not either completely hiding the element, or atleast moving it off screen, it can cause selection errors... this is finicky, and although it would work in most cases, now you have a bunch of little 0pixel elements all over your screen you cant see. I know this doesnt make sense, but in a rare case, this could be a bad idea, otherwise in normal use it should be fine. – Mister SirCode Jul 19 '19 at 13:53