访问剪贴板
使用 navigator.clipboard 对象访问系统的剪贴板:
- readText: 读取剪贴板的内容
- writeText: 设置剪贴板的内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>System Clipboard</title> </head>
<body> <button id="button-copy">Copy</button> <button id="button-paste">Paste</button>
<script> document.querySelector('#button-copy').addEventListener('click', () => { navigator.clipboard.writeText('Hello'); });
document.querySelector('#button-paste').addEventListener('click', () => { navigator.clipboard.readText().then(text => { console.log(text); }); }); </script> </body> </html>
|
提示: IE 不支持 Navigator.clipboard,但 Edge 支持。
绑定快捷键
在 document 上注册键盘事件,按下组合键 ctrl + c 时触发动作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>System Clipboard</title> </head> <body> <script> let ctrlPressed = false; let cPressed = false;
document.addEventListener('keydown', e => { switch(e.keyCode) { case 17: ctrlPressed = true; break; case 67: cPressed = true; break; default: ; }
if (ctrlPressed && cPressed) { console.log('ctrl and c are both pressed: ' + Date.now()); } });
document.addEventListener('keyup', e => { switch(e.keyCode) { case 17: ctrlPressed = false; break; case 67: cPressed = false; break; default: ; } }); </script> </body> </html>
|
在指定的元素上触发快捷键,而不是整个 document,如果一个元素可以捕捉键盘事件,其需要能够获得焦点。默认能够获得焦点的 DOM 元素有 input, button 等,div 不能获得焦点。
为了使得一个元素能够获得焦点,设置其属性 tabindex="-1"
即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>System Clipboard</title>
<style> .container { display: inline-block; width: 300px; height: 300px; background: #efefef; outline: none; border-radius: 4px; }
.container:focus { box-shadow: 0 0 5px rgb(50, 123, 218) inset; } </style> </head>
<body> <div id="container-1" class="container" tabindex='-1'></div> <div id="container-2" class="container" tabindex='-1'></div>
<script> const holder = document.querySelector('#container-2');
let ctrlPressed = false; let cPressed = false;
holder.addEventListener('keydown', e => { switch(e.keyCode) { case 17: ctrlPressed = true; break; case 67: cPressed = true; break; default: ; }
if (ctrlPressed && cPressed) { console.log('ctrl and c are both pressed: ' + Date.now()); } });
holder.addEventListener('keyup', e => { switch(e.keyCode) { case 17: ctrlPressed = false; break; case 67: cPressed = false; break; default: ; } }); </script> </body> </html>
|
剪贴板和快捷键插件
已经有比较成熟的操作系统剪贴板和快捷键的插件可以使用,在项目中没必要像上面这样手动原生的实现一遍:
参考示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <template> <div class="problem"> <Button @click="copy">Copy</Button> <Button @click="paste">Paste</Button>
<button class="sbtn" v-shortkey="['ctrl', 'c']" @shortkey="theAction">Open</button> <button class="sbtn" v-shortkey="['ctrl', 'v']" @shortkey="theAction">Open</button>
<Input v-model="name"/> </div> </template>
<script> import * as clipboard from 'clipboard-polyfill/text';
export default { data() { return { name: 'Hi' }; }, methods: { copy(e) { clipboard.writeText('This text is plain.'); }, paste() { clipboard.readText().then(text => { this.name = text; }).catch((err) => { console.log(err); }); }, theAction(e) { console.log(e); } } }; </script>
<style lang="scss"> .sbtn { display: none; } </style>
|
复制粘贴组件
同一个页面中的组件,数据还可以通过全局变量、Vuex 的 store 等进行传递,但是跨页面的时候,不同页面里的 JS 对象就不能够互相访问了,这个时候需要通过一个中间层来传递数据,例如系统的剪贴板。
跨页面的组件复制粘贴的原理为:
- 选择要复制的组件
- 按下复制快捷键,把被复制组件的信息保存为 JSON 格式的字符串,然后设置到系统的剪贴板里
- 到另一个页面按下粘贴快捷键后,读取系统的剪贴板里的字符串,转换为 JSON 对象,校验得到的对象是否有效
- 获取得到的对象信息,创建对应的组件
组件复制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| <template> <div class="com-from"> <div class="news" v-shortkey="['ctrl', 'c']" @shortkey="copySelectedNews"> <Card v-for="news in newses" :key="news.title" dis-hover> <p slot="title">{{ news.title }}</p> <p>{{ news.content }}</p>
<Icon slot="extra" :type="newsIconType(news)" @click="selectNews(news)"/> </Card> </div> </div> </template>
<script> import * as clipboard from 'clipboard-polyfill/text';
export default { data() { return { newses: [ { title: '罗永浩承认售假', content: '罗永浩在 “交个朋友直播间” 销售了 “皮尔卡丹” 品牌的羊毛衫,有消费者在收到货后怀疑衣服不是纯羊毛,而是假冒伪劣产品' }, { title: '羊肉价格持续上涨', content: '羊肉价格持续 8 周上涨,每斤超 40 元,什么原因造成“羊贵妃”' }, ], selectedNews: null, }; }, methods: { newsIconType(news) { return news === this.selectedNews ? 'ios-checkmark-circle' : 'ios-checkmark-circle-outline'; }, selectNews(news) { this.selectedNews = news; }, copySelectedNews() { clipboard.writeText(JSON.stringify(this.selectedNews)).then(() => { this.$Message.success('组件复制成功'); }); } } }; </script>
<style lang="scss"> .com-from { .news { width: 280px;
.ivu-card { margin-bottom: 20px; } } } </style>
|
组件粘贴
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| <template> <div class="com-to"> <div class="news" v-shortkey="['ctrl', 'v']" @shortkey="pasteNews"> <Card v-for="news in newses" :key="news.title" dis-hover> <p slot="title">{{ news.title }}</p> <p>{{ news.content }}</p> </Card> </div> </div> </template>
<script> import * as clipboard from 'clipboard-polyfill/text';
export default { data() { return { newses: [], }; }, methods: { pasteNews() { clipboard.readText().then(newsJson => { const news = JSON.parse(newsJson);
if (this.validateNews(news)) { this.newses.push(news); } else { throw new Error('无效的新闻对象'); } }).catch((err) => { this.$Message.error(`粘贴组件错误: ${err.message}`); console.log(err); }); }, validateNews(news) { return this.hasProperty(news, 'title') && this.hasProperty(news, 'content'); }, hasProperty(obj, property) { return Object.prototype.hasOwnProperty.call(obj, property); } } }; </script>
<style lang="scss"> .com-to { .news { width: 280px;
.ivu-card { margin-bottom: 20px; } } } </style>
|