I have created a custom context menu (including a menu entry to copy arbitrary text that has previously been selected to the clipboard) which works fine, except for one thing: When attempting to copy and paste a text snippet from a web page somewhere else the formatting of said snippet (line breaks, etc.) is lost.
Now the question that's bothering me is, is there any way to get the selected (arbitrary) text and retain its formatting? I'm currently using this function to obtain the text:
function GetSelection()
{
var l_result;
if(window.getSelection)
l_result = window.getSelection();
else
if(document.getSelection)
l_result = document.getSelection();
else
if(document.selection)
l_result = document.selection.createRange().text;
return l_result;
}
This function is invoked by clicking on the Copy menu item in the context menu and so has the mandatory user intervention.
To facilitate the transfer mechanism I have also attached the following event handler (I found this part in this answer; unfortunately that one gets the contents of an entire element and copies it to the clipboard, which violates the prerequisite that arbitrary text selected by the user should be copied):
document.addEventListener('copy', function (p_event) {
if(selected == '')
return;
p_event.clipboardData.setData('text/plain', selected);
p_event.preventDefault();
selected = '';
}, true);
Right now the MIME type of the selection to be copied is set to text/plain
, because I currently cannot convince the clipboardData object to accept that as text/html
. I have done some googling around now, but I didn't find any viable solution so far.
Here's a full test page to demonstrate the effect (tested with Firefox 61.0.2):
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xml:lang="en">
<head>
<title>Clipboard Test Case</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script type="application/javascript">
/* <![CDATA[ */
var selected = '';
function GetSelection()
{
var l_result;
if(window.getSelection)
l_result = window.getSelection();
else
if(document.getSelection)
l_result = document.getSelection();
else
if(document.selection)
l_result = document.selection.createRange().text;
return l_result;
}
document.addEventListener('DOMContentLoaded', function () {
document.addEventListener('copy', function(p_element) {
if(selected == '')
return;
p_event.clipboardData.setData('text/html', selected); // Fails
p_event.clipboardData.setData('text/plain', selected); // Works
p_event.preventDefault();
selected = '';
}, true);
document.getElementById('grab-text').addEventListener('click', function (p_event) {
selected = GetSelection();
if(selected == '')
{
alert('Please select some text first!');
return;
}
document.execCommand('copy');
}, false);
}, false);
/* ]]> */
</script>
</head>
<body>
<header><h1>Clipboard Test Case</h1></header>
<main>
<p style="white-space: pre;">
Lorem ipsum dolor sit amet,
consetetur sadipscing elitr,
sed diam nonumy eirmod tempor invidunt
ut labore et dolore magna aliquyam erat,
sed diam voluptua.
At vero eos et accusam et justo duo dolores et ea rebum.
Stet clita kasd gubergren,
no sea takimata sanctus est Lorem ipsum dolor sit amet.
Lorem ipsum dolor sit amet,
consetetur sadipscing elitr,
sed diam nonumy eirmod tempor invidunt
ut labore et dolore magna aliquyam erat,
sed diam voluptua.
At vero eos et accusam et justo duo dolores et ea rebum.
Stet clita kasd gubergren,
no sea takimata sanctus est Lorem ipsum dolor sit amet.
</p>
<form action="javascript:void(0);">
<input type="button" id="grab-text" value="Grab text!" />
</form>
</main>
</body>
</html>
Viable solutions are to be in native JavaScript and no JQuery or other libraries, nor any detours via Flash or the likes.