从书签脚本复制文本到剪贴板

33

我正在尝试编写一个小书签,可以从活动页面提取一些文本并将其加载到剪贴板中。

提取部分很容易,但是我在复制到剪贴板的部分卡住了。目前,我只是警告文本并按下Ctrl+C从消息框中复制文本,这并不理想。

我已经阅读了JavaScript如何复制到剪贴板和其他建议使用zeroclipboard的问题,但我不知道如何从一个书签让它工作,因为我必须加载外部flash和javascript资源才能使用该库。

考虑到这只是一个私人书签,我没有问题通过修改页面的DOM来完成此操作或启用浏览器(Google Chrome)上的某些权限。

任何指针将不胜感激。

7个回答

25

自从新的剪贴板 API出现,对于支持该 API 的浏览器来说,这变得很容易:

javascript: navigator.clipboard.writeText('some text from somewhere');null;

注意:任何在您的书签小工具中弹出的警报或提示都将导致文档失去焦点,并且剪贴板API将变得不可用。在这种情况下,您需要将焦点移回文档,然后再执行任何后续剪贴板操作:

const oldFocus = document.activeElement; 
/* do popup stuff here */
oldFocus.focus(); 
/* do clipboard stuff here */

2
“某个地方的一些文本”比通常用于示例的“bla blah bal”更加严肃。 - dotbit
2
;null; 中,null; 的目的是什么? - Kevin Fegan
2
另外,由于这是用于书签工具的,所以所有内容都被“压缩”(合并)到一行中,因此在书签工具中使用//会有问题。我认为/* comment */应该可以正常工作。 - Kevin Fegan
@kevin-fegan 书签脚本需要返回一个假值,除非你想让浏览器用返回值替换当前页面。这段代码已经做到了,但通常最好始终明确将返回值设为 null,这样就可以安全地避免任何改变触发陷阱的情况。有很多方法可以保证这一点 - 我更喜欢返回一个明确的 null 或 false 值。 - Joachim Lous
它适用于不支持document.execCommand("copy")的Firefox,但是它会失去焦点。弹出式内容是什么意思?您是否有一个完整的示例,可以将网页标题复制到剪贴板而不会失去焦点?@JoachimLous - netawater
3
@netawater - 我相当确信JoachimLous所指的是,如果您的书签代码打开任何警报或“弹出窗口” (例如:window.alert(“ sometext”);alert(“ sometext”);window.confirm(“ sometext”);confirm(“ sometext”);window.prompt(“sometext”,“defaultText”);prompt(“ sometext”,“defaultText”);),则在您写入剪贴板的代码之前一定要小心。如果您不使用警报或提示,或者在写入剪贴板的代码之后使用它们,则无需担心恢复焦点,您可以忽略....focus();的内容。 - Kevin Fegan

21
在Github Gist中有一个很好的小书签,它可以做到你想要的核心内容--复制到剪贴板。它不使用任何外部库,我认为这是优点之一。如原文所述,它复制了一些静态文本,但向底部倾斜,讨论了将其改编为其他用途,例如复制页面标题。既然您已经说明“提取足够容易…”,您应该很容易地将该Gist适应您想要做的事情。我尝试了纯净版的书签,因为我经常需要将一些静态文本传输到我的剪贴板上。在Chrome 61下没有修改非常好用。但请确保您阅读评论;有些人对在其他浏览器和场景中使其工作有建议。这是我测试过的代码,已经被压缩并准备好转换为书签:
javascript:!function(a){var b=document.createElement("textarea"),c=document.getSelection();b.textContent=a,document.body.appendChild(b),c.removeAllRanges(),b.select(),document.execCommand("copy"),c.removeAllRanges(),document.body.removeChild(b)}("Text To Copy");

Gist也有预先压缩的代码。


1
显然,这在Firefox中无法正常工作document.execCommand('cut'/'copy')被拒绝,因为它没有在短时间内由用户生成的事件处理程序内调用。 - Perseids
1
谢谢@Perseids。Mark的答案提供了更多有关如何处理Firefox的信息。此外,这个基于原始Gist的内容还有一两条评论可能会有所帮助。 - Gundark

11

由于缺少权限,最近版本的Firefox浏览器通过书签小工具与剪贴板交互通常不起作用(有关更多详细信息,请参见此信息)。但是,可能有一种方法,可以使书签小工具显示一个按钮,并在按钮点击事件处理程序的上下文中执行剪贴板交互。

可能更简单的解决方案是使用用户脚本管理器,并以用户脚本的形式定义您的书签小工具,您可以通过键盘组合键激活它。 例如,参见此用户脚本,下面是完整内容:

