如何阻止 Chrome 在 copy/paste 上将相对链接转换为绝对链接?
How to stop Chrome from turning relative links to absolute links on copy/paste?
我正在使用 contenteditable="true"
从 div
复制富文本并将其粘贴到 Medium 草稿。大多数格式都保留得很好,但出于某种原因,我不明白所有的相关链接都转换为绝对链接。我不知道这发生在哪一步。我什至认为 Medium 可以监听 "paste" 事件。这将是最坏的情况,因为我几乎无法控制它。但如果是这样,他们如何访问我复制内容时所在页面的 URL ?事实上,在检查了其他浏览器之后,我得出结论,这是 Chrome 的错,而不是 Medium 的错。在 Safari 上它运行完美,在 Firefox 上它根本不起作用(但这是另一个问题的主题......)。
为了让事情更清楚,我试图通过编写一个功能基本相同的小书签来模仿我在 Wordpress 博客上使用的 footnotes plugin 的行为。
这是一个演示页面,您可以在其中使用类似 wiki 的语法粘贴文本以进行内联引用,并将它们解析为适当的脚注:
https://rawgit.com/arielpontes/footnoter/master/index.html
在两种使用模式下([1] copy/pasting 到演示页面或 [2] 使用小书签),生成的 html 具有正确的相关链接。然而,在 Chrome 上粘贴回 Medium 后,它们变成绝对的,指向 rawgit.com
并破坏了功能。
如果我 运行 来自我本地机器的代码而不是 rawgit.com
,但是,即使在 Chrome.
上粘贴后,链接仍保持相对形式
可能发生了什么?有什么办法可以解决吗?
TL;DR - The one that in charge for the pasted content is the program that puts it in the clipboard.
每次您将某些内容复制到剪贴板时,执行复制的应用程序可以将多种数据类型放在那里,因此您 paste
进入的程序将能够使用最有效的一种为了它。如果是浏览器 - 当您 select 网页内容并将其复制到剪贴板时 - 浏览器将创建两种类型(html/plain
和 text/html
),因此如果您粘贴该内容到可以处理 html 的程序中 - 您将粘贴的数据将是 html,但如果不是 - 该数据将是纯文本。
基本上你有两个选择:
- 覆盖浏览器保存在剪贴板中的内容(这样 - 无论将内容粘贴到哪里 - 它看起来都将完全按照你想要的方式显示)
- 劫持粘贴事件,从剪贴板中获取数据,随意更改,然后自行放入编辑器中。
$('#text').on('paste', function(e) {
if ($('input[name=paste-type]:checked').val() == 'special') {
e.preventDefault();
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
node = document.createElement("p");
text = 'Replacement text only for the paste'
node.appendChild(document.createTextNode(text))
range.insertNode(node);
}
}
}
});
$(document).on('copy', function(e) {
if ($('input[name=copy-type]:checked').val() == 'special') {
e.preventDefault();
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0);
nodes = range.cloneContents().childNodes
content = ''
contentPlain = ''
for (var i = 0; i < nodes.length; i++) {
node = nodes[i];
contentPlain += node.textContent
if (node.nodeType == 3) {
content += node.textContent
} else if (node.nodeType == 1) {
content += node.outerHTML
}
}
}
} else {
content = '<span style="color: red; background: yellow;">Replacement text only for the copy</span>';
}
e.originalEvent.clipboardData.setData('text/html', content);
e.originalEvent.clipboardData.setData('text/plain', contentPlain);
}
});
$('#btn1').click(function() {
$('#ta1').val($('#text').html());
});
<script src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<div id="text" contenteditable="true" style="width: 400px; height :250px; border: 1px solid black;">Paste your text here...</div><br />
<textarea id="ta1" style="width: 400px; height: 150px; border: 1px solid green;" disabled="disabled"></textarea><br />
<button id="btn1">View HTML</button><br />
<label for="reg"><input type="radio" name="paste-type" value="regular" id="reg" checked="checked" /> Regular Paste</label>
<label for="special"><input type="radio" name="paste-type" value="special" id="special" /> Force my paste</label>
<br /><br />
<label for="copy-reg"><input type="radio" name="copy-type" value="regular" id="copy-reg" checked="checked" /> Regular Copy</label>
<label for="copy-special"><input type="radio" name="copy-type" value="special" id="copy-special" /> Force my copy</label>
<br /><br />
<div style="width: 400px; height: 300px; border: 1px solid red;">
<p>Nonumes molestiae <b>scripserit mei eu. In sea singulis evertitur</b>, verear inimicus delicatissimi ad eam. Eu eros scripserit cum, nam ferri ludus saperet te, ex sea nostro prompta inciderint. Est at causae .</p>
<p>Quem feugait nam cu, sed <span style="background: red;">tantas meliore eu. Propriae efficiendi at</span> has, in usu nusquam noluisse, no nam natum verterem. Eu tation dignissim pro. Id eos wisi mollis commune</p>
<p>Ea has quando blandit <a href="#a1">intellegebat, iusto</a> fabulas eos in, per consul suscipit inciderint cu. Ea veri possim nostrud vis. Id civibi. Ut duo posse <a href="#a2">graecis voluptatibus</a>, mea eu errem possim quaestio.</p>
</div>
在上面的示例中,我提供了您可以使用的选项(原始 copy/paste 和特殊 copy/paste)。
您可以在特殊副本的示例中看到,我构建了 html
字符串以从页面中的 selection 放入剪贴板(基于 DOM
元素)。通过这种方式,我能够获得 href
的确切值(无需将其更改为绝对路径)。
为了方便起见,jsfiddle中的代码完全相同:
https://jsfiddle.net/m0ad3uaa/
问题出在客户端。浏览器使用绝对 URL 复制 links,这是为了防止将 links 粘贴到不同区域时出现问题。
例如,如果我在 http://site1.com
上单击 <a href="/myresource.jpg">
上的 link,我将被定向到 http://site1.com/myresource.jpg
。
现在,如果您将同一标签复制到 http://site2.com
,link 现在将指向 http://site2.com/myresource.jpg
,它可能存在也可能不存在。
这在大多数情况下都有意义,就好像您正在使用 Chrome,您不太可能试图复制网站、资产和所有内容。它还解决了 <img>
标签指向不存在的资产的问题。
但是,说了这么多,可能会以编程方式弄乱所选择的内容。
这里有一个很好的例子:
http://bavotasan.com/2010/add-a-copyright-notice-to-copied-text/
基本上,您只想将 document.oncopy
更改为采用 window.getSelection()
并删除域名的所有实例,确保 link 是相对的。
我正在使用 contenteditable="true"
从 div
复制富文本并将其粘贴到 Medium 草稿。大多数格式都保留得很好,但出于某种原因,我不明白所有的相关链接都转换为绝对链接。我不知道这发生在哪一步。我什至认为 Medium 可以监听 "paste" 事件。这将是最坏的情况,因为我几乎无法控制它。但如果是这样,他们如何访问我复制内容时所在页面的 URL ?事实上,在检查了其他浏览器之后,我得出结论,这是 Chrome 的错,而不是 Medium 的错。在 Safari 上它运行完美,在 Firefox 上它根本不起作用(但这是另一个问题的主题......)。
为了让事情更清楚,我试图通过编写一个功能基本相同的小书签来模仿我在 Wordpress 博客上使用的 footnotes plugin 的行为。
这是一个演示页面,您可以在其中使用类似 wiki 的语法粘贴文本以进行内联引用,并将它们解析为适当的脚注:
https://rawgit.com/arielpontes/footnoter/master/index.html
在两种使用模式下([1] copy/pasting 到演示页面或 [2] 使用小书签),生成的 html 具有正确的相关链接。然而,在 Chrome 上粘贴回 Medium 后,它们变成绝对的,指向 rawgit.com
并破坏了功能。
如果我 运行 来自我本地机器的代码而不是 rawgit.com
,但是,即使在 Chrome.
可能发生了什么?有什么办法可以解决吗?
TL;DR - The one that in charge for the pasted content is the program that puts it in the clipboard.
每次您将某些内容复制到剪贴板时,执行复制的应用程序可以将多种数据类型放在那里,因此您 paste
进入的程序将能够使用最有效的一种为了它。如果是浏览器 - 当您 select 网页内容并将其复制到剪贴板时 - 浏览器将创建两种类型(html/plain
和 text/html
),因此如果您粘贴该内容到可以处理 html 的程序中 - 您将粘贴的数据将是 html,但如果不是 - 该数据将是纯文本。
基本上你有两个选择:
- 覆盖浏览器保存在剪贴板中的内容(这样 - 无论将内容粘贴到哪里 - 它看起来都将完全按照你想要的方式显示)
- 劫持粘贴事件,从剪贴板中获取数据,随意更改,然后自行放入编辑器中。
$('#text').on('paste', function(e) {
if ($('input[name=paste-type]:checked').val() == 'special') {
e.preventDefault();
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
node = document.createElement("p");
text = 'Replacement text only for the paste'
node.appendChild(document.createTextNode(text))
range.insertNode(node);
}
}
}
});
$(document).on('copy', function(e) {
if ($('input[name=copy-type]:checked').val() == 'special') {
e.preventDefault();
if (window.getSelection) {
sel = window.getSelection();
if (sel.rangeCount) {
range = sel.getRangeAt(0);
nodes = range.cloneContents().childNodes
content = ''
contentPlain = ''
for (var i = 0; i < nodes.length; i++) {
node = nodes[i];
contentPlain += node.textContent
if (node.nodeType == 3) {
content += node.textContent
} else if (node.nodeType == 1) {
content += node.outerHTML
}
}
}
} else {
content = '<span style="color: red; background: yellow;">Replacement text only for the copy</span>';
}
e.originalEvent.clipboardData.setData('text/html', content);
e.originalEvent.clipboardData.setData('text/plain', contentPlain);
}
});
$('#btn1').click(function() {
$('#ta1').val($('#text').html());
});
<script src="http://code.jquery.com/jquery-2.1.3.min.js"></script>
<div id="text" contenteditable="true" style="width: 400px; height :250px; border: 1px solid black;">Paste your text here...</div><br />
<textarea id="ta1" style="width: 400px; height: 150px; border: 1px solid green;" disabled="disabled"></textarea><br />
<button id="btn1">View HTML</button><br />
<label for="reg"><input type="radio" name="paste-type" value="regular" id="reg" checked="checked" /> Regular Paste</label>
<label for="special"><input type="radio" name="paste-type" value="special" id="special" /> Force my paste</label>
<br /><br />
<label for="copy-reg"><input type="radio" name="copy-type" value="regular" id="copy-reg" checked="checked" /> Regular Copy</label>
<label for="copy-special"><input type="radio" name="copy-type" value="special" id="copy-special" /> Force my copy</label>
<br /><br />
<div style="width: 400px; height: 300px; border: 1px solid red;">
<p>Nonumes molestiae <b>scripserit mei eu. In sea singulis evertitur</b>, verear inimicus delicatissimi ad eam. Eu eros scripserit cum, nam ferri ludus saperet te, ex sea nostro prompta inciderint. Est at causae .</p>
<p>Quem feugait nam cu, sed <span style="background: red;">tantas meliore eu. Propriae efficiendi at</span> has, in usu nusquam noluisse, no nam natum verterem. Eu tation dignissim pro. Id eos wisi mollis commune</p>
<p>Ea has quando blandit <a href="#a1">intellegebat, iusto</a> fabulas eos in, per consul suscipit inciderint cu. Ea veri possim nostrud vis. Id civibi. Ut duo posse <a href="#a2">graecis voluptatibus</a>, mea eu errem possim quaestio.</p>
</div>
在上面的示例中,我提供了您可以使用的选项(原始 copy/paste 和特殊 copy/paste)。
您可以在特殊副本的示例中看到,我构建了 html
字符串以从页面中的 selection 放入剪贴板(基于 DOM
元素)。通过这种方式,我能够获得 href
的确切值(无需将其更改为绝对路径)。
为了方便起见,jsfiddle中的代码完全相同: https://jsfiddle.net/m0ad3uaa/
问题出在客户端。浏览器使用绝对 URL 复制 links,这是为了防止将 links 粘贴到不同区域时出现问题。
例如,如果我在 http://site1.com
上单击 <a href="/myresource.jpg">
上的 link,我将被定向到 http://site1.com/myresource.jpg
。
现在,如果您将同一标签复制到 http://site2.com
,link 现在将指向 http://site2.com/myresource.jpg
,它可能存在也可能不存在。
这在大多数情况下都有意义,就好像您正在使用 Chrome,您不太可能试图复制网站、资产和所有内容。它还解决了 <img>
标签指向不存在的资产的问题。
但是,说了这么多,可能会以编程方式弄乱所选择的内容。
这里有一个很好的例子: http://bavotasan.com/2010/add-a-copyright-notice-to-copied-text/
基本上,您只想将 document.oncopy
更改为采用 window.getSelection()
并删除域名的所有实例,确保 link 是相对的。