First of all, let me note that this works perfectly in Chrome.
I am trying to override the copy event in JavaScript and replace the clipboard contents with my own data. I extracted the essentials of the problem into this fiddle: https://jsfiddle.net/gxewmc2h/4/ (yes, I need to use global variables to set the data)
window.globalCopyObject = {};
window.globalCopyObject.clipboardDataText = "text value";
window.globalCopyObject.clipboardHtmlText = "html value";
document.addEventListener("copy", function (event) {
event.clipboardData.setData("text/plain", window.globalCopyObject.clipboardDataText);
event.clipboardData.setData("text/html", window.globalCopyObject.clipboardHtmlText);
event.preventDefault();
});
When you use Edge and try to copy the text on the page and paste it into the input, it does override the event since the clipboard is emptied, but it does not fill it with new data.
As far as I know, the latest Edge should support the clipboard API, is there something obvious I am missing?
Thank you in advance for any ideas.
Related
Is there a way in javascript to copy an html string (ie <b>xx<b>) into the clipboard as text/html, so that it can then be pasted into for example a gmail message with the formatting (ie, xx in bold)
There exists solutions to copy to the clipboard as text (text/plain) for example https://stackoverflow.com/a/30810322/460084 but not as text/html
I need a non flash, non jquery solution that will work at least on IE11 FF42 and Chrome.
Ideally I would like to store both text and html versions of the string in the clipboard so that the right one can be pasted depending if the target supports html or not.
Since this answer has gotten some attention, I have completely rewritten the messy original to be easier to grasp. If you want to look at the pre-revisioned version, you can find it here.
The boiled down question:
Can I use JavaScript to copy the formatted output of some HTML code to the users clipboard?
Answer:
Yes, with some limitations, you can.
Solution:
Below is a function that will do exactly that. I tested it with your required browsers, it works in all of them. However, IE 11 will ask for confirmation on that action.
Explanation how this works can be found below, you may interactively test the function out in this jsFiddle.
// This function expects an HTML string and copies it as rich text.
function copyFormatted (html) {
// Create container for the HTML
// [1]
var container = document.createElement('div')
container.innerHTML = html
// Hide element
// [2]
container.style.position = 'fixed'
container.style.pointerEvents = 'none'
container.style.opacity = 0
// Detect all style sheets of the page
var activeSheets = Array.prototype.slice.call(document.styleSheets)
.filter(function (sheet) {
return !sheet.disabled
})
// Mount the container to the DOM to make `contentWindow` available
// [3]
document.body.appendChild(container)
// Copy to clipboard
// [4]
window.getSelection().removeAllRanges()
var range = document.createRange()
range.selectNode(container)
window.getSelection().addRange(range)
// [5.1]
document.execCommand('copy')
// [5.2]
for (var i = 0; i < activeSheets.length; i++) activeSheets[i].disabled = true
// [5.3]
document.execCommand('copy')
// [5.4]
for (var i = 0; i < activeSheets.length; i++) activeSheets[i].disabled = false
// Remove the container
// [6]
document.body.removeChild(container)
}
Explanation:
Look into the comments in the code above to see where you currently are in the following process:
We create a container to put our HTML code into.
We style the container to be hidden and detect the page's active stylesheets. The reason will be explained shortly.
We put the container into the page's DOM.
We remove possibly existing selections and select the contents of our container.
We do the copying itself. This is actually a multi-step process:
Chrome will copy text as it sees it, with applied CSS styles, while other browsers will copy it with the browser's default styles.
Therefore we will disable all user styles before copying to get the most consistent result possible.
Before we do this, we prematurely execute the copy command.
This is a hack for IE11: In this browser, the copying must be manually confirmed once. Until the user clicked the "Confirm" button, IE users would see the page without any styles. To avoid this, we copy first, wait for confirmation, then disable the styles and copy again. That time we won't get a confirmation dialog since IE remembers our last choice.
We actually disable the page's styles.
Now we execute the copy command again.
We re-enable the stylesheets.
We remove the container from the page's DOM.
And we're done.
Caveats:
The formatted content will not be perfectly consistent across browsers.
As explained above, Chrome (i.e. the Blink engine) will use a different strategy than Firefox and IE: Chrome will copy the contents with their CSS styling, but omitting any styles that are not defined.
Firefox and IE on the other hand won't apply page-specific CSS, they will apply the browser's default styles. This also means they will have some weird styles applied to them, e.g. the default font (which is usually Times New Roman).
For security reasons, browsers will only allow the function to execute as an effect of a user interaction (e.g. a click, keypress etc.)
There is a much simpler solution. Copy a section of your page (element) than copying HTML.
With this simple function you can copy whatever you want (text, images, tables, etc.) on your page or the whole document to the clipboard.
The function receives the element id or the element itself.
function copyElementToClipboard(element) {
window.getSelection().removeAllRanges();
let range = document.createRange();
range.selectNode(typeof element === 'string' ? document.getElementById(element) : element);
window.getSelection().addRange(range);
document.execCommand('copy');
window.getSelection().removeAllRanges();
}
How to use:
copyElementToClipboard(document.body);
copyElementToClipboard('myImageId');
If you want to use the new Clipboard API, use the write method like below:
var type = "text/html";
var blob = new Blob([text], { type });
var data = [new ClipboardItem({ [type]: blob })];
navigator.clipboard.write(data).then(
function () {
/* success */
},
function () {
/* failure */
}
);
Currently(Sep 2021), The problem is that Firefox doesn't support this method.
I have done a few modifications on Loilo's answer above:
setting (and later restoring) the focus to the hidden div prevents FF going into endless recursion when copying from a textarea
setting the range to the inner children of the div prevents chrome inserting an extra <br> in the beginning
removeAllRanges on getSelection() prevents appending to existing selection (possibly not needed)
try/catch around execCommand
hiding the copy div better
On OSX this will not work. Safari does not support execCommand and chrome OSX has a known bug https://bugs.chromium.org/p/chromium/issues/detail?id=552975
code:
clipboardDiv = document.createElement('div');
clipboardDiv.style.fontSize = '12pt'; // Prevent zooming on iOS
// Reset box model
clipboardDiv.style.border = '0';
clipboardDiv.style.padding = '0';
clipboardDiv.style.margin = '0';
// Move element out of screen
clipboardDiv.style.position = 'fixed';
clipboardDiv.style['right'] = '-9999px';
clipboardDiv.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
// more hiding
clipboardDiv.setAttribute('readonly', '');
clipboardDiv.style.opacity = 0;
clipboardDiv.style.pointerEvents = 'none';
clipboardDiv.style.zIndex = -1;
clipboardDiv.setAttribute('tabindex', '0'); // so it can be focused
clipboardDiv.innerHTML = '';
document.body.appendChild(clipboardDiv);
function copyHtmlToClipboard(html) {
clipboardDiv.innerHTML=html;
var focused=document.activeElement;
clipboardDiv.focus();
window.getSelection().removeAllRanges();
var range = document.createRange();
range.setStartBefore(clipboardDiv.firstChild);
range.setEndAfter(clipboardDiv.lastChild);
window.getSelection().addRange(range);
var ok=false;
try {
if (document.execCommand('copy')) ok=true; else utils.log('execCommand returned false !');
} catch (err) {
utils.log('execCommand failed ! exception '+err);
}
focused.focus();
}
see jsfiddle where you can enter html segment into the textarea and copy to the clipboard with ctrl+c.
For those looking for a way to do this using ClipboardItem and cannot get it to work even with dom.events.asyncClipboard.clipboardItem set to true, the answer can be found at nikouusitalo.com.
And here is my working code (tested on Firefox 102).
const clipboardItem = new
ClipboardItem({'text/html': new Blob([html],
{type: 'text/html'}),
'text/plain': new Blob([html],
{type: 'text/plain'})});
navigator.clipboard.write([clipboardItem]).
then(_ => console.log("clipboard.write() Ok"),
error => alert(error));
Make sure you try pasting it into a rich text editor such as gmail and not to a plain text/markdown editor such as stackoverflow.
how can I get a text saved in the clipboard to a string using javascript? (or if it is to an image an image object) if it requires any permission just state which one (i use it in a chrome extension, so what should I add to the manifest in the permissions arrays?)
(don't worry, I know it sounds suspicious. I don't use the data in any form of abusive way, but for a clipboard chrome extension.)
Answer:
You can use the ClipBoard API
Per an example on MDN:
navigator.clipboard.readText().then(
clipText => document.querySelector(".editor").innerText += clipText);
Keep in mind that this is Asynchronous so you'll need to have your functions based on that Promise.
Aside:
Currently the ClipBoard API is not compatible with Internet Explorer, and other than FireFox with feature flags set, reading is the only thing allowed. This may change in the future, but just be aware that you can't expect to alter the ClipBoard through the API.
For fallback purposes you would have to use document.execCommand and most likely as follows:
let el = document.querySelector.bind(document);
let btn = el("button");
btn.addEventListener("click", function() {
let i = el("input");
//empty input
i.value = "";
//focus input
i.focus();
//paste into input
document.execCommand("paste");
//do something with pasted string
console.log(i.textContent);
});
.hidden { display: none; }
<input class="hidden">
<small>Note: This should only work in IE.</small>
<button>Get String</button>
Is there a way in javascript to copy an html string (ie <b>xx<b>) into the clipboard as text/html, so that it can then be pasted into for example a gmail message with the formatting (ie, xx in bold)
There exists solutions to copy to the clipboard as text (text/plain) for example https://stackoverflow.com/a/30810322/460084 but not as text/html
I need a non flash, non jquery solution that will work at least on IE11 FF42 and Chrome.
Ideally I would like to store both text and html versions of the string in the clipboard so that the right one can be pasted depending if the target supports html or not.
Since this answer has gotten some attention, I have completely rewritten the messy original to be easier to grasp. If you want to look at the pre-revisioned version, you can find it here.
The boiled down question:
Can I use JavaScript to copy the formatted output of some HTML code to the users clipboard?
Answer:
Yes, with some limitations, you can.
Solution:
Below is a function that will do exactly that. I tested it with your required browsers, it works in all of them. However, IE 11 will ask for confirmation on that action.
Explanation how this works can be found below, you may interactively test the function out in this jsFiddle.
// This function expects an HTML string and copies it as rich text.
function copyFormatted (html) {
// Create container for the HTML
// [1]
var container = document.createElement('div')
container.innerHTML = html
// Hide element
// [2]
container.style.position = 'fixed'
container.style.pointerEvents = 'none'
container.style.opacity = 0
// Detect all style sheets of the page
var activeSheets = Array.prototype.slice.call(document.styleSheets)
.filter(function (sheet) {
return !sheet.disabled
})
// Mount the container to the DOM to make `contentWindow` available
// [3]
document.body.appendChild(container)
// Copy to clipboard
// [4]
window.getSelection().removeAllRanges()
var range = document.createRange()
range.selectNode(container)
window.getSelection().addRange(range)
// [5.1]
document.execCommand('copy')
// [5.2]
for (var i = 0; i < activeSheets.length; i++) activeSheets[i].disabled = true
// [5.3]
document.execCommand('copy')
// [5.4]
for (var i = 0; i < activeSheets.length; i++) activeSheets[i].disabled = false
// Remove the container
// [6]
document.body.removeChild(container)
}
Explanation:
Look into the comments in the code above to see where you currently are in the following process:
We create a container to put our HTML code into.
We style the container to be hidden and detect the page's active stylesheets. The reason will be explained shortly.
We put the container into the page's DOM.
We remove possibly existing selections and select the contents of our container.
We do the copying itself. This is actually a multi-step process:
Chrome will copy text as it sees it, with applied CSS styles, while other browsers will copy it with the browser's default styles.
Therefore we will disable all user styles before copying to get the most consistent result possible.
Before we do this, we prematurely execute the copy command.
This is a hack for IE11: In this browser, the copying must be manually confirmed once. Until the user clicked the "Confirm" button, IE users would see the page without any styles. To avoid this, we copy first, wait for confirmation, then disable the styles and copy again. That time we won't get a confirmation dialog since IE remembers our last choice.
We actually disable the page's styles.
Now we execute the copy command again.
We re-enable the stylesheets.
We remove the container from the page's DOM.
And we're done.
Caveats:
The formatted content will not be perfectly consistent across browsers.
As explained above, Chrome (i.e. the Blink engine) will use a different strategy than Firefox and IE: Chrome will copy the contents with their CSS styling, but omitting any styles that are not defined.
Firefox and IE on the other hand won't apply page-specific CSS, they will apply the browser's default styles. This also means they will have some weird styles applied to them, e.g. the default font (which is usually Times New Roman).
For security reasons, browsers will only allow the function to execute as an effect of a user interaction (e.g. a click, keypress etc.)
There is a much simpler solution. Copy a section of your page (element) than copying HTML.
With this simple function you can copy whatever you want (text, images, tables, etc.) on your page or the whole document to the clipboard.
The function receives the element id or the element itself.
function copyElementToClipboard(element) {
window.getSelection().removeAllRanges();
let range = document.createRange();
range.selectNode(typeof element === 'string' ? document.getElementById(element) : element);
window.getSelection().addRange(range);
document.execCommand('copy');
window.getSelection().removeAllRanges();
}
How to use:
copyElementToClipboard(document.body);
copyElementToClipboard('myImageId');
If you want to use the new Clipboard API, use the write method like below:
var type = "text/html";
var blob = new Blob([text], { type });
var data = [new ClipboardItem({ [type]: blob })];
navigator.clipboard.write(data).then(
function () {
/* success */
},
function () {
/* failure */
}
);
Currently(Sep 2021), The problem is that Firefox doesn't support this method.
I have done a few modifications on Loilo's answer above:
setting (and later restoring) the focus to the hidden div prevents FF going into endless recursion when copying from a textarea
setting the range to the inner children of the div prevents chrome inserting an extra <br> in the beginning
removeAllRanges on getSelection() prevents appending to existing selection (possibly not needed)
try/catch around execCommand
hiding the copy div better
On OSX this will not work. Safari does not support execCommand and chrome OSX has a known bug https://bugs.chromium.org/p/chromium/issues/detail?id=552975
code:
clipboardDiv = document.createElement('div');
clipboardDiv.style.fontSize = '12pt'; // Prevent zooming on iOS
// Reset box model
clipboardDiv.style.border = '0';
clipboardDiv.style.padding = '0';
clipboardDiv.style.margin = '0';
// Move element out of screen
clipboardDiv.style.position = 'fixed';
clipboardDiv.style['right'] = '-9999px';
clipboardDiv.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
// more hiding
clipboardDiv.setAttribute('readonly', '');
clipboardDiv.style.opacity = 0;
clipboardDiv.style.pointerEvents = 'none';
clipboardDiv.style.zIndex = -1;
clipboardDiv.setAttribute('tabindex', '0'); // so it can be focused
clipboardDiv.innerHTML = '';
document.body.appendChild(clipboardDiv);
function copyHtmlToClipboard(html) {
clipboardDiv.innerHTML=html;
var focused=document.activeElement;
clipboardDiv.focus();
window.getSelection().removeAllRanges();
var range = document.createRange();
range.setStartBefore(clipboardDiv.firstChild);
range.setEndAfter(clipboardDiv.lastChild);
window.getSelection().addRange(range);
var ok=false;
try {
if (document.execCommand('copy')) ok=true; else utils.log('execCommand returned false !');
} catch (err) {
utils.log('execCommand failed ! exception '+err);
}
focused.focus();
}
see jsfiddle where you can enter html segment into the textarea and copy to the clipboard with ctrl+c.
For those looking for a way to do this using ClipboardItem and cannot get it to work even with dom.events.asyncClipboard.clipboardItem set to true, the answer can be found at nikouusitalo.com.
And here is my working code (tested on Firefox 102).
const clipboardItem = new
ClipboardItem({'text/html': new Blob([html],
{type: 'text/html'}),
'text/plain': new Blob([html],
{type: 'text/plain'})});
navigator.clipboard.write([clipboardItem]).
then(_ => console.log("clipboard.write() Ok"),
error => alert(error));
Make sure you try pasting it into a rich text editor such as gmail and not to a plain text/markdown editor such as stackoverflow.
I'm developing a shared canvas using HTML5 + javascript. I am developing copying/pasting functionality, and I have no problems to do it with Ctrl+C, +X, +V, but I also would like to add the typical buttons that provide the same functionality (mainly intended to be able to copy/paste in tablets).
The code to manage the standard events is quite straigtforward:
window.addEventListener("copy", copyFunc);
...
copyFunc(e){
if (BDKanvasInstance.selection !== null){
var data = BDKanvasInstance.selection.serialize();
var jsonData = JSON.stringify(data);
e.clipboardData.setData('application/json', jsonData);
e.preventDefault();
}
}
But I have to way to access the clipboardData from a button...
copyBtn.addEventListener("click", copyBtnFunc);
copyBtnFunc(e){
/* Any way to access clipboardData or to trigger the standard copy command? */
}
I've seen several solutions involving creating a textarea, inserting the text, selecting it programmatically and using "execCommand('copy')", but that does not copy the text with an "application/json" type...
Any solutions? With a computer using keyboard shortcuts is ok, but they are not a solution when using it on the tablet...
Thank you!
Everything is done on the same page, so I've done:
window.addEventListener("copy", copyFunc);
...
copyFunc(e){
if (BDKanvasInstance.selection !== null){
var data = BDKanvasInstance.selection.serialize();
if (e && e.clipboardData)
{
var jsonData = JSON.stringify(data);
e.clipboardData.setData('application/json', jsonData);
e.preventDefault();
} else {
internalClipboard = data;
}
}
}
copyBtn.addEventListener("click", copyBtnFunc);
copyBtnFunc(e){
copyFunc(null);
}
And similar with the three actions (copy/cut/paste). The buttons call the functions from their handlers, with a null argument. There is a "clipboard" for the buttons, and another one for the shortcuts. Dirty, but may do the trick...
Thank you!
I am facing issues while accessing clipboard data from javascript in Fire Fox. My issues is i am pasting data which greater than max length of test filed and trying to validate that we pasted text > max length. For this i am trying to read data from clipboard. But i am unable to access it from FF.
Can any one plz provide the solution.
I tried with below code
$("#abc").bind("paste", function (event) {
var clpBoardData = window.clipboardData.getData("Text");
}
});
try this
$("#abc").bind("paste", function (event) {
var clpBoardData = event.originalEvent.clipboardData.getData("Text");
console.log(clpBoardData);
}
});
u cant use 'window.clipboardData' on crome, firefox and opera
I think you are referring to this https://support.mozilla.org/en-US/questions/964543