// Add the following as a user-script (via an extension like https://github.com/violentmonkey/violentmonkey) in order to copy the
// current webpage and selected text to the clipboard in a format suitable for pasting into an org-mode document.
// To execute the action, you need to press Alt-C on a webpage, though this can be modified by changing the keycode
// used in the onkeyup function.

// ==UserScript==
// @name Copy Org-mode Link
// @namespace Violentmonkey Scripts
// @match *://*/*
// @grant clipboardWrite
// ==/UserScript==

function main() {
    function copyTextToClipboard(text) { var textArea = document.createElement("textarea"); textArea.style.position = 'fixed'; textArea.style.top = 0; textArea.style.left = 0; textArea.style.width = '2em'; textArea.style.height = '2em'; textArea.style.padding = 0; textArea.style.border = 'none'; textArea.style.outline = 'none'; textArea.style.boxShadow = 'none'; textArea.style.background = 'transparent'; textArea.value = text; document.body.appendChild(textArea); textArea.select(); try { var successful = document.execCommand('copy'); var msg = successful ? 'successful' : 'unsuccessful'; console.log('Copying text command was ' + msg); } catch (err) { console.log('Oops, unable to copy'); } document.body.removeChild(textArea); }; var url = encodeURIComponent(location.href); url = url.replace(/%253A/g, ':').replace(/%252F/g, '/'); var title = document.title; title = title.replace(/\[/g, '{'); title = title.replace(/\]/g, '}'); var sel_text = window.getSelection(); copyTextToClipboard('[['+url+']['+title+']]'+'\n\n'+sel_text);
}

// listen for Alt-C key-combination, and then execute
document.onkeyup=function(e){
    var e = e || window.event; // for IE to cover IEs window object
    if(e.altKey && e.which == 67) {
         main();
         return false;
    }
}

5
一个有点不寻常的答案:打开一个空白页面,让用户从中复制文本。
<a href="javascript:window.open('data:text/html, <html contenteditable>sup<script>document.execCommand(\'selectAll\')</script></html>')">
  Copy the text “sup”
</a>

只需将 sup 替换为您想让用户复制的文本即可。

JS Bin 示例


