Last active
August 22, 2026 16:22
-
-
Save eladkarako/fbc3b639b5ea15738145e7bd6d38f1b4 to your computer and use it in GitHub Desktop.
copy_to_clipboard is a javascript for web-browsers, it first tries the modern API that needs a secure context, and document with some user-interaction, if failed, it tries old-school way. best to be used with button or anchor elements and click events. textarea may be used instead of div, but div can handle more text.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| const copy_to_clipboard = async (text)=>{ | |
| text = String(text); | |
| //------------------------------------------------------- a lot of checks needs to finish, I've condensed it to just trying. failing will skip to old-school way without notifying regards errors in this modern clipboard API (for example lack of permission would result with "NotAllowedError"), the sum of this function is "maximum effort", in most cases you'll probably end up using the old-school way. if you want you can use explicit true===self.isSecureContext && navigator.clipboard && navigator.clipboard.writeText && navigator.permissions && navigator.permissions.query && ("granted" === (await navigator.permissions.query({name:"clipboard-write"})).state) | |
| let is_success = false; | |
| try{ | |
| await navigator.clipboard.writeText(text); | |
| is_success = true; | |
| }catch(err){} | |
| if(is_success){return;} | |
| //------------------------------------------------------- if reached here, an old-school way will be used. again, minimal checks, and just trying. | |
| let dummy_element = document.querySelector('[id="dummy_element"]'); //just like singleton, keep it in DOM for possible future calls. | |
| if(null === dummy_element){ | |
| dummy_element = document.createElement("div"); | |
| dummy_element.setAttribute("id" , "dummy_element"); | |
| dummy_element.setAttribute("contentEditable" , "plaintext-only"); | |
| dummy_element.setAttribute("spellcheck" , "false"); | |
| dummy_element.setAttribute("autocomplete" , "off"); | |
| dummy_element.setAttribute("autocorrect" , "off"); | |
| dummy_element.setAttribute("autocapitalize" , "off"); | |
| dummy_element.setAttribute("translate" , "false"); | |
| dummy_element.setAttribute("role" , "textbox"); | |
| dummy_element.setAttribute("aria-hidden" , "true"); | |
| dummy_element.setAttribute("tabindex" , "-1"); | |
| (document.querySelector("head") || document.documentElement).appendChild(dummy_element); | |
| } | |
| //------------------------------------------------------- backup existing selections in page. | |
| const ranges_backup = []; | |
| let selection = self.getSelection(); | |
| const count = (selection ? selection.rangeCount : 0) || 0; | |
| for(let index = 0; index < count; index+=1){ | |
| ranges_backup.push( selection.getRangeAt(index).cloneRange() ); | |
| } | |
| //------------------------------------------------------- copy, using an old-school method, needs focused element, and selected content. | |
| selection = self.getSelection(); | |
| selection.removeAllRanges(); | |
| try{dummy_element.focus();}catch(err){} | |
| dummy_element.textContent = text; | |
| const range = document.createRange(); | |
| range.selectNodeContents( dummy_element ); | |
| selection.addRange( range ); | |
| is_success = false; | |
| try{ | |
| is_success = document.execCommand("copy"); | |
| }catch(err){} | |
| dummy_element.textContent = ""; | |
| //------------------------------------------------------- before handling the copy result, try to restore previously backed-up selections from page. | |
| selection = self.getSelection(); | |
| selection.removeAllRanges(); | |
| ranges_backup.forEach(range=>{ | |
| try{ | |
| selection.addRange(range); | |
| }catch(err){} | |
| }); | |
| //------------------------------------------------------- handle copy result, everything is unified to no-support error. | |
| if(false === is_success){ | |
| let error = null; | |
| if("undefined" !== typeof DOMException){ | |
| error = new DOMException("no support for \"copy\" command", "NotSupportedError"); | |
| }else{ | |
| error = new Error(); | |
| error.name = "NotSupportedError"; | |
| error.message = "no support for \"copy\" command"; | |
| error.code = 9; //DOMException.NOT_SUPPORTED_ERR === 9 | |
| } | |
| throw error; | |
| } | |
| }; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment