Javascript - 将字符串作为 text/html 复制到剪贴板
Javascript - Copy string to clipboard as text/html
有没有办法在javascript中将html字符串(即<b>xx<b>
)复制到剪贴板中作为text/html,以便随后可以将其粘贴到例如,格式为 xx 的 gmail 邮件
存在将文本(text/plain)复制到剪贴板的解决方案,例如 但不是 text/html
我需要一个至少在 IE11 FF42 和 Chrome.
上工作的非闪存、非 jquery 解决方案
理想情况下,我想在剪贴板中存储字符串的文本和 html 版本,以便可以粘贴正确的版本,具体取决于目标是否支持 html。
由于这个答案受到了一些关注,我已经完全重写了凌乱的原文以便于理解。想看改版前的可以找here.
归纳问题:
Can I use JavaScript to copy the formatted output of some HTML code to the users clipboard?
答案:
是的,但有一些限制,您可以。
解法:
下面是一个函数,可以做到这一点。我用您需要的浏览器对其进行了测试,它适用于所有浏览器。但是,IE 11 将要求对该操作进行确认。
可以在下面找到其工作原理的说明,您可以在此 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)
}
解释:
查看上面代码中的注释,了解您当前在以下过程中所处的位置:
- 我们创建一个容器来放入我们的 HTML 代码。
- 我们将容器样式设置为隐藏并检测页面的活动样式表。稍后会解释原因。
- 我们把容器放到页面的DOM.
- 我们删除可能存在的 select 离子和 select 我们容器的内容。
我们自己进行复制。这实际上是一个多步骤的过程:
Chrome 将复制它看到的文本,并应用 CSS 样式,而其他浏览器将使用浏览器的默认样式复制它。
因此,我们将在复制之前禁用所有用户样式,以获得尽可能一致的结果。
- 在执行此操作之前,我们过早地执行了
copy
命令。
这是IE11的hack:在这个浏览器中,复制必须手动确认一次。在用户单击 "Confirm" 按钮之前,IE 用户将看到没有任何样式的页面。为了避免这种情况,我们先复制,等待确认,然后禁用样式并再次复制。那时我们不会收到确认对话框,因为 IE 会记住我们最后的选择。
- 我们实际上禁用了页面的样式。
- 现在我们再次执行
copy
命令。
- 我们重新启用样式表。
- 我们从页面的 DOM 中删除容器。
我们完成了。
注意事项:
格式化的内容在浏览器之间不会完全一致。
如上所述,Chrome(即 Blink 引擎)将使用与 Firefox 和 IE 不同的策略:Chrome 将使用其 CSS 样式复制内容,但省略任何未定义的样式。
另一方面,Firefox 和 IE 不会应用特定页面 CSS,它们会应用浏览器的默认样式。这也意味着他们将应用一些奇怪的样式,例如默认字体(通常为 Times New Roman)。
出于安全原因,浏览器将只允许函数作为用户交互的结果执行(例如单击、按键等)
我对上面Loilo的回答做了一些修改:
设置(并稍后恢复)焦点到隐藏 div 防止 FF 在从文本区域复制时进入无限递归
将范围设置为 div 的内部子项可防止 chrome 在开头插入额外的 <br>
removeAllRanges on getSelection() 防止附加到现有选择(可能不需要)
try/catch围绕execCommand
隐藏副本div更好
在 OSX 这将不起作用。 Safari 不支持 execCommand 并且 chrome OSX 有一个已知错误 https://bugs.chromium.org/p/chromium/issues/detail?id=552975
代码:
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();
}
请参阅 jsfiddle,您可以在其中将 html 段输入文本区域并使用 ctrl+c 复制到剪贴板。
有一个更简单的解决方案。复制页面(元素)的一部分而不是复制 HTML.
使用这个简单的功能,您可以将页面或整个文档中的任何内容(文本、图像、表格等)复制到剪贴板。
该函数接收元素 id 或 元素 本身。
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();
}
使用方法:
copyElementToClipboard(document.body);
copyElementToClipboard('myImageId');
如果您想像下面这样使用新的 Clipboard API, use the write
method:
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 */
}
);
目前(2021 年 9 月),问题是 Firefox doesn't support this method。
有没有办法在javascript中将html字符串(即<b>xx<b>
)复制到剪贴板中作为text/html,以便随后可以将其粘贴到例如,格式为 xx 的 gmail 邮件
存在将文本(text/plain)复制到剪贴板的解决方案,例如 但不是 text/html
我需要一个至少在 IE11 FF42 和 Chrome.
上工作的非闪存、非 jquery 解决方案理想情况下,我想在剪贴板中存储字符串的文本和 html 版本,以便可以粘贴正确的版本,具体取决于目标是否支持 html。
由于这个答案受到了一些关注,我已经完全重写了凌乱的原文以便于理解。想看改版前的可以找here.
归纳问题:
Can I use JavaScript to copy the formatted output of some HTML code to the users clipboard?
答案:
是的,但有一些限制,您可以。
解法:
下面是一个函数,可以做到这一点。我用您需要的浏览器对其进行了测试,它适用于所有浏览器。但是,IE 11 将要求对该操作进行确认。
可以在下面找到其工作原理的说明,您可以在此 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)
}
解释:
查看上面代码中的注释,了解您当前在以下过程中所处的位置:
- 我们创建一个容器来放入我们的 HTML 代码。
- 我们将容器样式设置为隐藏并检测页面的活动样式表。稍后会解释原因。
- 我们把容器放到页面的DOM.
- 我们删除可能存在的 select 离子和 select 我们容器的内容。
我们自己进行复制。这实际上是一个多步骤的过程: Chrome 将复制它看到的文本,并应用 CSS 样式,而其他浏览器将使用浏览器的默认样式复制它。 因此,我们将在复制之前禁用所有用户样式,以获得尽可能一致的结果。
- 在执行此操作之前,我们过早地执行了
copy
命令。 这是IE11的hack:在这个浏览器中,复制必须手动确认一次。在用户单击 "Confirm" 按钮之前,IE 用户将看到没有任何样式的页面。为了避免这种情况,我们先复制,等待确认,然后禁用样式并再次复制。那时我们不会收到确认对话框,因为 IE 会记住我们最后的选择。 - 我们实际上禁用了页面的样式。
- 现在我们再次执行
copy
命令。 - 我们重新启用样式表。
- 在执行此操作之前,我们过早地执行了
- 我们从页面的 DOM 中删除容器。
我们完成了。
注意事项:
格式化的内容在浏览器之间不会完全一致。
如上所述,Chrome(即 Blink 引擎)将使用与 Firefox 和 IE 不同的策略:Chrome 将使用其 CSS 样式复制内容,但省略任何未定义的样式。
另一方面,Firefox 和 IE 不会应用特定页面 CSS,它们会应用浏览器的默认样式。这也意味着他们将应用一些奇怪的样式,例如默认字体(通常为 Times New Roman)。
出于安全原因,浏览器将只允许函数作为用户交互的结果执行(例如单击、按键等)
我对上面Loilo的回答做了一些修改:
设置(并稍后恢复)焦点到隐藏 div 防止 FF 在从文本区域复制时进入无限递归
将范围设置为 div 的内部子项可防止 chrome 在开头插入额外的
<br>
removeAllRanges on getSelection() 防止附加到现有选择(可能不需要)
try/catch围绕execCommand
隐藏副本div更好
在 OSX 这将不起作用。 Safari 不支持 execCommand 并且 chrome OSX 有一个已知错误 https://bugs.chromium.org/p/chromium/issues/detail?id=552975
代码:
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();
}
请参阅 jsfiddle,您可以在其中将 html 段输入文本区域并使用 ctrl+c 复制到剪贴板。
有一个更简单的解决方案。复制页面(元素)的一部分而不是复制 HTML.
使用这个简单的功能,您可以将页面或整个文档中的任何内容(文本、图像、表格等)复制到剪贴板。 该函数接收元素 id 或 元素 本身。
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();
}
使用方法:
copyElementToClipboard(document.body);
copyElementToClipboard('myImageId');
如果您想像下面这样使用新的 Clipboard API, use the write
method:
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 */
}
);
目前(2021 年 9 月),问题是 Firefox doesn't support this method。