copy-to-clipboard
npm地址:https://www.npmjs.com/package/copy-to-clipboard
GitHub地址:https://github.com/sudodoki/copy-to-clipboard
该库使用的复制方法实现已经过时。window.clipboardData
已经不支持;document.execCommand("copy")
已经过时,已废除,只是还没删除。
使用
copy(text,options)
text
,需要复制的字符串内容options
onCopy
(clipboardData),可选择的接收剪贴板数据元素,用于添加自定义行为,如其他格式message
,复制出错自定义错误信息
源码部分
"use strict";
var deselectCurrent = require("toggle-selection");
var clipboardToIE11Formatting = {
"text/plain": "Text",
"text/html": "Url",
"default": "Text"
}
var defaultMessage = "Copy to clipboard: #{key}, Enter";
function format(message) {
var copyKey = (/mac os x/i.test(navigator.userAgent) ? "⌘" : "Ctrl") + "+C";
return message.replace(/#{\s*key\s*}/g, copyKey);
}
// 参数text, 要copy的内容字符串, 必填
// 参数options, 一些选项信息,非必填
function copy(text, options) {
// 一次性声明多个变量
var debug,
message,
reselectPrevious,
range,
selection,
mark,
success = false;
if (!options) {
options = {};
}
debug = options.debug || false;
try {
// 赋值,deselectCurrent方法主要取消选择当前浏览器选择并返回恢复选择的函数。
reselectPrevious = deselectCurrent();
//返回一个range对象,Range 接口表示一个包含节点与文本节点的一部分的文档片段。
range = document.createRange();
//返回一个selection对象,表示用户选择的文本范围或光标的当前位置。
selection = document.getSelection();
//创建一个span元素
mark = document.createElement("span");
//Node 接口的 textContent 属性表示一个节点及其后代的文本内容
mark.textContent = text;
// avoid screen readers from reading out loud the text,把 aria-hidden="true" 加到元素上会把该元素和它的所有子元素从无障碍树上移除
mark.ariaHidden = "true"
// reset user styles for span element,CSS all 简写属性 将除了 unicode-bidi 与 direction 之外的所有属性重设至其初始值,或继承值。unset,该关键字代表如果该元素的属性的值是可继承的,则改变该元素或该元素的父元素的所有属性的值为他们父元素的属性值,反之则改变为初始值。
mark.style.all = "unset";
// prevents scrolling to the end of the page
mark.style.position = "fixed";
mark.style.top = 0;
//clip屬性废弃。 clip 属性定义了元素的哪一部分是可见的。clip 属性只适用于 position:absolute 的元素。
mark.style.clip = "rect(0, 0, 0, 0)";
//whiteSpace处理空白元素。 pre连续的空白符会被保留。仅在遇到换行符或 <br> 元素时才会换行。
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";
// user-select CSS 属性用于控制用户是否可以选择文本。这不会对作为浏览器用户界面(即 chrome)的一部分的内容加载产生任何影响,除非是在文本框中。
mark.style.userSelect = "text";
// 触发监听copy事件时执行
mark.addEventListener("copy", function (e) {
e.stopPropagation();
if (options.,) {
e.preventDefault();
// 如果事件的event对象没有clipboardData对象,则使用 window.clipboardData
if (typeof e.clipboardData === "undefined") { // IE 11
debug && console.warn("unable to use e.clipboardData");
debug && console.warn("trying IE specific stuff");
// window.clipboardData为undef,如今已没有此API;删除所有类型的拖拽操作的数据
window.clipboardData.clearData();
var format = clipboardToIE11Formatting[options.format] || clipboardToIE11Formatting["default"]
// format 数据的格式,是string类型
window.clipboardData.setData(format, text);
} else { // all other browsers
// 当用户通过浏览器的用户界面发起复制动作时,将触发 copy 事件。该事件的默认行为是将当前选中的内容(如有)复制到系统剪贴板。事件处理器可以通过调用 ClipboardEvent.clipboardData 属性上的 setData(format, data) 修改剪贴板内容,并通过 event.preventDefault() 取消默认行为。
e.clipboardData.clearData();
e.clipboardData.setData(options.format, text);
}
}
if (options.onCopy) {
// 阻止默认行为
e.preventDefault();
options.onCopy(e.clipboardData);
}
});
// 往body中插入一个子元素Mark
document.body.appendChild(mark);
// Range.selectNodeContents() 方法用于设置 Range,使其包含一个 Node 的内容。
// Range 的起始和结束节点的父节点即为引用节点。 startOffset 为 0, endOffset 则是引用节点包含的字符数或子节点个数。
range.selectNodeContents(mark);
// 向选区(Selection)中添加一个区域(Range)。
selection.addRange(range);
// 利用系统命令实现
var successful = document.execCommand("copy");
if (!successful) {
throw new Error("copy command was unsuccessful");
}
success = true;
} catch (err) {
debug && console.error("unable to copy using execCommand: ", err);
debug && console.warn("trying IE specific stuff");
try {
// window.clipboardData为undefined,现在已经不支持了
window.clipboardData.setData(options.format || "text", text);
// 自定义格式化返回剪切板的数据
options.onCopy && options.onCopy(window.clipboardData);
success = true;
} catch (err) {
debug && console.error("unable to copy using clipboardData: ", err);
debug && console.error("falling back to prompt");
message = format("message" in options ? options.message : defaultMessage);
window.prompt(message, text);
}
} finally {
if (selection) {
if (typeof selection.removeRange == "function") {
// 从选区中移除一个区域。
selection.removeRange(range);
} else {
// 将所有的区域都从选区中移除。
selection.removeAllRanges();
}
}
if (mark) {
// 移除子节点mark
document.body.removeChild(mark);
}
// 取消选择当前浏览器选择并返回恢复选择的函数。
reselectPrevious();
}
return success;
}
module.exports = copy;
Window.getSelection()与 document.getSelection()
详细地址:https://developer.mozilla.org/zh-CN/docs/Web/API/Selection
返回一个 Selection 对象,表示用户选择的文本范围或光标的当前位置。
Selection
对象表示用户选择的文本范围或插入符号的当前位置。它代表页面中的文本选区,可能横跨多个元素。文本选区由用户拖拽鼠标经过文字而产生。要获取用于检查或修改的 Selection 对象,请调用window.getSelection()
。
一般来说,插入光标的位置可通过 Selection 获取,这时它被标记为 Collapsed,这表示选区被压缩至一点,即光标位置。
但要注意它与focus
事件或Document.activeElement
等的值没有必然联系。
selection
是一个 Selection
对象。如果想要将 selection 转换为字符串,可通过连接一个空字符串(“”)或使用 String.toString()
方法。
const selection = window.getSelection() ;
selection对象属性
anchorNode: null
;返回该选区起点所在的节点(Node)。anchorOffset: 0
;返回一个数字,其表示的是选区起点在 anchorNode 中的位置偏移量。- 如果 anchorNode 是文本节点,那么返回的就是从该文字节点的第一个字开始,直到被选中的第一个字之间的字数(如果第一个字就被选中,那么偏移量为零)。
- 如果 anchorNode 是一个元素,那么返回的就是在选区第一个节点之前的同级节点总数。(这些节点都是 anchorNode 的子节点)
baseNode: null
;baseOffset: 0
;extentNode: null
;extentOffset: 0
;focusNode: null
;返回该选区终点所在的节点。focusOffset: 0
;返回一个数字,其表示的是选区终点在 focusNode 中的位置偏移量。- 如果 focusNode 是文本节点,那么选区末尾未被选中的第一个字,在该文字节点中是第几个字(从 0 开始计),就返回它。
- 如果 focusNode 是一个元素,那么返回的就是在选区末尾之后第一个节点之前的同级节点总数。
isCollapsed: true
;返回一个布尔值,用于判断选区的起始点和终点是否在同一个位置。rangeCount: 0
;返回该选区所包含的连续范围的数量.Selection.rangeCount
是一个返回选区(selection)中范围(range)数量的只读属性。- 在网页使用者点击一个加载完毕的新打开的页面之前,rangeCount 的值是 0。
- 在使用者点击页面之后,rangeCount 的值变为 1,即使并没有可视的选区。
- 通常情况下, rangeCount 为0或者1,因为浏览器一般不允许用户选择多个 range 。当用户没有选择任何区域,则 rangeCount 值为
type: "None"
;是 Selection 接口的只读属性,其返回的是DOMString即描述当前选择的类型。DOMString 描述的是当前选择的类型。可能的值为:None
: 当前没有选择。Caret:
选区已折叠(即 光标在字符之间,并未处于选中状态)。Range
: 选择的是一个范围。
anchorOffset
和baseOffset
为选取文本起点所在节点的位置(从0开始,从左向右数),extentOffset
和focusOffset
为选取文本结束点所在节点的位置(从0开始,从左向右数)
用户可能从左到右(与文档方向相同)选择文本或从右到左(与文档方向相反)选择文本。
anchor
指向用户开始选择的地方,而focus
指向用户结束选择的地方。如果你使用鼠标选择文本的话,anchor
就指向你按下鼠标键的地方,而focus
就指向你松开鼠标键的地方。
anchor
和focus
的概念不能与选区的起始位置和终止位置混淆,因为 anchor 指向的位置可能在 focus 指向的位置的前面,也可能在 focus 指向位置的后面,这取决于你选择文本时鼠标移动的方向(也就是按下鼠标键和松开鼠标键的位置)。
判断当前是否有选取的内容
anchorNode
为nulltoString
返回为空anchorOffset
和focusOffset
相等isCollapsed
为truetype
等于Caret
或None
(当有选取内容时,type
为Range
)rangeCount
为0
Docuemnt.createRange()
详细地址:https://developer.mozilla.org/zh-CN/docs/Web/API/Range
Docuemnt.createRange()
方法返回一个新的Range
对象。一旦建立了 Range
对象,在使用它的大多数方法之前需要设置它的边界点。
Range
接口表示一个包含节点与文本节点的一部分的文档片段。
- 可以使用
Document.createRange
方法创建Range
。 - 也可以用
Selection
对象的getRangeAt()
方法或者Document
对象的caretRangeFromPoint()
方法获取Range
对象。 - 还可以用
Range()
构造函数。
range属性
collapsed: true
;返回一个表示 Range 的起始位置和终止位置是否相同的布尔值commonAncestorContainer: document
;返回完整包含startContainer
和endContainer
的、最深一级的节点。endContainer: document
;返回包含Range
终点的节点。endOffset: 0
;返回一个表示 Range 终点在endContainer
中的位置的数字。startContainer: document
;返回包含Range
开始的节点。startOffset: 0
;返回一个数字,表示Range
在startContainer
中的起始位置。
ClipboardEvent.clipboardData
ClipboardEvent.clipboardData
属性保存了一个 DataTransfer
对象,这个对象可用于:
描述哪些数据可以由 cut 和 copy 事件处理器放入剪切板,通常通过调用 setData(format, data)
方法;
获取由 paste 事件处理器拷贝进剪切板的数据,通常通过调用 getData(format) 方法
document.execCommand(“copy”)
执行copy命令,将文本放置剪切板
不过该命令已经过时,被废弃,但未删除。
Selection.removeAllRanges()
Selection.removeAllRanges()
方法会从当前 selection
对象中移除所有的 range
对象,取消所有的选择只 留下anchorNode
和focusNode
属性并将其设置为 null
。
Range.selectNodeContents()
Range.selectNodeContents() 方
法用于设置 Range,使其包含一个 Node 的内容。
Range 的起始和结束节点的父节点即为引用节点。 startOffset 为 0, endOffset 则是引用节点包含的字符数或子节点个数。
range.selectNodeContents(referenceNode);
referenceNode,此 Node 中的内容被包含在 Range 中。
document.activeElement
Document 和 ShadowRoot 接口的 activeElement 只读属性,用来返回当前在 DOM 或者 shadow DOM 树中处于聚焦状态的Element。
通常情况下,如果 HTMLInputElement 或者 HTMLTextAreaElement元素中有文字被选中时, activeElement属性就会返回该元素。这时,你可以调用该元素的selectionStart 和 selectionEnd 属性获取更多选中文字的信息。其他情况下,焦点元素也可能是元素 (menu) 或者一个别的<input>
元素,比如 "button"
、"checkbox"
或者 "radio"
。
element = DocumentOrShadowRoot.activeElement
当前获得焦点的 Element ,如果没有焦点元素,会返回<body>
或者 null
。
toggle-selection
npm地址:https://www.npmjs.com/package/toggle-selection
一个简单的模块,用于显示取消选择当前浏览器选择并返回恢复选择的函数。
module.exports = function () {
// 返回一个 Selection 对象,表示用户选择的文本范围或光标的当前位置。
var selection = document.getSelection();
// Selection.rangeCount 是一个返回选区(selection)中范围(range)数量的只读属性。通常情况下, rangeCount 为0或者1,因为浏览器一般不允许用户选择多个 range 。当用户没有选择任何区域,则 rangeCount 值为
if (!selection.rangeCount) { //新打开的页面rangeCount为0,返回一个空函数
return function () {};
}
// 当前获得焦点的 Element ,如果没有焦点元素,会返回 <body> 或者 null 。
var active = document.activeElement;
// 存储区域rang对象的一个数组
var ranges = [];
// selection.rangeCount一般情况下值为0或1
for (var i = 0; i < selection.rangeCount; i++) {
// Range 接口表示一个包含节点与文本节点的一部分的文档片段。
ranges.push(selection.getRangeAt(i));
}
switch (active.tagName.toUpperCase()) { // .toUpperCase handles XHTML
case 'INPUT':
case 'TEXTAREA':
active.blur(); //失去焦点
break;
default:
active = null;
break;
}
// Selection.removeAllRanges() 方法会从当前 selection 对象中移除所有的 range 对象,取消所有的选择只 留下anchorNode 和focusNode属性并将其设置为 null。
selection.removeAllRanges();
//
return function () {
// selection.type 返回的是DOMString即描述当前选择的类型。
/**
* DOMString 描述的是当前选择的类型。可能的值为:
None: 当前没有选择。
Caret: 选区已折叠(即 光标在字符之间,并未处于选中状态)。
Range: 选择的是一个范围。
*/
selection.type === 'Caret' &&
selection.removeAllRanges(); // 将所有的区域都从选区中移除。
if (!selection.rangeCount) { //当页面没被点击过 rangCount为0
ranges.forEach(function (range) {
// 向选区(Selection)中添加一个区域(Range)。
selection.addRange(range);
});
}
active &&
active.focus(); //获得焦点
};
};
navigator.clipboard实现复制
剪贴板 Clipboard API 为 Navigator 接口添加了只读属性 clipboard,该属性返回一个可以读写剪切板内容的 Clipboard 对象。在 Web 应用中,剪切板 API 可用于实现剪切、复制、粘贴的功能。
只有在用户事先授予网站或应用对剪切板的访问许可之后,才能使用异步剪切板读写方法。许可操作必须通过取得权限 Permissions API (en-US) 的 “clipboard-read” 和/或 “clipboard-write” 项获得。
支持环境:https、localhost
不支持环境:直接IP访问的网页、http(navigator.clipboard直接返回undefined)
实现代码
<body>
<div id="content">
<button id="btnCopy">copy</button>
</div>
</body>
<script>
let dom = document.getElementById('btnCopy')
console.log('dom', dom);
dom.onclick = () => {
navigator.clipboard.writeText('hhyyyyy').then(() => {
console.log('copy success');
})
}
</script>