コード用コピーボタンをカスタマイズ(Clipboard API対応)

2021/03/15
4
copy

highlight.jsの表示関連でコピーボタンも付けてみたいなぁと以前から思っていて、今回ついに導入することに。導入に伴い、コピーボタンの仕様を自分好みにアレンジしてみました。

変更点

もともとのコピーボタンはソースコードの要素上にポインターが乗ったら次に移るまで表示されたままになっていましたが、個人的にマウスオーバー時は表示してそれ以外は非表示、というスタイルが良かったので変更しました。

コピーボタンの提供元はこちら。便利な機能を実現していただき、感謝です。

【 highlight.js 】 を使いやすくする!ソースコードをボタン1つで全選択他

変更コード

書き換えたコードはこちらです。

<script type='text/javascript'>
  (function() {
    const btn = document.createElement('button');
    btn.id = 'selectPre';
    btn.textContent = 'COPY';
    btn.addEventListener('click', () => {
      const sel = window.getSelection();
      const pre = btn.parentNode;
      sel.selectAllChildren(pre);
      sel.extend(pre, pre.childNodes.length-1);
      if (navigator.clipboard) {
        navigator.clipboard
        .writeText(sel)
        .then(() => {
          alert('コピーしました!');
        })
        .catch(() => {
          alert('コピーに失敗しました');
        });
      } else {
        document.execCommand('copy');
        alert('コピーしました!');
      }
      //sel.empty();
    });
    const pres = document.querySelectorAll('pre');
    pres.forEach((pre) => {
      pre.addEventListener('mouseover', () => {
        pre.appendChild(btn);
      });
      pre.addEventListener('mouseleave', () => {
        btn.remove();
      });
    });
  }());
</script>

基本的な機能はそのままに、要素上にポインターがあるかどうかでボタンの表示/非表示を切り替えるようにしました。設置は</body>の真上辺りが良いかと思います。

コメントアウト部分のsel.empty()は、コピー後の全選択状態を解除するものです。選択状態を解除したい場合はコメントアウトを外して使用してください。

presの取得が元コードと違い"pre"になっていますが、これは私が本文中でも<code> ~ </code>を単体で用いることがあるため、それらと混同せず確実にソースコード部分を指定できるようにしたためです。こちらの設定で不都合が出る方は"code"に変えていただいても問題ありません。

CSSはこちらを参考にしてください。

<style>
  #selectPre {
    position: absolute;
    top: 5px;
    right: 8px;
    color: #444;
    background: #fff;
    padding: 2px 5px; 
    border: none;
    border-radius: 2px;    
    cursor: pointer;
    opacity: 0.8;
    transition: 0.3s;
  }
  #selectPre:hover {
    opacity: 1;
  }
  #selectPre::before {
    content: url('data:image/svg+xml;utf-8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="1em" height="1em"><path d="M433.941 65.941l-51.882-51.882A48 48 0 0 0 348.118 0H176c-26.51 0-48 21.49-48 48v48H48c-26.51 0-48 21.49-48 48v320c0 26.51 21.49 48 48 48h224c26.51 0 48-21.49 48-48v-48h80c26.51 0 48-21.49 48-48V99.882a48 48 0 0 0-14.059-33.941zM266 464H54a6 6 0 0 1-6-6V150a6 6 0 0 1 6-6h74v224c0 26.51 21.49 48 48 48h96v42a6 6 0 0 1-6 6zm128-96H182a6 6 0 0 1-6-6V54a6 6 0 0 1 6-6h106v88c0 13.255 10.745 24 24 24h88v202a6 6 0 0 1-6 6zm6-256h-64V48h9.632c1.591 0 3.117.632 4.243 1.757l48.368 48.368a6 6 0 0 1 1.757 4.243V112z" fill="%23444"/></svg>');
    vertical-align: -0.125em;
    margin-right: 2px;
  }
</style>

※コピーボタンのアイコン部分にはsvgを使用しています。

highlight.jsに関する詳しい情報とコピーボタンのCSSはこちらの記事を参考にさせていただきました。

highlight.js を導入しました | ふじろじっく

ソースコードの表示を見栄え良くして、便利なコピーボタンも付けました。

追記(2021/7/14)

document.execCommandの廃止に伴い、コピー機能をClipboard APIに対応させました。

4件のコメント
こんにちは。
マウスホバー時のみ現れるという仕様が何となく良さそうだったので、早速当ブログのコピーボタンのコードをこちらのコードと入れ替えてみました。記事にも追記してあります。
https://fujilogic.blogspot.com/2020/08/highlight.js.html#ps2

ところで、このコードのような次世代のスクリプトって圧縮できないんですね。
ひとつ勉強になりました(^^;
ふじやんさん、こんにちは。気に入っていただけたようなら何よりです。記事のご紹介もありがとうございます(^^)

スクリプトの件、私も最近気づいたのですがどうやらそのようですね。単純にホワイトスペースを削除するぐらいで、根本的な圧縮はできないみたいです。コードとしての安全性と利便性とのトレードオフ…という感じでしょうか(^^;)
マウスオーバー時に表示する...。というのをやってみたかったので参考になりました。ありがとうございます。
>sutajpさん
コメントありがとうございます。お役に立てたようで嬉しいです。