这个问题来源于我的一个项目需求,需要在点击分享按钮时,将文本复制到剪贴板以方便用户直接进行粘贴操作。
在解决另一个衍生问题的同时,我在这里使用了四种不同的复制方式,如下文:
react-copy-to-clipboard
这个组件是对
copy-to-clipboard
组件的二次封装
说起来这类通用组件的使用其实很简单,按照 README 指导就行。
但是我这里要实现的效果和官方提供的 Demo 略有不同,也是在这一步最后测试发现出现问题。
class App extends React.PureComponent {
state = {value: 'some\ntext', copied: false};
onChange = ({target: {value}}) => {
this.setState({value, copied: false});
};
onClick = ({target: {innerHTML}}) => {
console.log(`Clicked on "${innerHTML}"!`); // eslint-disable-line
};
onCopy = () => {
this.setState({copied: true});
};
render() {
return (
<div className="app">
<h1>react-copy-to-clipboard</h1>
<section className="section">
<textarea onChange={this.onChange} rows={2} cols={10} value={this.state.value} />
</section>
<section className="section">
<h2>with onClick</h2>
<CopyToClipboard
onCopy={this.onCopy}
options={{message: 'Whoa!'}}
text={this.state.value}>
<button onClick={this.onClick}>Copy to clipboard with onClick prop</button>
</CopyToClipboard>
</section>
</div>
);
}
}
const appRoot = document.createElement('div');
document.body.appendChild(appRoot);
ReactDOM.render(<App />, appRoot);
因为我需要在 <CopyToClipBoard>
中包裹两个按钮,而只让其中一个产生复制效果,这里就出现了问题。
组件高度封装导致包裹在组件中的按钮默认都和复制功能相绑定,因此反而无法应用在这一场景。
copy-to-clipboard
这里的实现方式其实和
react-copy-to-clipboard
相差不多
相对而言,这个组件使用较为简单。我们只需要在 copy
方法执行时将需要被复制对文本作为参数传递进去即可。
import copy from 'copy-to-clipboard';
onChange = ({ target: { value } }) => {
this.setState({ value });
};
onClickCopy = () => {
const { value } = this.state;
copy(value);
...
}
render() {
const { value } = this.state
return (
...
<textarea value={value} onChange={this.onChange}></textarea>
...
)
}
这里将
textarea
的数据实时监听,并在点击事件触发时传递给copy
,从而实现复制内容。
clipboard.js
clipboard.js 的实现是通过使用类
jQuery
式的选择器,绑定文本框,然后可以去监听success
事件
<!-- Target -->
<textarea id="bar">Mussum ipsum cacilds...</textarea>
<!-- Trigger -->
<button class="btn" data-clipboard-action="cut" data-clipboard-target="#bar">
Cut to clipboard
</button>
var clipboard = new ClipboardJS('.btn');
clipboard.on('success', function(e) {
console.info('Action:', e.action);
console.info('Text:', e.text);
console.info('Trigger:', e.trigger);
e.clearSelection();
});
clipboard.on('error', function(e) {
console.error('Action:', e.action);
console.error('Trigger:', e.trigger);
});
execCommand
这个是浏览器原生API
使用 document.execCommand
时在 ios 端会出现一点问题,因为无法使用 input.select()
方法
所以在这里我使用了 range 对象,构建了一个可以被选中的文本区域:
selectText(textbox, startIndex, stopIndex) {
if (textbox.createTextRange) {
//ie
const range = textbox.createTextRange();
range.collapse(true);
range.moveStart('character', startIndex); //起始光标
range.moveEnd('character', stopIndex - startIndex); //结束光标
range.select(); //不兼容苹果
} else {
//firefox/chrome
textbox.setSelectionRange(startIndex, stopIndex);
textbox.focus();
}
}
let textarea = document.getElementById('clipboard-text');
this.selectText(textarea, 0, value.length);
if (document.execCommand('copy')) {
// ...
}
最终选择
考虑到客户端问题,目前选择了
copy-to-clipboard
进行实现。
react-copy-to-clipboard
存在问题,被放弃
clipboard.js
没有进行过多尝试
document.execCommand
因为需要进行文本域选中,不符合客户端要求,所以放弃。
总结
事实上后续我在查看前面封装好的组件源码时,发现其实它们底层调用的基本都是 document.execCommand
,只是在这一基础上进行了其他更完善的考虑。
版权属于:ajycc20
本文链接:https://ajycc20.top/archives/21.html
所有原创文章采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。 您可以自由的转载和修改,但请务必注明文章来源并且不可用于商业目的。