Skip to content

Instantly share code, notes, and snippets.

@gangsthub
Last active July 16, 2020 16:40
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save gangsthub/a4d873e5a450ca4681364e230498710b to your computer and use it in GitHub Desktop.
`copytoclipboard`: Alternative to clipboard.js: Async implementation of "Copy to clipboard" in plain JS. Works in Safari, Chrome, Firefox, Opera and Edge Canary. Less than 1kb minified.

copyToClipBoard.js

copytoclipboard: Alternative to clipboard.js: Async implementation of "Copy to clipboard" in plain JS. Works in Safari, Chrome, Firefox, Opera and Edge Canary. Less than 1kb minified.

copyToClipBoard accepts an html element and returns a Promise.

Copies the content of the html element with the format included and also as plain text (both options).

📦 Install

npm i -S gist:a4d873e5a450ca4681364e230498710b

Usage

import { copyToClipBoard } from 'copytoclipboard'

copyToClipBoard(element)
  .then(() => console.log('✅ it worked ! ("Ctrl/Cmd + V" to paste it)'))
  .catch(e => console.log('❌ something went wrong', e))
// see https://stackoverflow.com/a/46858939/6799546
// see https://gist.github.com/rproenca/64781c6a1329b48a455b645d361a9aa3
// see https://stackoverflow.com/questions/52274735/cannot-save-formatted-data-text-html-in-the-clipboard-in-safari
/**
* @param {HTMLElement} element
* @returns {Promise<void>}
*/
export const copyToClipBoard = element => {
if (!element) return Promise.reject(new Error('No element passed'))
if (typeof window === 'undefined')
return Promise.reject(new Error('Not in a browser environment'))
if (!(element instanceof HTMLElement)) {
throw new Error('this is not an HTMLElement')
}
const isCopySupported = () => {
return document.queryCommandSupported('copy')
}
if (!isCopySupported) {
return Promise.reject(new Error('execCommand not supported'))
}
// By spec it has to be the selected text
const selectElementContents = el => {
const range = document.createRange()
range.selectNodeContents(el)
const sel = window.getSelection()
sel && sel.removeAllRanges()
sel && sel.addRange(range)
}
const removeWindowSelections = () => {
const selection = window.getSelection()
selection && selection.removeAllRanges()
document.body.focus()
}
// execCommand is only available in designMode
// https://developer.mozilla.org/en-US/docs/Web/API/Document/designMode
const oldContentEditable = element.contentEditable
// @ts-ignore
const oldReadonly = element.readOnly
const makeElementEditable = () => {
element.contentEditable = 'true'
// @ts-ignore
element.readOnly = 'false'
}
const turnElementBackToOldEditMode = () => {
element.contentEditable = oldContentEditable
// @ts-ignore
element.readOnly = oldReadonly
}
const copy = () => {
/**
* @param {ClipboardEvent} event
*/
function listener(event) {
event.clipboardData &&
event.clipboardData.setData(
'text/plain',
element.innerText || /** @type {HTMLTextAreaElement} */ (element).value
)
event.clipboardData &&
event.clipboardData.setData('text/html', element.innerHTML)
// https://w3c.github.io/clipboard-apis/#override-copy
event.preventDefault()
}
document.addEventListener('copy', listener)
document.execCommand('copy')
document.removeEventListener('copy', listener)
}
/**
* @type {Promise<void>}
*/
const promise = new Promise(() => {
makeElementEditable()
selectElementContents(element)
copy()
turnElementBackToOldEditMode()
removeWindowSelections()
})
return promise
}
{
"name": "copytoclipboard",
"version": "1.2.1",
"main": "copyToClipBoard.js"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment