JS复制文本基本分为两步-First: 选中需要复制的节点,及选区;Second: 执行document.execCommand('copy')命令复制

对于选区,属于HTMLInputElement<textarea><input>元素支持element.select()方法

<div id="test1">
<input type="text" placeholder="你能复制我的人,但不能复制我的心" /><button>
点击复制文字
</button>
</div>
// 复制输入框文字
const target1 = document.querySelector('#test1>input')
const btn1 = document.querySelector('#test1>button')
btn1.addEventListener(
'click',
() => {
target1.select() //选中输入框中文字,这个方法执行后没有返回值
document.execCommand('copy') //复制成功则返回值为true
target1.blur() // 解除文字选中状态
},
false
)


其他元素不支持select()方法,但是浏览器也提供了相关选区的API,主要借助document.createRange()document.getSelection()这两个方法同时也存在于window对象上

<div id="test2">
<span>你能复制我的人,但不能带走我的心</span>
<button>点击复制文字</button>
</div>
// 复制任意节点文字
const target2 = document.querySelector('#test2>span')
const btn2 = document.querySelector('#test2>button')
btn2.addEventListener(
'click',
() => {
const range = document.createRange()
const selection = document.getSelection()
range.selectNodeContents(target2)
selection.removeAllRanges()
selection.addRange(range)
document.execCommand('copy')
// 清除选中状态
selection.removeAllRanges()
},
false
)

如果我们想自定义复制的内容,该怎么办呢?我们可以临时创建一个节点,把需要复制的内容作为节点内容,然后使用上面提到的方法进行复制,如下面的两种方法都是可行的。

<div id="test3">
<button>点击复制变量</button>
</div>
// 复制自定义文字 方法一
const target3 = '你能复制我的人,但不能带走我的心eee'
const btn3 = document.querySelector('#test3>button')
btn3.addEventListener(
'click',
() => {
const range = document.createRange()
const selection = document.getSelection()
const fake = document.createElement('span')
// 重置样式
fake.style.all = 'unset'
fake.textContent = target3
// fixed定位防止选中节点时页面滚动到页面底部
fake.style.position = 'fixed'
fake.style.top = 0
// 让这个临时元素不可见,注意不能使用display: none和visibility: hidden,会导致元素选不中
fake.style.clip = 'rect(0, 0, 0, 0)'
// 保存空格和换行
fake.style.whiteSpace = 'pre'
document.body.appendChild(fake)
range.selectNodeContents(fake)
// 清除已有选区
selection.removeAllRanges()
selection.addRange(range)
document.execCommand('copy')
document.body.removeChild(fake)
},
false
)

上述代码中的注释部分都是非常重要的,网上很多教程的实现方式都有缺陷,本文实现的方法是从copy-to-clipboard抄袭的,基本考虑到了很多异常情况.不过我个人觉得使用textarea元素作为中转节点是更好的选择,起兼容性更好,结果可控。

<div id="test4">
<button>点击复制变量</button>
</div>
// 复制自定义文字 方法二
const target4 = '你能复制我的人,但不能带走我的心'
const btn4 = document.querySelector('#test4>button')
btn4.addEventListener(
'click',
() => {
const fake = document.createElement('textarea')
// 防止弹出键盘
fake.setAttribute('readonly', 'readonly')
fake.value = target4
fake.style.position = 'fixed'
fake.style.top = 0
fake.style.clip = 'rect(0, 0, 0, 0)'
document.body.appendChild(fake)
fake.select()
document.execCommand('copy')
document.body.removeChild(fake)
},
false
)

为啥不使用input元素呢,因为textarea能保持换行等.

上述方法在React组件中也是可以使用的,不过在使用过程中很多人发现一个问题document.execCommand ('copy') don't work in React,这个问题一开始我也遇到了,有人回答是因为document.execCommand ('copy')不能在React的合成事件中调用,只能直接在原生事件中调用,其实这是不对的,react-copy-to-clipboard这个组件不也是在合成事件中调用这个命令吗?经过不断尝试,我发现问题出现在一个小的细节上,我们一开始通过const selection = document.getSelection()获取选区操作对象,默认我们添加选区前,选区信息应该为空,可以使用下面的语句打印信息

   console.log('选区个数', selection.rangeCount, '选区对象', selection)