4
方便,谢谢!注意:一些引号需要转义:execCommand(\'selectAll\') - ptim

4
这是我解决它的方法,使用@zzzzBov在他的答案中提到的技术,通过书签工具将 zeroclipboard导入页面。当书签工具运行时,手形光标会出现在页面上的任何位置上悬停。单击将复制(例如)文档标题到剪贴板。 (由于Chrome似乎正在从书签工具中删除所有换行符(或其他内容),因此已将指向zeroclipboard资源的链接替换为占位符,并使用多行注释)。
javascript:

var s = document.createElement('script');
s.setAttribute('src', 'http://example.com/ZeroClipboard.js');

s.onload = s.onreadystatechange = 
  function()
  { 
     ZeroClipboard.setMoviePath( 'http://example.com/ZeroClipboard.swf');
     var clip = new ZeroClipboard.Client();   

     /* glue to the body: sample only, in reality  we should
        probably create a new visible element and glue to that. */
     clip.glue(document.body);   

     clip.setHandCursor( true );

     /* copy to clipboard on mouse-up */
     clip.addEventListener('onMouseUp', 
      function (client) 
      {      
         /* example */
         var toCopy = document.title;        
         clip.setText(toCopy);    

         alert(toCopy + ' copied.');
         clip.hide();
      });  
   };

document.body.appendChild(s);

3

声明:

  1. 我不是在试图垃圾邮件你
  2. 如果您选择使用此内容,我将不会获得任何收益

我以前制作了一个书签生成器,以便更轻松地创建书签。

它启用了jQuery,但这并不意味着您必须使用jQuery。

您可以查看源代码,了解如何通过书签导入另一个脚本/库到页面中。

特别是导入jQuery的行:

if (!window.zbooks)
  {
    //if zbooks hasn't been set, initialize it

    //s used for the Script element
    var s = document.createElement('script');
    //r used for the Ready state
    var r = false;
    //set the script to the latest version of jQuery
    s.setAttribute('src', 'http://code.jquery.com/jquery-latest.min.js');
    //set the load/readystate events
    s.onload = s.onreadystatechange = function()
    {
/**
 * LOAD/READYSTATE LOGIC
 * execute if the script hasn't been ready yet and:
 * - the ready state isn't set
 * - the ready state is complete
 *   - note: readyState == 'loaded' executes before the script gets called so
 *     we skip this event because it wouldn't have loaded the init event yet.
 */
      if ( !r && (!this.readyState || this.readyState == 'complete' ) )
      {
        //set the ready flag to true to keep the event from initializing again
        r = true;
        //prevent jQuery conflicts by placing jQuery in the zbooks object
        window.zbooks = {'jQuery':jQuery.noConflict()};
        //make a new zbook
        window.zbooks[n] = new zbooks(c);
      }
    };
    //append the jQuery script to the body
    b.appendChild(s);
  }

我希望那有所帮助。


看起来很有用;我会试一下并让您知道。这将需要我大量的试错;我对js一窍不通。 - Ani
1
谢谢,那个导入技巧足以让我开始工作了。 - Ani

1

同时适用于HTTP和HTTPS环境的解决方案

@Joachim Lous提供的剪贴板API解决方案对我很有帮助。但是,这在使用http而不是https的localhost上无法工作。为了解决这个问题,我使用了一个“copyToClipboard”函数(从this SO answer改编而来),它作为一个全能的包装函数,通过使用巧妙的“视口外隐藏文本区域”技巧考虑了http环境。

易读版本:

javascript:

function copyToClipboard(textToCopy) {
    // navigator clipboard api needs a secure context (https)
    if (navigator.clipboard && window.isSecureContext) {
        // navigator clipboard api method
        return navigator.clipboard.writeText(textToCopy);
    } else {
        // use the 'out of viewport hidden text area' trick
        let textArea = document.createElement("textarea");
        textArea.value = textToCopy;
        // make the textarea out of viewport
        textArea.style.position = "fixed";
        textArea.style.left = "-999999px";
        textArea.style.top = "-999999px";
        document.body.appendChild(textArea);
        textArea.focus();
        textArea.select();
        return new Promise((res, rej) => {
            // here the magic happens
            document.execCommand('copy') ? res() : rej();
            textArea.remove();
        });
    }
};
var element = document.querySelector(".myClass");
var text = element.textContent || element.value;
copyToClipboard(text);

只需将“.myClass”替换为您的选择器。

书签小程序的压缩版本:

javascript:void function(){var a=document.querySelector(".myClass"),b=a.textContent||a.value;(function(a){if(navigator.clipboard&&window.isSecureContext)return navigator.clipboard.writeText(a);else{let b=document.createElement("textarea");return b.value=a,b.style.position="fixed",b.style.left="-999999px",b.style.top="-999999px",document.body.appendChild(b),b.focus(),b.select(),new Promise((a,c)=>{document.execCommand("copy")?a():c(),b.remove()})}})(b)}();

编辑:

以下是一个解决方案,适用于那些希望从页面动态选择文本的人。

javascript:void function(){function a(a){if(navigator.clipboard&&window.isSecureContext)return navigator.clipboard.writeText(a);else{let b=document.createElement("textarea");return b.value=a,b.style.position="fixed",b.style.left="-999999px",b.style.top="-999999px",document.body.appendChild(b),b.focus(),b.select(),new Promise((a,c)=>{document.execCommand("copy")?a():c(),b.remove()})}}function b(a){c.style.pointerEvents="none";var b=document.elementFromPoint(a.clientX,a.clientY);return c.style.pointerEvents="auto",b}var c=document.createElement("div");Object.assign(c.style,{position:"fixed",top:0,left:0,width:"100vw",height:"100vh",zIndex:99999999,background:"transparent",cursor:"crosshair"}),document.body.append(c);document.addEventListener("mousemove",function(a){var d=b(a);if(d){var e=d.getBoundingClientRect();Object.assign(c.style,{background:"rgba(0, 128, 255, 0.25)",outline:"1px solid rgba(0, 128, 255, 0.5)",top:""+e.top+"px",left:""+e.left+"px",width:""+e.width+"px",height:""+e.height+"px"})}}),c.addEventListener("click",function(d){var e=b(d),f=e.textContent||e.value;f=f.replace(/\n[ \n]+\n/g,"\n").replace(/\n\n+/g,"\n\n").replace(/^\n+|\n+$/g,""),f.match("\n")||(f=f.replace(/^ +| +$/,"")),a(f),document.body.removeChild(c)})}();

只需将其复制并粘贴到书签的“URL”部分,如下所示:

bookmarklet


为什么你返回一个新的 Promise?我得到了未捕获的 Promise 错误。我认为原因是 resrej 不存在? - IMTheNachoMan
你是否在使用压缩版本?我已经在回复的编辑部分添加了另一种压缩版本供你尝试。根据你所需的功能,它可能会更好地适合你。 - Justin Dehorty
我会尝试。谢谢! - IMTheNachoMan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接