目录
写在最前面
- 有一个简单的需求,用户需要快捷的复制一些相关的信息,然后进行下一步信息的填写。前端这里需要做一个剪贴板方便用户体验。想直接参考 react 使用的可以看 使用react和typescript改写和优化一下
- 大概设计如下,有多条信息,然后用户可以点击右边的复制 icon 进行快捷的复制。
怎么使用JavaScript实现一个剪贴板
-
具体分为五步
- 1、创建一个
textarea
,把需要的文本放进textarea
中 - 2、将
textarea
元素插入body
中。 - 3、使用
HTMLInputElement.select()
方法选择textarea
中的文本内容 - 4、使用
document.execCommand('copy')
复制textarea
中的文本内容到剪贴板 - 5、从
body
删除textarea
元素
- 1、创建一个
-
code
const copyToClipboard = str => {
const el = document.createElement('textarea');
el.value = str;
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
};
必要 api 参考
上面的方法不是很完美我们优化一下
- 这个方法不是在每个地方都能运行,由于 textarea 的插入和移除,有时候会出现页面的频闪和抖动
- 下面用 css 优化一下我们的 textarea 样式,隐藏 textarea 的显示。
const copyToClipboard = str => {
const el = document.createElement('textarea');
el.value = str;
el.setAttribute('readonly', '');
el.style.position = 'absolute';
el.style.left = '-9999px';
document.body.appendChild(el);
el.select();
document.execCommand('copy');
document.body.removeChild(el);
};
思考一个问题
- 我们用户在使用我们的剪贴板之前可能已经选择了已存在 html 中的文本内容了,所以我们这里需要多加一些判断防止遗漏用户选择的文本。
DocumentOrShadowRoot.getSelection(), Selection.rangeCount, Selection.getRangeAt(), Selection.removeAllRanges() and Selection.addRange()
这些方法存储用户选择的文本内容和解决范围选择的问题
const copyToClipboard = str => {
const el = document.createElement('textarea'); // Create a <textarea> element
el.value = str; // Set its value to the string that you want copied
el.setAttribute('readonly', ''); // Make it readonly to be tamper-proof
el.style.position = 'absolute';
el.style.left = '-9999px'; // Move outside the screen to make it invisible
document.body.appendChild(el); // Append the <textarea> element to the HTML document
const selected =
document.getSelection().rangeCount > 0 // Check if there is any content selected previously
? document.getSelection().getRangeAt(0) // Store selection if found
: false; // Mark as false to know no selection existed before
el.select(); // Select the <textarea> content
document.execCommand('copy'); // Copy - only works as a result of a user action (e.g. click events)
document.body.removeChild(el); // Remove the <textarea> element
if (selected) { // If a selection existed before copying
document.getSelection().removeAllRanges(); // Unselect everything on the HTML document
document.getSelection().addRange(selected); // Restore the original selection
}
};
使用react和typescript改写和优化一下
- 学习了上面的文章,结合产品的需求改写一下相关代码。
- 思路
- 1、首先创建一个 targetNode,设置绝对布局,赢藏我们的元素
- 2、document.getSelection() 已经由 window.getSelection() 替代了,具体流程如上
- 3、创建一个 result 标记能否能正常 使用剪贴功能,不能的返回 false
- 4、删除这个 targetNode
function createNode(text) {
const node = document.createElement('div');
node.innerText = text;
node.style.cssText = 'position:absolute; top: 0; left: 0; height:0; width:0; pointer-events: none;';
document.body.appendChild(node);
return node;
}
export default function copyMe(text) {
const targetNode = createNode(text);
const range = document.createRange();
const selection = window.getSelection()!;
const selected = selection.rangeCount > 0
? selection.getRangeAt(0)
: false;
targetNode.focus(); // focus 我们需要的文本
range.selectNodeContents(targetNode);
if(selected){
selection.removeAllRanges();
selection.addRange(range);
}
let result;
try {
result = document.execCommand('copy');
} catch (e) {
result = false;
}
document.body.removeChild(targetNode);
return result;
}
如何使用copyme
import React, { Fragment } from 'react';
import copyMe from 'utils/copyMe';
interface ItemProps {
value?: string | number;
}
const Item: React.FC<ItemProps> = props => {
const { value } = props;
const copyme = () => {
alert(copyMe(value) ? 'Copied!' : 'Failed!');
};
return (
<Fragment>
{value && (
<div>
{value}
<textarea value={value} readOnly></textarea>
<span onClick={copyme}></span>
</div>
)}
</Fragment>
);
};
export default Item;