如何添加额外的信息复制网络文本

一些网站现在使用来自 Tynt的 JavaScript 服务,将文本附加到复制的内容中。

如果你使用这个从一个网站复制文本,然后粘贴,你会得到一个链接到文本底部的原始内容。

Tynt 也在追踪这个,干得漂亮。

他们这样做的脚本是令人印象深刻的-而不是试图操纵剪贴板(只有旧版本的 IE 默认允许他们这样做,应该总是关闭) ,他们操纵实际的选择。

因此,当您选择一块文本时,额外的内容将作为隐藏的 <div>添加到您的选择中。当您粘贴额外的样式时,将忽略并显示额外的链接。

对于简单的文本块,这实际上相当容易做到,但是当您考虑在不同浏览器中跨复杂 HTML 的所有可能选择时,这将是一场噩梦。

我正在开发一个网络应用程序-我不希望任何人能够跟踪复制的内容,我希望额外的信息包含一些上下文,而不只是一个链接。在这种情况下,Tynt 的服务并不合适。

有没有人知道一个开源的 JavaScript 库(可能是 jQuery 插件或类似的)提供类似的功能,但是不公开内部应用程序数据?

56452 次浏览

2022 Update

More complex solution that handles rich text formatting. The 2020 solution is still relevant if you only deal with plain text.

const copyListener = (event) => {
const range = window.getSelection().getRangeAt(0),
rangeContents = range.cloneContents(),
pageLink = `Read more at: ${document.location.href}`,
helper = document.createElement("div");


helper.appendChild(rangeContents);


event.clipboardData.setData("text/plain", `${helper.innerText}\n${pageLink}`);
event.clipboardData.setData("text/html", `${helper.innerHTML}<br>${pageLink}`);
event.preventDefault();
};
document.addEventListener("copy", copyListener);
#richText {
width: 415px;
height: 70px;
border: 1px solid #777;
overflow: scroll;
}


#richText:empty:before {
content: "Paste your copied text here";
color: #888;
}
<h4>Rich text:</h4>
<p>Lorem <u>ipsum</u> dolor sit <b>amet</b>, consectetur <i>adipiscing</i> elit.</p>
<h4>Plain text editor:</h4>
<textarea name="textarea" rows="5" cols="50" placeholder="Paste your copied text here"></textarea>
<h4>Rich text editor:</h4>
<div id="richText" contenteditable="true"></div>


2020 Update

Solution that works on all recent browsers.

Note that this solution will strip rich text formatting (such as bold and italic), even when pasting into a rich text editor.

document.addEventListener('copy', (event) => {
const pagelink = `\n\nRead more at: ${document.location.href}`;
event.clipboardData.setData('text/plain', document.getSelection() + pagelink);
event.preventDefault();
});
Lorem ipsum dolor sit <b>amet</b>, consectetur <i>adipiscing</i> elit.<br/>
<textarea name="textarea" rows="7" cols="50" placeholder="paste your copied text here"></textarea>


[Older post - before the 2020 update]

There are two main ways to add extra info to copied web text.

  1. Manipulating the selection

The idea is to watch for the copy event, then append a hidden container with our extra info to the dom, and extend the selection to it.
This method is adapted from this article by c.bavota. Check also jitbit's version for more complex case.

  • Browser compatibility: All major browsers, IE > 8.
  • Demo: jsFiddle demo.
  • Javascript code:

function addLink() {
//Get the selected text and append the extra info
var selection = window.getSelection(),
pagelink = '<br /><br /> Read more at: ' + document.location.href,
copytext = selection + pagelink,
newdiv = document.createElement('div');


//hide the newly created container
newdiv.style.position = 'absolute';
newdiv.style.left = '-99999px';


//insert the container, fill it with the extended text, and define the new selection
document.body.appendChild(newdiv);
newdiv.innerHTML = copytext;
selection.selectAllChildren(newdiv);


window.setTimeout(function () {
document.body.removeChild(newdiv);
}, 100);
}


document.addEventListener('copy', addLink);
  1. Manipulating the clipboard

The idea is to watch the copy event and directly modify the clipboard data. This is possible using the clipboardData property. Note that this property is available in all major browsers in read-only; the setData method is only available on IE.

  • Browser compatibility: IE > 4.
  • Demo: jsFiddle demo.
  • Javascript code:

function addLink(event) {
event.preventDefault();


var pagelink = '\n\n Read more at: ' + document.location.href,
copytext =  window.getSelection() + pagelink;


if (window.clipboardData) {
window.clipboardData.setData('Text', copytext);
}
}


document.addEventListener('copy', addLink);

Also a little shorter solution:

