├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ └── codeql.yml ├── .gitignore ├── .travis.yml ├── .vscode └── settings.json ├── LICENSE ├── README.md ├── bower.json ├── dist ├── copee.d.ts ├── copee.js ├── copee.mjs └── copee.umd.js ├── docs ├── CNAME ├── iife.js ├── index.html ├── mod.mjs └── script.js ├── package-lock.json ├── package.json ├── replace.sh ├── src └── copee.ts └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "main" ] 6 | pull_request: 7 | branches: [ "main" ] 8 | schedule: 9 | - cron: "36 19 * * 6" 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | language: [ javascript ] 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v3 28 | 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v2 31 | with: 32 | languages: ${{ matrix.language }} 33 | queries: +security-and-quality 34 | 35 | - name: Autobuild 36 | uses: github/codeql-action/autobuild@v2 37 | 38 | - name: Perform CodeQL Analysis 39 | uses: github/codeql-action/analyze@v2 40 | with: 41 | category: "/language:${{ matrix.language }}" 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # node.js 2 | node_modules 3 | debug.log 4 | 5 | # Windows image file caches 6 | Thumbs.db 7 | ehthumbs.db 8 | 9 | # Folder config file 10 | Desktop.ini 11 | 12 | # Recycle Bin used on file shares 13 | $RECYCLE.BIN/ 14 | 15 | # Windows Installer files 16 | *.cab 17 | *.msi 18 | *.msm 19 | *.msp 20 | 21 | # Windows shortcuts 22 | *.lnk 23 | 24 | # ========================= 25 | # Operating System Files 26 | # ========================= 27 | 28 | # OSX 29 | # ========================= 30 | 31 | .DS_Store 32 | .AppleDouble 33 | .LSOverride 34 | 35 | # Thumbnails 36 | ._* 37 | 38 | # Files that might appear on external disk 39 | .Spotlight-V100 40 | .Trashes 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | branches: 2 | only: 3 | - "main" 4 | 5 | git: 6 | depth: 5 7 | 8 | language: node_js 9 | 10 | node_js: 11 | - "12" 12 | 13 | script: 14 | - npm run build 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "./node_modules/typescript/lib", 3 | "files.exclude": { 4 | "**/.git": true, 5 | "**/.svn": true, 6 | "**/.hg": true, 7 | "**/CVS": true, 8 | "**/.DS_Store": true, 9 | "node_modules": true 10 | } 11 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Steven 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # copee 2 | 3 | [](https://www.npmjs.com/package/copee) 4 | [](https://cdn.jsdelivr.net/npm/copee/dist/copee.umd.js) 5 | [](https://packagephobia.now.sh/result?p=copee) 6 | [](https://www.npmjs.com/package/copee) 7 | [](https://david-dm.org/styfle/copee) 8 | [](https://david-dm.org/styfle/copee?type=dev) 9 | [](https://travis-ci.org/styfle/copee) 10 | 11 | Copy text from browser to clipboard...natively! Less than 1 kB! 12 | 13 | Have you ever wanted to send a string to the user's clipboard? Front-end developers have invented many hacks to get around this limitation...until now! 14 | 15 | Introducing `copee`, a micro-wrapper around the browser's native "copy text" API 🗜️ 16 | 17 | Death to Adobe Flash 💀 18 | 19 | ## Demo 20 | 21 | [Try the demo](https://copee.ceriously.com/) to see `copee` in action! 22 | 23 | You can view the page source to quickly learn how to use it. 24 | 25 | Also, read [this blog post](https://styfle.dev/blog/es6-modules-today-with-typescript) for some background. 26 | 27 | ## Browser ESM Usage 28 | 29 | ```html 30 | 40 | ``` 41 | 42 | ## Browser UMD Usage 43 | 44 | ```html 45 | 46 | 54 | ``` 55 | 56 | ## Browser Suppport 57 | 58 | - UMD (`.js`) supports IE 11+, Chrome 43+, Opera 29+, and Firefox 41+ 59 | - ESM ([.mjs](https://caniuse.com/#feat=es6-module)) supports Chrome 61+, Safari 10.1+, Edge 16+, and Firefox 60+ 60 | 61 | ## Prior Art 62 | 63 | This package was influenced by the following: 64 | 65 | - [Google Dev Blog](https://developers.google.com/web/updates/2015/04/cut-and-copy-commands) 66 | - [Mozilla Hacks](https://hacks.mozilla.org/2015/09/flash-free-clipboard-for-the-web/) 67 | - [StackOverflow](https://stackoverflow.com/a/30810322/266535) 68 | 69 | ## Authors 70 | 71 | Developed by [styfle](https://styfle.dev) 72 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "copee", 3 | "version": "0.0.1", 4 | "description": "Copy text from browser to clipboard...natively!", 5 | "main": "copee.js", 6 | "keywords": [ 7 | "copy" 8 | ], 9 | "authors": [ 10 | "styfle" 11 | ], 12 | "license": "MIT", 13 | "homepage": "http://copee.ceriously.com/", 14 | "repository": { 15 | "type": "git", 16 | "url": "git://github.com/styfle/copee.git" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /dist/copee.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copy text to the user's clipboard 3 | */ 4 | export declare function toClipboard(text: string): boolean; 5 | /** 6 | * Copy text from an input/textarea element to a user's clipboard 7 | * @param el The or element 8 | * @param preserveSelection True if text selection/highlight should be preserved after copying text 9 | */ 10 | export declare function fromElement(el: HTMLInputElement | HTMLTextAreaElement, preserveSelection?: boolean): boolean; 11 | -------------------------------------------------------------------------------- /dist/copee.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | /** 4 | * Copy text to the user's clipboard 5 | */ 6 | function toClipboard(text) { 7 | // Create element and select the text 8 | const ta = document.createElement('textarea'); 9 | ta.value = text; 10 | ta.cols = 1; 11 | ta.rows = 1; 12 | ta.style.color = 'transparent'; 13 | ta.style.border = 'none'; 14 | document.body.appendChild(ta); 15 | ta.select(); 16 | let success = false; 17 | try { 18 | success = document.execCommand('copy'); 19 | } 20 | catch (err) { 21 | success = false; 22 | } 23 | // Cleanup the element we created 24 | document.body.removeChild(ta); 25 | return success; 26 | } 27 | exports.toClipboard = toClipboard; 28 | /** 29 | * Copy text from an input/textarea element to a user's clipboard 30 | * @param el The or element 31 | * @param preserveSelection True if text selection/highlight should be preserved after copying text 32 | */ 33 | function fromElement(el, preserveSelection) { 34 | let start = 0; 35 | let end = 0; 36 | let success = false; 37 | if (preserveSelection) { 38 | start = el.selectionStart || 0; 39 | end = el.selectionEnd || 0; 40 | } 41 | el.select(); 42 | try { 43 | success = document.execCommand('copy'); 44 | } 45 | catch (err) { 46 | success = false; 47 | } 48 | if (preserveSelection) { 49 | el.setSelectionRange(start, end); 50 | } 51 | return success; 52 | } 53 | exports.fromElement = fromElement; 54 | -------------------------------------------------------------------------------- /dist/copee.mjs: -------------------------------------------------------------------------------- 1 | /** 2 | * Copy text to the user's clipboard 3 | */ 4 | export function toClipboard(text) { 5 | // Create element and select the text 6 | const ta = document.createElement('textarea'); 7 | ta.value = text; 8 | ta.cols = 1; 9 | ta.rows = 1; 10 | ta.style.color = 'transparent'; 11 | ta.style.border = 'none'; 12 | document.body.appendChild(ta); 13 | ta.select(); 14 | let success = false; 15 | try { 16 | success = document.execCommand('copy'); 17 | } 18 | catch (err) { 19 | success = false; 20 | } 21 | // Cleanup the element we created 22 | document.body.removeChild(ta); 23 | return success; 24 | } 25 | /** 26 | * Copy text from an input/textarea element to a user's clipboard 27 | * @param el The or element 28 | * @param preserveSelection True if text selection/highlight should be preserved after copying text 29 | */ 30 | export function fromElement(el, preserveSelection) { 31 | let start = 0; 32 | let end = 0; 33 | let success = false; 34 | if (preserveSelection) { 35 | start = el.selectionStart || 0; 36 | end = el.selectionEnd || 0; 37 | } 38 | el.select(); 39 | try { 40 | success = document.execCommand('copy'); 41 | } 42 | catch (err) { 43 | success = false; 44 | } 45 | if (preserveSelection) { 46 | el.setSelectionRange(start, end); 47 | } 48 | return success; 49 | } 50 | -------------------------------------------------------------------------------- /dist/copee.umd.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : 3 | typeof define === 'function' && define.amd ? define(['exports'], factory) : 4 | (global = global || self, factory(global.copee = {})); 5 | }(this, (function (exports) { 'use strict'; 6 | 7 | /** 8 | * Copy text to the user's clipboard 9 | */ 10 | function toClipboard(text) { 11 | // Create element and select the text 12 | const ta = document.createElement('textarea'); 13 | ta.value = text; 14 | ta.cols = 1; 15 | ta.rows = 1; 16 | ta.style.color = 'transparent'; 17 | ta.style.border = 'none'; 18 | document.body.appendChild(ta); 19 | ta.select(); 20 | let success = false; 21 | try { 22 | success = document.execCommand('copy'); 23 | } 24 | catch (err) { 25 | success = false; 26 | } 27 | // Cleanup the element we created 28 | document.body.removeChild(ta); 29 | return success; 30 | } 31 | /** 32 | * Copy text from an input/textarea element to a user's clipboard 33 | * @param el The or element 34 | * @param preserveSelection True if text selection/highlight should be preserved after copying text 35 | */ 36 | function fromElement(el, preserveSelection) { 37 | let start = 0; 38 | let end = 0; 39 | let success = false; 40 | if (preserveSelection) { 41 | start = el.selectionStart || 0; 42 | end = el.selectionEnd || 0; 43 | } 44 | el.select(); 45 | try { 46 | success = document.execCommand('copy'); 47 | } 48 | catch (err) { 49 | success = false; 50 | } 51 | if (preserveSelection) { 52 | el.setSelectionRange(start, end); 53 | } 54 | return success; 55 | } 56 | 57 | exports.fromElement = fromElement; 58 | exports.toClipboard = toClipboard; 59 | 60 | Object.defineProperty(exports, '__esModule', { value: true }); 61 | 62 | }))); 63 | -------------------------------------------------------------------------------- /docs/CNAME: -------------------------------------------------------------------------------- 1 | copee.ceriously.com -------------------------------------------------------------------------------- /docs/iife.js: -------------------------------------------------------------------------------- 1 | (function (copee) { 2 | 'use strict'; 3 | 4 | const q = (sel) => document.querySelector(sel); 5 | 6 | const showAlert = (success) => { 7 | const el = success ? q('.alert-success') : q('.alert-danger'); 8 | el.style.display = 'block'; 9 | setTimeout(() => el.style.display = 'none', 3000); 10 | }; 11 | 12 | q('#btn1').addEventListener('click', () => { 13 | const success = copee.toClipboard('Magic happens here'); 14 | showAlert(success); 15 | }); 16 | 17 | q('#btn2').addEventListener('click', () => { 18 | const el = q('#input2'); 19 | const success = copee.fromElement(el); 20 | showAlert(success); 21 | }); 22 | 23 | }(copee)); 24 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Copee Example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Copee Example 16 | Copy text from browser to clipboard...natively! 17 | 18 | 19 | btn1.addEventListener('click', function (e) { 20 | var success = toClipboard('Magic happens here'); 21 | showAlert(success); 22 | }); 23 | 24 | 25 | Example 1: Copy hard-coded text 26 | Copy To Clipboard 27 | 28 | 29 | 30 | 31 | btn2.addEventListener('click', function (e) { 32 | var success = fromElement(input); 33 | showAlert(success); 34 | }); 35 | 36 | 37 | 38 | Example 2: Copy from input element 39 | 40 | 41 | 42 | 43 | Copy! 44 | 45 | 46 | 47 | 48 | 49 | 50 | Check your clipboard :) 51 | Something went wrong :( 52 | 53 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /docs/mod.mjs: -------------------------------------------------------------------------------- 1 | import { toClipboard, fromElement } from 'https://cdn.jsdelivr.net/npm/copee/dist/copee.mjs'; 2 | 3 | const q = (sel) => document.querySelector(sel); 4 | 5 | const showAlert = (success) => { 6 | const el = success ? q('.alert-success') : q('.alert-danger'); 7 | el.style.display = 'block'; 8 | setTimeout(() => el.style.display = 'none', 3000); 9 | } 10 | 11 | q('#btn1').addEventListener('click', () => { 12 | const success = toClipboard('Magic happens here'); 13 | showAlert(success); 14 | }); 15 | 16 | q('#btn2').addEventListener('click', () => { 17 | const el = q('#input2'); 18 | const success = fromElement(el); 19 | showAlert(success); 20 | }); 21 | -------------------------------------------------------------------------------- /docs/script.js: -------------------------------------------------------------------------------- 1 | import { toClipboard, fromElement } from 'copee'; 2 | 3 | const q = (sel) => document.querySelector(sel); 4 | 5 | const showAlert = (success) => { 6 | const el = success ? q('.alert-success') : q('.alert-danger'); 7 | el.style.display = 'block'; 8 | setTimeout(() => el.style.display = 'none', 3000); 9 | } 10 | 11 | q('#btn1').addEventListener('click', () => { 12 | const success = toClipboard('Magic happens here'); 13 | showAlert(success); 14 | }); 15 | 16 | q('#btn2').addEventListener('click', () => { 17 | const el = q('#input2'); 18 | const success = fromElement(el); 19 | showAlert(success); 20 | }); -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "copee", 3 | "version": "1.0.6", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "version": "1.0.6", 9 | "license": "MIT", 10 | "devDependencies": { 11 | "rollup": "^4.2.0", 12 | "typescript": "^5.0.3" 13 | } 14 | }, 15 | "node_modules/@rollup/rollup-android-arm-eabi": { 16 | "version": "4.40.1", 17 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz", 18 | "integrity": "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==", 19 | "cpu": [ 20 | "arm" 21 | ], 22 | "dev": true, 23 | "license": "MIT", 24 | "optional": true, 25 | "os": [ 26 | "android" 27 | ] 28 | }, 29 | "node_modules/@rollup/rollup-android-arm64": { 30 | "version": "4.40.1", 31 | "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz", 32 | "integrity": "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==", 33 | "cpu": [ 34 | "arm64" 35 | ], 36 | "dev": true, 37 | "license": "MIT", 38 | "optional": true, 39 | "os": [ 40 | "android" 41 | ] 42 | }, 43 | "node_modules/@rollup/rollup-darwin-arm64": { 44 | "version": "4.40.1", 45 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz", 46 | "integrity": "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==", 47 | "cpu": [ 48 | "arm64" 49 | ], 50 | "dev": true, 51 | "license": "MIT", 52 | "optional": true, 53 | "os": [ 54 | "darwin" 55 | ] 56 | }, 57 | "node_modules/@rollup/rollup-darwin-x64": { 58 | "version": "4.40.1", 59 | "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz", 60 | "integrity": "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==", 61 | "cpu": [ 62 | "x64" 63 | ], 64 | "dev": true, 65 | "license": "MIT", 66 | "optional": true, 67 | "os": [ 68 | "darwin" 69 | ] 70 | }, 71 | "node_modules/@rollup/rollup-freebsd-arm64": { 72 | "version": "4.40.1", 73 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz", 74 | "integrity": "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==", 75 | "cpu": [ 76 | "arm64" 77 | ], 78 | "dev": true, 79 | "license": "MIT", 80 | "optional": true, 81 | "os": [ 82 | "freebsd" 83 | ] 84 | }, 85 | "node_modules/@rollup/rollup-freebsd-x64": { 86 | "version": "4.40.1", 87 | "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz", 88 | "integrity": "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==", 89 | "cpu": [ 90 | "x64" 91 | ], 92 | "dev": true, 93 | "license": "MIT", 94 | "optional": true, 95 | "os": [ 96 | "freebsd" 97 | ] 98 | }, 99 | "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 100 | "version": "4.40.1", 101 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz", 102 | "integrity": "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==", 103 | "cpu": [ 104 | "arm" 105 | ], 106 | "dev": true, 107 | "license": "MIT", 108 | "optional": true, 109 | "os": [ 110 | "linux" 111 | ] 112 | }, 113 | "node_modules/@rollup/rollup-linux-arm-musleabihf": { 114 | "version": "4.40.1", 115 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz", 116 | "integrity": "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==", 117 | "cpu": [ 118 | "arm" 119 | ], 120 | "dev": true, 121 | "license": "MIT", 122 | "optional": true, 123 | "os": [ 124 | "linux" 125 | ] 126 | }, 127 | "node_modules/@rollup/rollup-linux-arm64-gnu": { 128 | "version": "4.40.1", 129 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz", 130 | "integrity": "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==", 131 | "cpu": [ 132 | "arm64" 133 | ], 134 | "dev": true, 135 | "license": "MIT", 136 | "optional": true, 137 | "os": [ 138 | "linux" 139 | ] 140 | }, 141 | "node_modules/@rollup/rollup-linux-arm64-musl": { 142 | "version": "4.40.1", 143 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz", 144 | "integrity": "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==", 145 | "cpu": [ 146 | "arm64" 147 | ], 148 | "dev": true, 149 | "license": "MIT", 150 | "optional": true, 151 | "os": [ 152 | "linux" 153 | ] 154 | }, 155 | "node_modules/@rollup/rollup-linux-loongarch64-gnu": { 156 | "version": "4.40.1", 157 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz", 158 | "integrity": "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==", 159 | "cpu": [ 160 | "loong64" 161 | ], 162 | "dev": true, 163 | "license": "MIT", 164 | "optional": true, 165 | "os": [ 166 | "linux" 167 | ] 168 | }, 169 | "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { 170 | "version": "4.40.1", 171 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz", 172 | "integrity": "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==", 173 | "cpu": [ 174 | "ppc64" 175 | ], 176 | "dev": true, 177 | "license": "MIT", 178 | "optional": true, 179 | "os": [ 180 | "linux" 181 | ] 182 | }, 183 | "node_modules/@rollup/rollup-linux-riscv64-gnu": { 184 | "version": "4.40.1", 185 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz", 186 | "integrity": "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==", 187 | "cpu": [ 188 | "riscv64" 189 | ], 190 | "dev": true, 191 | "license": "MIT", 192 | "optional": true, 193 | "os": [ 194 | "linux" 195 | ] 196 | }, 197 | "node_modules/@rollup/rollup-linux-riscv64-musl": { 198 | "version": "4.40.1", 199 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz", 200 | "integrity": "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==", 201 | "cpu": [ 202 | "riscv64" 203 | ], 204 | "dev": true, 205 | "license": "MIT", 206 | "optional": true, 207 | "os": [ 208 | "linux" 209 | ] 210 | }, 211 | "node_modules/@rollup/rollup-linux-s390x-gnu": { 212 | "version": "4.40.1", 213 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz", 214 | "integrity": "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==", 215 | "cpu": [ 216 | "s390x" 217 | ], 218 | "dev": true, 219 | "license": "MIT", 220 | "optional": true, 221 | "os": [ 222 | "linux" 223 | ] 224 | }, 225 | "node_modules/@rollup/rollup-linux-x64-gnu": { 226 | "version": "4.40.1", 227 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz", 228 | "integrity": "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==", 229 | "cpu": [ 230 | "x64" 231 | ], 232 | "dev": true, 233 | "license": "MIT", 234 | "optional": true, 235 | "os": [ 236 | "linux" 237 | ] 238 | }, 239 | "node_modules/@rollup/rollup-linux-x64-musl": { 240 | "version": "4.40.1", 241 | "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz", 242 | "integrity": "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==", 243 | "cpu": [ 244 | "x64" 245 | ], 246 | "dev": true, 247 | "license": "MIT", 248 | "optional": true, 249 | "os": [ 250 | "linux" 251 | ] 252 | }, 253 | "node_modules/@rollup/rollup-win32-arm64-msvc": { 254 | "version": "4.40.1", 255 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz", 256 | "integrity": "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==", 257 | "cpu": [ 258 | "arm64" 259 | ], 260 | "dev": true, 261 | "license": "MIT", 262 | "optional": true, 263 | "os": [ 264 | "win32" 265 | ] 266 | }, 267 | "node_modules/@rollup/rollup-win32-ia32-msvc": { 268 | "version": "4.40.1", 269 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz", 270 | "integrity": "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==", 271 | "cpu": [ 272 | "ia32" 273 | ], 274 | "dev": true, 275 | "license": "MIT", 276 | "optional": true, 277 | "os": [ 278 | "win32" 279 | ] 280 | }, 281 | "node_modules/@rollup/rollup-win32-x64-msvc": { 282 | "version": "4.40.1", 283 | "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz", 284 | "integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==", 285 | "cpu": [ 286 | "x64" 287 | ], 288 | "dev": true, 289 | "license": "MIT", 290 | "optional": true, 291 | "os": [ 292 | "win32" 293 | ] 294 | }, 295 | "node_modules/@types/estree": { 296 | "version": "1.0.7", 297 | "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", 298 | "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", 299 | "dev": true, 300 | "license": "MIT" 301 | }, 302 | "node_modules/fsevents": { 303 | "version": "2.3.3", 304 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 305 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 306 | "dev": true, 307 | "hasInstallScript": true, 308 | "optional": true, 309 | "os": [ 310 | "darwin" 311 | ], 312 | "engines": { 313 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 314 | } 315 | }, 316 | "node_modules/rollup": { 317 | "version": "4.40.1", 318 | "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz", 319 | "integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==", 320 | "dev": true, 321 | "license": "MIT", 322 | "dependencies": { 323 | "@types/estree": "1.0.7" 324 | }, 325 | "bin": { 326 | "rollup": "dist/bin/rollup" 327 | }, 328 | "engines": { 329 | "node": ">=18.0.0", 330 | "npm": ">=8.0.0" 331 | }, 332 | "optionalDependencies": { 333 | "@rollup/rollup-android-arm-eabi": "4.40.1", 334 | "@rollup/rollup-android-arm64": "4.40.1", 335 | "@rollup/rollup-darwin-arm64": "4.40.1", 336 | "@rollup/rollup-darwin-x64": "4.40.1", 337 | "@rollup/rollup-freebsd-arm64": "4.40.1", 338 | "@rollup/rollup-freebsd-x64": "4.40.1", 339 | "@rollup/rollup-linux-arm-gnueabihf": "4.40.1", 340 | "@rollup/rollup-linux-arm-musleabihf": "4.40.1", 341 | "@rollup/rollup-linux-arm64-gnu": "4.40.1", 342 | "@rollup/rollup-linux-arm64-musl": "4.40.1", 343 | "@rollup/rollup-linux-loongarch64-gnu": "4.40.1", 344 | "@rollup/rollup-linux-powerpc64le-gnu": "4.40.1", 345 | "@rollup/rollup-linux-riscv64-gnu": "4.40.1", 346 | "@rollup/rollup-linux-riscv64-musl": "4.40.1", 347 | "@rollup/rollup-linux-s390x-gnu": "4.40.1", 348 | "@rollup/rollup-linux-x64-gnu": "4.40.1", 349 | "@rollup/rollup-linux-x64-musl": "4.40.1", 350 | "@rollup/rollup-win32-arm64-msvc": "4.40.1", 351 | "@rollup/rollup-win32-ia32-msvc": "4.40.1", 352 | "@rollup/rollup-win32-x64-msvc": "4.40.1", 353 | "fsevents": "~2.3.2" 354 | } 355 | }, 356 | "node_modules/typescript": { 357 | "version": "5.8.3", 358 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", 359 | "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", 360 | "dev": true, 361 | "license": "Apache-2.0", 362 | "bin": { 363 | "tsc": "bin/tsc", 364 | "tsserver": "bin/tsserver" 365 | }, 366 | "engines": { 367 | "node": ">=14.17" 368 | } 369 | } 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "copee", 3 | "version": "1.0.6", 4 | "description": "Copy text from browser to clipboard...natively! < 1kB", 5 | "homepage": "https://copee.ceriously.com/", 6 | "main": "dist/copee", 7 | "types": "dist/copee.d.ts", 8 | "browser": "dist/copee.mjs", 9 | "module": "dist/copee.mjs", 10 | "scripts": { 11 | "build": "npm run mjs && npm run cjs && npm run umd && npm run iife && npm run docs", 12 | "mjs": "tsc -d && mv dist/copee.js dist/copee.mjs", 13 | "cjs": "tsc -m commonjs", 14 | "umd": "rollup -i dist/copee.mjs -o dist/copee.umd.js -f umd -n copee", 15 | "iife": "rollup -i docs/script.js -o docs/iife.js -f iife -g copee:copee", 16 | "docs": "./replace.sh", 17 | "test": "echo 'no tests at this time'", 18 | "release": "npm version patch && git push --follow-tags && npm publish" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/styfle/copee.git" 23 | }, 24 | "files": [ 25 | "dist" 26 | ], 27 | "keywords": [ 28 | "copy", 29 | "paste", 30 | "clipboard" 31 | ], 32 | "author": "styfle", 33 | "license": "MIT", 34 | "bugs": { 35 | "url": "https://github.com/styfle/copee/issues" 36 | }, 37 | "devDependencies": { 38 | "rollup": "^4.2.0", 39 | "typescript": "^5.0.3" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /replace.sh: -------------------------------------------------------------------------------- 1 | cat docs/script.js | sed -e 's/copee/https:\/\/cdn.jsdelivr.net\/npm\/copee\/dist\/copee.mjs/' > docs/mod.mjs -------------------------------------------------------------------------------- /src/copee.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copy text to the user's clipboard 3 | */ 4 | export function toClipboard(text: string): boolean { 5 | 6 | // Create element and select the text 7 | const ta = document.createElement('textarea'); 8 | ta.value = text; 9 | ta.cols = 1; 10 | ta.rows = 1; 11 | ta.style.color = 'transparent'; 12 | ta.style.border = 'none'; 13 | document.body.appendChild(ta); 14 | ta.select(); 15 | 16 | let success = false; 17 | try { 18 | success = document.execCommand('copy'); 19 | } catch (err) { 20 | success = false; 21 | } 22 | 23 | // Cleanup the element we created 24 | document.body.removeChild(ta); 25 | 26 | return success; 27 | } 28 | 29 | /** 30 | * Copy text from an input/textarea element to a user's clipboard 31 | * @param el The or element 32 | * @param preserveSelection True if text selection/highlight should be preserved after copying text 33 | */ 34 | export function fromElement(el: HTMLInputElement | HTMLTextAreaElement, preserveSelection?: boolean) { 35 | let start = 0; 36 | let end = 0; 37 | let success = false; 38 | 39 | if (preserveSelection) { 40 | start = el.selectionStart || 0; 41 | end = el.selectionEnd || 0; 42 | } 43 | 44 | el.select(); 45 | 46 | try { 47 | success = document.execCommand('copy'); 48 | } catch (err) { 49 | success = false; 50 | } 51 | 52 | if (preserveSelection) { 53 | el.setSelectionRange(start, end); 54 | } 55 | 56 | return success; 57 | } 58 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es2015", 4 | "target": "ES2017", 5 | "strict": true, 6 | "rootDir": "src", 7 | "outDir": "dist", 8 | "sourceMap": false 9 | }, 10 | "exclude": [ 11 | "node_modules", 12 | "dist/copee.d.ts" 13 | ] 14 | } --------------------------------------------------------------------------------
Copy text from browser to clipboard...natively!
btn1.addEventListener('click', function (e) { 20 | var success = toClipboard('Magic happens here'); 21 | showAlert(success); 22 | });
Example 1: Copy hard-coded text
Copy To Clipboard
btn2.addEventListener('click', function (e) { 32 | var success = fromElement(input); 33 | showAlert(success); 34 | }); 35 |
Example 2: Copy from input element
input