That's it :) I have a div with the id #toCopy, and a button with the id #copy.
What's the best way to copy #toCopy content to clipboard when pressing #copy?
You can copy to clipboard almost in any browser from input elements only (elements that has .value property), but you can't from elements like <div>, <p>, <span>... (elements that has .innerHTML property).
But I use this trick to do so:
Create a temporary input element, say <textarea>
Copy innerHTML from <div> to the newly created <textarea>
Copy .value of <textarea> to clipboard
Remove the temporary <textarea> element we just created
function CopyToClipboard (containerid) {
// Create a new textarea element and give it id='temp_element'
const textarea = document.createElement('textarea')
textarea.id = 'temp_element'
// Optional step to make less noise on the page, if any!
textarea.style.height = 0
// Now append it to your page somewhere, I chose <body>
document.body.appendChild(textarea)
// Give our textarea a value of whatever inside the div of id=containerid
textarea.value = document.getElementById(containerid).innerText
// Now copy whatever inside the textarea to clipboard
const selector = document.querySelector('#temp_element')
selector.select()
document.execCommand('copy')
// Remove the textarea
document.body.removeChild(textarea)
}
<div id="to-copy">
This text will be copied to your clipboard when you click the button!
</div>
<button onClick="CopyToClipboard('to-copy')">Copy</button>
The same without id:
function copyClipboard(el, win){
var textarea,
parent;
if(!win || (win !== win.self) || (win !== win.window))
win = window;
textarea = document.createElement('textarea');
textarea.style.height = 0;
if(el.parentElement)
parent = el.parentElement;
else
parent = win.document;
parent.appendChild(textarea);
textarea.value = el.innerText;
textarea.select();
win.document.execCommand('copy');
parent.removeChild(textarea);
}
I didn't tested for different windows (iframes) though!
UPDATED ANSWER
Javascript was restricted from using the clipboard, early on.
but nowadays it supports copy/paste commands.
See documentation of mozilla and caniuse.com.
document.execCommand('paste')
make sure that you support browsers that don't.
https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand
http://caniuse.com/#search=command
Javascript is not allowed to use the clipboard, but other plugins like flash do have access.
How do I copy to the clipboard in JavaScript?
Related
I'm trying to copy a parameter string into my clipboard using native JS. This works fine so far, however I have a small cosmetic problem, when running my snippet in IE 7.
My code:
function copyStringToClipboard (str) {
// Create new element
var el = document.createElement('input');
el.setAttribute("display", "none");
el.setAttribute("type", "text");
el.value = str;
el.setAttribute('readonly', '');
document.body.appendChild(el);
el.select();
// Copy text to clipboard
document.execCommand('copy');
// Remove temporary element
document.body.removeChild(el);
}
As I mentioned above, this does work in the tested browsers. However, it creates a visible text-input field (line 3). I tried using el.style = {position: 'absolute', left: '-9999px'};, but Internet Explorer yields:
Not implemented
I thought about creating an input type="hidden", but it seems like this hidden field is not selectable - which makes sense. Needlessly to say, that this action triggers onClick(), so indeed with an user-action.
Ideas on how to solve this?
Instead of using el.setAttribute("display", "none");, You should change that line to:
el.style.display = "none";
Why this works?
Setting the attribute display none does not affect the style. It should be added as inline style or in the css to hide the input box.
I've been trying to copy the innerContent of a <span> to my clipboard without success:
HTML
<span id="pwd_spn" class="password-span"></span>
JavaScript
Function Call
document.addEventListener('DOMContentLoaded', function () {
document.getElementById('copy').addEventListener('click', copy_password);
});
Function
function copy_password() {
var copyText = document.getElementById("pwd_spn").select();
document.execCommand("Copy");
}
I've also tried:
function copy_password() {
var copyText = document.getElementById("pwd_spn").textContent;
copyText.select();
document.execCommand("Copy");
}
It seems like .select() doesn't work on a <span> element since I get the following error on both:
You could do this: create a temporary text area and append it to the page, then add the content of the span element to the text area, copy the value from the text area and remove the text area.
Because of some security restrictions you can only execute the Copy command if the user interacted with the page, so you have to add a button and copy the text after the user clicks on the button.
document.getElementById("cp_btn").addEventListener("click", copy_password);
function copy_password() {
var copyText = document.getElementById("pwd_spn");
var textArea = document.createElement("textarea");
textArea.value = copyText.textContent;
document.body.appendChild(textArea);
textArea.select();
document.execCommand("Copy");
textArea.remove();
}
<span id="pwd_spn" class="password-span">Test</span>
<button id="cp_btn">Copy</button>
See https://stackoverflow.com/a/48020189/2240670 there is a snippet of code for that gives you an example for a div, that also applies to a span, I did not copy it here to avoid duplication.
Basically, when you are copying to clipboard you need to create a selection of text, <textarea> and <input> elements make this easy because they have a select() method, but if you are trying to copy contents from any other type of element like a <div> or <span>, you'll need to:
Create/get a Range object(some browsers do not provide a constructor, or a decent way to do this). Calling document.getSelection().getRangeAt(0), I found works on most browsers except edge(ie11 works though).
Add the element you want to copy from to that range's selection.
Add that range to the window or document Selection.
Call document.execCommand("copy") to copy the selected text.
I also recommend checking the API of Selection and Range, that will give you a better grasp of this.
simple method
1)create a input
2)give style z-index -1 and it will be hide
var code = $("#copy-to-clipboard-input");
var btnCopy = $("#btn-copy");
btnCopy.on("click", function () {
code.select();
document.execCommand("copy");
});
<input type="input" style="width:10px; position:absolute; z-index: -100 !important;" value="hello" id="copy-to-clipboard-input">
<button class="btn btn-success" id="btn-copy">Copy</button>
I looked at this question where it is asked for a way to simply copy text as plain text. I want to do exactly that but with one additional thing - not lose focus on the current element.
I need this for a Chrome extension, so I'm not bothered with cross-browser support. When the user types in an input (or contenteditable), a dropdown with choices appears. If he chooses one of them, it is copied to his clipboard. I don't want the element to lose focus because some sites might have implemented logic to run on the element's blur event.
Here's what I've tried:
Solution 1
Create an <input> element and use its select() method:
function clipWithInput(text) {
var input = document.createElement("input");
document.body.appendChild(input);
input.addEventListener("focus", function (e) {
e.preventDefault();
e.stopPropagation();
});
input.value = text;
input.select();
document.execCommand("copy");
document.body.removeChild(input);
}
document.getElementById("choice").onmousedown = function (e) {
e.preventDefault(); // prevents loss of focus when clicked
clipWithInput("Hello");
};
#main {background: #eee;}
#choice {background: #fac;}
<div id="main" contenteditable="true">Focus this, click the div below and then paste here.</div>
<div id="choice">Click to add "Hello" to clipboard</div>
As you can see, this works. The text is copied. However, when you focus the contenteditable and click on the "choice", the focus is lost. The choice element has preventDefault() on its mousedown event which causes it to not break focus. The dummy <input> element is the problem here, even though it has preventDefault() on its focus event. I guess the problem here is that it's too late - the initial element has already fired its blur, so my dummy input's focus is irrelevant.
Solution 2
Use a dummy text node and the Selection API:
function clipWithSelection(text) {
var node = document.createTextNode(text),
selection = window.getSelection(),
range = document.createRange(),
clone = null;
if (selection.rangeCount > 0) {
clone = selection.getRangeAt(selection.rangeCount - 1).cloneRange();
}
document.body.appendChild(node);
selection.removeAllRanges();
range.selectNodeContents(node);
selection.addRange(range);
document.execCommand("copy");
selection.removeAllRanges();
document.body.removeChild(node);
if (clone !== null) {
selection.addRange(clone);
}
}
document.getElementById("choice").onmousedown = function (e) {
e.preventDefault(); // prevents loss of focus when clicked
clipWithSelection("Hello");
};
#main {background: #eee;}
#choice {background: #fac;}
<div id="main" contenteditable="true">Focus this, click the div below and then paste here.</div>
<div id="choice">Click to add "Hello" to clipboard</div>
This works perfectly at first glance. The text is copied, no focus is lost, the caret stays at the same position. No drama. However, when you paste the text in a contenteditable (like Gmail's email composer), this is the result:
<span style="color: rgb(0, 0, 0); font-family: "Times New Roman"; font-size: medium;">Hello</span>
Not plain text.
I tried appending the element in the <head> where there are no styles - nope. Text isn't selected and nothing is copied.
I tried appending the text node in a <span> and set stuff like style.fontFamily to inherit, as well as fontSize and color. Still doesn't work. I logged the dummy element and it correctly had my inherit styles. However, the pasted text didn't.
Recap
I want to programmatically copy plain text with no styles while preserving focus on the currently active element.
Your solution (especially 2) was okay. When you paste in a contenteditable, it needs to be expected that there are span codes inserted, many use that in insertHTML. You are not to expect plain text programmatically. Some would suggest not using a contenteditable at all (though I understand you're talking about some extension). But your solution is more compatible with mobiles than MDN or such.
So, you programmatically copy plain with no style added (if no contenteditable) while preserving focus on the current element.
Say, in my Chrome extension I need to copy a user formatted HTML text on to Clipboard. The text (in its HTML form) may be something like this:
This is a <b>test</b><div><br></div>
<div>And this is too</div><div><br></div><div>Thank you</div>
If I use this JavaScript example, all I get copied is HTML markup from above. But I'm wondering, can I copy it as formatted text, or this?
This is a test And this is
tooThank you
Once crbug.com/395376 is fixed, you can declare the clipboardWrite permission in your manifest file and simply use the folllowing code from yuor content script:
var element = document.body; // Example, select the "whole" document
// Change selected area
var r = document.createRange();
r.selectNode(element);
var s = window.getSelection();
s.removeAllRanges();
s.addRange(r);
// Copy - requires clipboardWrite permission + crbug.com/395376 must be fixed
document.execCommand('copy');
Until the previous bug is fixed, you have to pass the data to the background page and copy the message from there. This solution is not optimal because you're going to insert untrusted HTML in your background page. See https://stackoverflow.com/a/25275151 for examples on how using innerHTML for copying can be abused.
If you understand the risks associated with using innerHTML, and you're accepting its consequences, then you could use the following code to copy rich text:
// content script
var element = document.body; // Example
chrome.runtime.sendMessage({
html: 'copyhtml',
text: element.outerHTML
});
background page:
chrome.runtime.onMessage.addListener(function(message) {
if (message && message.type == 'copyhtml') {
var wrapper = document.createElement('div');
// WARNING: Potentially insecure!
wrapper.innerHTML = message.html;
document.body.appendChild(wrapper);
var range = document.createRange();
r.selectNode(wrapper);
var s = window.getSelection();
s.removeAllRanges();
s.addRange(r);
// Copy - requires clipboardWrite permission
document.execCommand('copy');
wrapper.remove();
}
});
(if the reader wants to copy text instead of rich text, see Clipboard Copy / Paste on Content script (Chrome Extension))
I'm trying to make a page that has some editabable fields, but I only want them to display as input boxes once the user clicks on them (the rest of the time showing as plain text). Is there a simple way to do this in Javascript?
Introduction
Fairly simple, yes. I can think of two basic approaches:
Using the contenteditable attribute
Using an input you add on-the-fly
Handy references for both of the below:
DOM2 Core
DOM2 HTML
DOM3 Core
HTML5 spec - "user interaction" section
Using the contenteditable attribute
The contentEditable attribute (W3C, MDC, MSDN) can be "true" indicating that the element can be edited directly. This has the advantage of not requiring any JavaScript at all (live example):
<p id="container">The <span contenteditable="true">colored items</span> in this paragraph
are <span contenteditable="true">editable</span>.</p>
Lest you think this is some l33t new thing, IE has supported it since IE 5.5 and other major browsers for very nearly that long. (In fact, this was one of many Microsoft innovations from the IE5.5 / IE6 timeframe; they also gave us innerHTML and Ajax.)
If you want to grab the (edited) content, you just grab innerHTML from the elements you've made editable. Here's an example of some JavaScript that will flag up when contenteditable spans blur (live copy):
var spans = document.getElementsByTagName("span"),
index,
span;
for (index = 0; index < spans.length; ++index) {
span = spans[index];
if (span.contentEditable) {
span.onblur = function() {
var text = this.innerHTML;
text = text.replace(/&/g, "&").replace(/</g, "<");
console.log("Content committed, span " +
(this.id || "anonymous") +
": '" +
text + "'");
};
}
}
#container span {
background-color: #ff6;
}
<p id="container">The <span id="span1" contenteditable="true">colored items</span> in this paragraph
are <span contenteditable="true">editable</span>.</p>
Using an input you add on-the-fly
You need to get a reference to the element that you're using for display (a span, perhaps) and then hook its click event (or hook the click event on a parent of the desired element(s)). In the click event, hide the span and insert a input[type=text] alongside it.
Here's a very simple example of using an input:
window.onload = function() {
document.getElementById('container').onclick = function(event) {
var span, input, text;
// Get the event (handle MS difference)
event = event || window.event;
// Get the root element of the event (handle MS difference)
span = event.target || event.srcElement;
// If it's a span...
if (span && span.tagName.toUpperCase() === "SPAN") {
// Hide it
span.style.display = "none";
// Get its text
text = span.innerHTML;
// Create an input
input = document.createElement("input");
input.type = "text";
input.value = text;
input.size = Math.max(text.length / 4 * 3, 4);
span.parentNode.insertBefore(input, span);
// Focus it, hook blur to undo
input.focus();
input.onblur = function() {
// Remove the input
span.parentNode.removeChild(input);
// Update the span
span.innerHTML = input.value == "" ? " " : input.value;
// Show the span again
span.style.display = "";
};
}
};
};
#container span {
background-color: #ff6;
}
<p id="container">The <span>colored items</span> in this paragraph
are <span>editable</span>.</p>
There I'm hooking the click on the parent p element, not the individual spans, because I wanted to have more than one and it's easier to do that. (It's called "event delegation.") You can find the various functions used above in the references I gave at the beginning of the answer.
In this case I used blur to take the edit down again, but you may wish to have an OK button and/or other triggers (like the Enter key).
Off-topic: You may have noticed in the JavaScript code above that I had to handle a couple of "MS differences" (e.g., things that IE does differently from other browsers), and I've used the old "DOM0" style of event handler where you just assign a function to a property, which isn't ideal, but it avoids my having to handle yet another difference where some versions of IE don't have the DOM2 addEventListener and so you have to fall back to attachEvent.
My point here is: You can smooth over browser differences and get a lot of utility functions as well by using a decent JavaScript library like jQuery, Prototype, YUI, Closure, or any of several others. You didn't say you were using any libraries, so I didn't in the above, but there are compelling reasons to use them so you don't have to worry about all the little browser niggles and can just get on with addressing your actual business need.
A trivial example using plain JavaScript would be along the lines of: http://jsfiddle.net/vzxW4/.
document.getElementById('test').onclick = function() {
document.body.removeChild(this);
var input = document.createElement('input');
input.id = 'test';
input.value = this.innerHTML;
document.body.appendChild(input);
input.select();
}
Using a library would save you time and headaches, though. For example, using jQuery: http://jsfiddle.net/vzxW4/1/.
$("#test").click(function() {
var input = $("<input>", { val: $(this).text(),
type: "text" });
$(this).replaceWith(input);
input.select();
});
Can we do it simple guys?
Just keep textbox with readonly property true and some CSS which makes text box looks like span with border.
Then as soon as user clicks on text box remove readonly attribute.
On blur restore the CSS and readonly attributes.