Qiita のコードの右上にコピーボタンを追加するユーザースクリプト

# はじめに

Qiita のコードの右上にコピーボタンを追加するユーザースクリプトです。

[Tampermonkey (Chrome)](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo?hl=ja) や [Greasemonkey (Firefox)](https://addons.mozilla.org/ja/firefox/addon/greasemonkey/) などの拡張機能をインストールした後、以下のリンクをクリックすると追加できます。

#### https://gist.github.com/shge/6a348d939b6f1ee76611288a7cfaed5b/raw/qiita-copy.user.js

![copy.gif](https://qiita-image-store.s3.amazonaws.com/0/155587/c2369d2a-a0e3-9872-aa11-004cf60ac868.gif)


以下の2つの記事のコードをミックスさせていただき、少し手を加えました。

#### [Qiitaの記事内のコード部分を楽にコピペするユーザースクリプト - Qiita](https://qiita.com/vzvu3k6k/items/0a0abb0e848ed2c10651)

- コードが簡潔

#### [Qiitaのコードにコピーボタンを追加するユーザースクリプト - Qiita](https://qiita.com/khsk/items/27a80755d883521ecd73)

- コピー機能（`document.execCommand('copy')`）
- アイコン（`fa fa-clipboard`）

#### 自分で追加したもの

- コピー後の **Copied!** という表示


# コード

```qiita-copy.user.js
// ==UserScript==
// @name           Qiita: Copy Code
// @description    Qiita のコードをコピーするボタンを右上に追加します。
// @version        1.0
// @match          *://qiita.com/*/items/*
// @namespace      https://shge.github.io
// @author         shge
// ==/UserScript==

var style = document.createElement('style');
style.textContent =
    '.code-frame {' +
    '   position: relative;' +
    '}' +
    '.code-frame .__select-code {' +
    '   position: absolute;' +
    '   padding-top: 8px;' +
    '   right: 25px;' +
    '   float: right;' +
    '   font-size: 1.5rem;' +
    '}' +
    '.code-frame .__select-code span {' +
    '   margin-right: 2rem;' +
    '   font-weight: bold;' +
    '   opacity: 0;' +
    '   background-color: #364549;' +
    '   padding: 10px;' +
    '}' +
    '.code-frame .__select-code i {' +
    '   cursor: pointer;' +
    '}' +
    '.code-frame .__select-code i:hover {' +
    '   opacity: 0.8;' +
    '}' +
    '.code-frame .__select-code i:active {' +
    '   opacity: 0.6;' +
    '}';
document.head.appendChild(style);

document.addEventListener('DOMNodeInserted', function(e){
    var node = e.target;
    if(node.nodeType == Node.ELEMENT_NODE && node.classList.contains('code-frame')) {
        if(!node.querySelector('.__select-code')){
            addSelectButton(e.target);
        }
    }
});

Array.prototype.forEach.call(document.querySelectorAll('.code-frame'), addSelectButton);

function addSelectButton(elmCodeFrame){
    var elmSelectButton = document.createElement('div');
    elmSelectButton.setAttribute('class', '__select-code');
    elmSelectButton.addEventListener('click', function(){
        select(elmCodeFrame);
    });
    elmSelectButton.insertAdjacentHTML('afterbegin', '<span>Copied!</span><i class="fa fa-clipboard"/>');
    elmCodeFrame.insertBefore(elmSelectButton, elmCodeFrame.firstChild);
    console.log('inserted');
}

function select(elm){
    var selection = window.getSelection();
    selection.removeAllRanges();
    var range = document.createRange();
    range.selectNodeContents(elm.querySelector('pre'));
    selection.addRange(range);
    document.execCommand('copy');
    selection.removeAllRanges();
    var span = elm.querySelector('.__select-code span');
    span.style.transition = 'none';
    span.style.opacity = 1;
    setTimeout(function() {
      span.style.transition = 'opacity 1s';
    }, 500);
    setTimeout(function() {
      span.style.opacity = 0;
    }, 1000);
}
```