需要注意的是selection对象是不能深克隆的,在控制台上如果展开对象打印结果显示的结果是selection最终的结果(如果后面修改过selection的话)而不是打印语句时的结果

这个结果在上面几个原生js的例子中都适用.但是在React的事件回调中,通过ocument.getSelection()获取的默认选区却不为空,如果把我们创建的节点直接塞进去,再执行document.getSelection()是不会复制成功的.

解决方法很简单,在添加选区前清除选区,不过我发现如果使用textarea作为中转节点则不存在这个问题。

if (selection.rangeCount > 0) {
selection.removeAllRanges()
}
import React, { Component } from 'react'
class App extends Component {
render() {
return (
<div className="App">
<h1
onClick={e => {
const range = document.createRange()
const selection = document.getSelection()
const mark = document.createElement('span')
mark.textContent = 'text to copy'
// reset user styles for span element
mark.style.all = 'unset'
// prevents scrolling to the end of the page
mark.style.position = 'fixed'
mark.style.top = 0
mark.style.clip = 'rect(0, 0, 0, 0)'
// used to preserve spaces and line breaks
mark.style.whiteSpace = 'pre'
// do not inherit user-select (it may be `none`)
mark.style.webkitUserSelect = 'text'
mark.style.MozUserSelect = 'text'
mark.style.msUserSelect = 'text'
mark.style.userSelect = 'text'
mark.addEventListener('copy', function(e) {
e.stopPropagation()
}) document.body.appendChild(mark) // The following line is very important
if (selection.rangeCount > 0) {
selection.removeAllRanges()
} range.selectNodeContents(mark)
selection.addRange(range)
document.execCommand('copy')
document.body.removeChild(mark)
}}
>
Click to Copy Text
</h1>
</div>
)
}
} export default App
import React, { Component } from 'react'
class App extends Component {
render() {
return (
<div className="App">
<h1
onClick={e => {
const mark = document.createElement('textarea')
//防止移动端键盘弹出
mark.setAttribute('readonly', 'readonly')
mark.value = 'copy me'
mark.style.position = 'fixed'
mark.style.top = 0
mark.style.clip = 'rect(0, 0, 0, 0)'
document.body.appendChild(mark)
mark.select()
document.execCommand('copy')
document.body.removeChild(mark) }}
>
Click to Copy Text
</h1>
</div>
)
}
} export default App

最后再补充一点,使用上面的方法复制图片是否可行呢,

    <div id="test">
<img
src="https://www.zxing.top/media/15546249493670/15547312147294.jpg"
alt=""
/>
<button>点击复制文x字</button>
</div>
const target = document.querySelector('#test>img')
const btn = document.querySelector('#test>button')
btn.addEventListener(
'click',
() => {
const range = document.createRange()
const selection = document.getSelection()
selection.removeAllRanges()
// range.selectNodeContents(target)
range.selectNode(target)
selection.addRange(range)
document.execCommand('copy')
selection.removeAllRanges()
},
false
)

注意上面的代码修改了一处,range.selectNode(target),上面的代码我测试了一下,可以复制图片,但是复制的是某种html对象,走一些编辑器上是可以粘贴的,但不保障兼容性,我测试了Mac上的Page应用,印象笔记和手机上的wps是可以直接粘贴的。