jQuery( document ).ready( function( $ )
{
function addLink()
{
var sel = window.getSelection();
var pagelink = "<br /><br /> Source: <a href='" + document.location.href + "'>" + document.location.href + "</a><br />© text is here";
var div = $( '<div>', {style: {position: 'absolute', left: '-99999px'}, html: sel + pagelink} );
$( 'body' ).append( div );
sel.selectAllChildren( div[0] );
div.remove();
}






document.oncopy = addLink;
} );

The shortest version for jQuery that I tested and is working is:

jQuery(document).on('copy', function(e)
{
var sel = window.getSelection();
var copyFooter =
"<br /><br /> Source: <a href='" + document.location.href + "'>" + document.location.href + "</a><br />© YourSite";
var copyHolder = $('<div>', {html: sel+copyFooter, style: {position: 'absolute', left: '-99999px'}});
$('body').append(copyHolder);
sel.selectAllChildren( copyHolder[0] );
window.setTimeout(function() {
copyHolder.remove();
},0);
});

Here is a plugin in jquery to do that https://github.com/niklasvh/jquery.plugin.clipboard From the project readme "This script modifies the contents of a selection prior to a copy event being called, resulting in the copied selection being different from what the user selected.

This allows you to append/prepend content to the selection, such as copyright information or other content.

Released under MIT License"

Improving on the answer, restore selection after the alterations to prevent random selections after copy.

function addLink() {
//Get the selected text and append the extra info
var selection = window.getSelection(),
pagelink = '<br /><br /> Read more at: ' + document.location.href,
copytext = selection + pagelink,
newdiv = document.createElement('div');
var range = selection.getRangeAt(0); // edited according to @Vokiel's comment


//hide the newly created container
newdiv.style.position = 'absolute';
newdiv.style.left = '-99999px';


//insert the container, fill it with the extended text, and define the new selection
document.body.appendChild(newdiv);
newdiv.innerHTML = copytext;
selection.selectAllChildren(newdiv);


window.setTimeout(function () {
document.body.removeChild(newdiv);
selection.removeAllRanges();
selection.addRange(range);
}, 100);
}


document.addEventListener('copy', addLink);

Improvement for 2018

document.addEventListener('copy', function (e) {
var selection = window.getSelection();
e.clipboardData.setData('text/plain', $('<div/>').html(selection + "").text() + "\n\n" + 'Source: ' + document.location.href);
e.clipboardData.setData('text/html', selection + '<br /><br /><a href="' + document.location.href + '">Source</a>');
e.preventDefault();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


<p>Example text with <b>bold</b> and <i>italic</i>. Try copying and pasting me into a rich text editor.</p>

This is a vanilla javascript solution from a modified solution above but supports more browsers (cross browser method)

function addLink(e) {
e.preventDefault();
var pagelink = '\nRead more: ' + document.location.href,
copytext =  window.getSelection() + pagelink;
clipdata = e.clipboardData || window.clipboardData;
if (clipdata) {
clipdata.setData('Text', copytext);
}
}
document.addEventListener('copy', addLink);

It's a compilation of 2 answers above + compatibility with Microsoft Edge.

I've also added a restore of the original selection at the end, as it is expected by default in any browser.

function addCopyrightInfo() {
//Get the selected text and append the extra info
var selection, selectedNode, html;
if (window.getSelection) {
var selection = window.getSelection();
if (selection.rangeCount) {
selectedNode = selection.getRangeAt(0).startContainer.parentNode;
var container = document.createElement("div");
container.appendChild(selection.getRangeAt(0).cloneContents());
html = container.innerHTML;
}
}
else {
console.debug("The text [selection] not found.")
return;
}


// Save current selection to resore it back later.
var range = selection.getRangeAt(0);


if (!html)
html = '' + selection;


html += "<br/><br/><small><span>Source: </span><a target='_blank' title='" + document.title + "' href='" + document.location.href + "'>" + document.title + "</a></small><br/>";
var newdiv = document.createElement('div');


//hide the newly created container
newdiv.style.position = 'absolute';
newdiv.style.left = '-99999px';


// Insert the container, fill it with the extended text, and define the new selection.
selectedNode.appendChild(newdiv); // *For the Microsoft Edge browser so that the page wouldn't scroll to the bottom.


newdiv.innerHTML = html;
selection.selectAllChildren(newdiv);


window.setTimeout(function () {
selectedNode.removeChild(newdiv);
selection.removeAllRanges();
selection.addRange(range); // Restore original selection.
}, 5); // Timeout is reduced to 10 msc for Microsoft Edge's sake so that it does not blink very noticeably.
}


document.addEventListener('copy', addCopyrightInfo);