JavaScript复制文本探究的更多相关文章

  1. 用javascript复制富文本

    由于项目需求,希望能够用javascript复制富文本格式的数据,例如全选一个网页Ctrl+C, Ctrl+V到一个word文档中,数据还是原来的格式,显示出来的样子也都和原来一样.现在希望使用jav ...

  2. HTML5 完美解决javascript中iphone手机和android手机复制文本到剪切板问题

    1.执行以下解决方案条件:(这个是原理) ①执行复制方法时 所复制文字不能被任何 块级元素和行内块元素和行内元素遮盖否则无效:(解决方案:将文本通过绝对定位或其他方式移除屏幕外) ②ios中不能复制属 ...

  3. ZeroClipboard – 轻松实现复制文本到剪贴板功能

    ZeroClipboard 库提供了一种把文本复制到剪贴板的简单方法.Zero 表示该库是不可见的,用户界面则完全取决于你. 该库完全兼容 Flash Player 10.0.0 或以上版本,这就要求 ...

  4. Clipboard.js – 现代方式实现复制文本到剪贴板

    复制文本到剪贴板应该并不难,目前已经有很成熟的 Flash 方法.但是 Flash 已经在很多场合不适用了,特别是随着 HTML5 技术的发展.今天推荐的这个 Clipboard.js 库不依赖 Fl ...

  5. javascript复制

    1.实现点击按钮,复制文本框中的的内容 1 <scrip type="text/javascript"> 2 function copyUrl2() 3 { 4 var ...

  6. JavaScript显示文本框后自动获取焦点

    废话少说,见官方文档: 他的用法是:document.getElementById('username').focus();                   这样写在display:block;显 ...

  7. js 实现点击复制文本内容

    js  实现点击复制文本内容 <table> <tr><td>姓名:<span onclick="copyContent(this);" ...

  8. javascript 在线文本编辑器

    javascript 在线文本编辑器实现代码. 效果例如以下: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcGhwZmVuZ2h1bw==/font/5 ...

  9. js兼容安卓和IOS的复制文本到剪切板

    1.在做点击按钮复制功能时遇到了小小的卡顿,此处遇到了两种系统手机的兼容性 / 复制后会对文本进行选中 / 输入法弹出 等.现将方法进行总结,如下代码很好对解决了以上问题,适用性强. 2.在文本此处使 ...

随机推荐

  1. Java 8 特性 —— 函数式接口

    函数式接口 概述:接口中只有一个抽象方法. 函数式接口,即适用于函数式编程场景的接口.而 Java 中的函数式编程体现就是 Lambda,所以函数式接口就是可以适用于 Lambda 使用的接口.只有确 ...

  2. Day055--MySQL--外键的变种,表与表的关系,单表查询,多表查询, 内连接,左右连接,全外连接

    表和表的关系 ---- 外键的变种 * 一对多或多对一 多对多 一对一 参考 https://www.cnblogs.com/majj/p/9169416.html 如何找出两张表之间的关系 分析步骤 ...

  3. CentOS安装glibc-2.14

    CentOS安装glibc-2.14   到http://ftp.gnu.org/gnu/glibc/下载glibc-2.14.tar.gz wget https://ftp.gnu.org/gnu/ ...

  4. python中__init__和__new__的区别

    参考:https://my.oschina.net/liuyuantao/blog/747164 python中__metaclass的详解 参考:https://www.cnblogs.com/ia ...

  5. jemter测试中常见的名词解析

    1.响应时间(RT) 响应时间是指系统对请求做出响应的时间 2.吞吐量(TPS)[Throughput] 吞吐量是指系统在单位时间内处理请求的数量 3.并发用户数 并发用户数是指系统可以同时承载的正常 ...

  6. 1、IDEA的常用快捷键

    一.常用的快捷键1.Ctrl+/ 或 Ctrl+Shift+/ 注释(// 或者/*...*/ )###2.Ctrl+D 复制行### 注意在MyEclipse中Ctrl+D的作用是删除行3.Ctrl ...

  7. 深入理解line-height与vertical-align——前端布局常用属性

    line-height.font-size.vertical-align是设置行内元素布局的关键属性.这三个属性是相互依赖的关系,改变行间距离.设置垂直对齐等都需要它们的通力合作.下面将主要介绍lin ...

  8. lambda+mutable配合move实现单函数多程序域

    主代码 //-----------------------------------说明一的代码 void fun0{ int t = 10; auto loopFun = [=]() mutable{ ...

  9. THUWC2019 GG记

    所以要什么时候才不会出现像这样的滚粗记呢? Day-?~Day-4 我也不清楚具体干了什么 不过学了很多东西,至少相对于去年还是个小菜鸡,今年已经变成大菜鸡了 Day-3~Day-1 几乎就是复习前面 ...

  10. awk和sed截取nginx和tomcat时间段日志

    1 nginx日志截取示例 日志路径:/usr/local/nginx/logs, 截取access.log中2019年3月24日17点00~02之间的日志: 写法1: cat access.log ...