前置き
"待"ってたぜェ!! この"瞬間"をよォ!!
と、いうことで、去る9/22にFirefox 41 が正式リリースされました。
注目すべきはコレ
document.execCommand("cut") / document.execCommand("copy") を利用して、Web コンテンツのコピーとカットができるようになりました
今までよくクリップボードへのコピーボタンは見かけましたが、あれってFlash経由らしいですね。
でも、これからはJavaScript
のみで出来るらしいです!
Chrome
ではすでに対応していたみたいですが、Firefox
も遅ればせながら…
なので、
やってみました。
動作確認環境
Windows7
+ Firefox41
+ Greasemonkey
今回も
Firefox
のスクラッチパッド機能で開発→Greasemonkey
へ貼り付け
なので、Greasemonkey
依存は無いはずです。
Chrome
のコンソールにも貼り付けてみたのですが、出来るはずのselection
でコケました。
コンソールは独特なのかな?と思っています。
Chrome 43からcutおよびcopyコマンドが使えます
はJSFiddleでChrome
でも動いたので、自分のコードが悪いかコンソールのせいか…
デモ
コード
人様用
Qiita_code_copy_button.user.js
// ==UserScript==
// @name Qiita code copy button
// @namespace khsk
// @description コードをコピーするボタンを追加する
// @include http://qiita.com/*/items/*
// @include https://qiita.com/*/items/*
// @version 1
// @grant none
// ==/UserScript==
const OFF = 0
const ON = 1
// マウスオーバー時のみボタンを表示 ただし、ボタン分の領域は常に確保
var enable_mouseover = ON
// インターフェース作成
var i = document.createElement('i')
i.className = 'fa fa-clipboard'
var div = document.createElement('div')
div.appendChild(i)
// クラスによるfloatを解除
div.style.float = 'none'
// クラスによるフォントの拡大を解除
div.style.fontSize = 'initial'
// コード内かつコード名の右側へ
div.style.display = 'inline-block'
// 上に張り付いているので少し離す(本来よりn pxだけ長くなる)
div.style.marginTop = '3px'
// 「書き方」のマウスオーバーを拝借
div.className = 'editorMarkdown_help'
if (enable_mouseover) {
div.style.visibility = 'hidden'
}
div.addEventListener('click', copy_code)
// 先に宣言しないと登録できないので、関数定義
// コピー関数
var copy_code = function() {
if (!document.queryCommandSupported('copy')) {
// Firefox40までは未実装でもtrueが来てしまう
alert('copyに対応していません')
return
}
// コピー対象を選択状態にする
var range = document.createRange();
range.selectNode(get_last_html_node(this.parentNode));
window.getSelection().addRange(range);
try {
document.execCommand('copy')
} catch (e) {
// 非対応なら例外が出る Firefox40までなど
alert('copyに失敗しました')
}
// 選択を解除する
window.getSelection().removeAllRanges();
}
// 空のテキストノードを含まないchildrenからラストノードを取得するヘルパー
var get_last_html_node = function (node) {
return node.children[node.children.length - 1]
}
// マウスオーバー処理有効時のみ宣言
if (enable_mouseover) {
var show_button = function() {
get_last_html_node(this).previousSibling.style.visibility = ''
}
var hide_button = function() {
get_last_html_node(this).previousSibling.style.visibility = 'hidden'
}
}
// 全 ``` 記法にコピーボタンをつける(``は対象外)
var code_frames = document.getElementsByClassName('code-frame')
Array.prototype.forEach.call(code_frames, function(item) {
// クローンしないと最後のitemにしか追加されない。trueでアイコンまでクローン
var div_clone = div.cloneNode(true)
// cloneしたものにイベントを登録する必要がある
div_clone.addEventListener('click', copy_code)
// childNodes first・lastChildはtextノードも含み、挿入場所が末尾になる場合があるので、childrenから取得
item.insertBefore(div_clone, get_last_html_node(item))
if(enable_mouseover) {
item.addEventListener('mouseover', show_button)
item.addEventListener('mouseout', hide_button)
}
})
自分用(不要なコメント追加)
// ==UserScript==
// @name Qiita code copy button
// @namespace khsk
// @description コードをコピーするボタンを追加する
// @include http://qiita.com/*/items/*
// @version 1
// @grant none
// ==/UserScript==
const OFF = 0
const ON = 1
// マウスオーバー時のみボタンを表示 ただし表示時にアイコン分下にずれる(OFFなら常にずれている)
var enable_mouseover = ON
// インターフェース作成
var i = document.createElement('i')
i.className = 'fa fa-clipboard'
var div = document.createElement('div')
div.appendChild(i)
// クラスによるfloatを解除
div.style.float = 'none'
// クラスによるフォントの拡大を解除
div.style.fontSize = 'initial'
// コード内かつコード名の右側へ
div.style.display = 'inline-block'
// 上に張り付いているので少し離す(本来よりn pxだけ長くなる)
div.style.marginTop = '3px'
// 「書き方」のマウスオーバーを拝借
div.className = 'editorMarkdown_help'
// notice 解除するぐらいなら必要なcssのみ手動でコピーした方が安心しそう
if (enable_mouseover) {
// displayではボタン分のスペースを確保しないのでvisibilityで。
// displayではマウスオーバー・アウトでガタガタになる。(marginを設定しないならば名前付きはずれない)
//div.style.display = 'none'
div.style.visibility = 'hidden'
}
div.addEventListener('click', copy_code)
// 先に宣言しないと登録できないので、関数定義
// コピー関数
var copy_code = function() {
if (!document.queryCommandSupported('copy')) {
// Firefox40までは未実装でもtrueが来てしまう
alert('copyに対応していません')
return
}
// コピー対象を選択状態にする
var range = document.createRange();
range.selectNode(get_last_html_node(this.parentNode));
window.getSelection().addRange(range);
try {
document.execCommand('copy')
} catch (e) {
// 非対応なら例外が出る Firefox40までなど
alert('copyに失敗しました')
}
// 選択を解除する
window.getSelection().removeAllRanges();
}
// 空のテキストノードを含まないchildrenからラストノードを取得するヘルパー
var get_last_html_node = function (node) {
return node.children[node.children.length - 1]
}
// マウスオーバー処理有効時のみ宣言
if (enable_mouseover) {
var show_button = function() {
//get_last_html_node(this).previousSibling.style.display = 'inline-block'
get_last_html_node(this).previousSibling.style.visibility = ''
}
var hide_button = function() {
//get_last_html_node(this).previousSibling.style.display = 'none'
get_last_html_node(this).previousSibling.style.visibility = 'hidden'
}
}
// 全 ``` 記法にコピーボタンをつける(``は対象外)
var code_frames = document.getElementsByClassName('code-frame')
/* Chromeでは Array.forEachに対応していない http://ptech.g.hatena.ne.jp/noromanba/20120521/1337639496
Array.forEach(code_frames, function(item) {
// クローンしないと最後のitemにしか追加されない。trueでアイコンまでクローン
var div_clone = div.cloneNode(true)
// cloneしたものにイベントを登録する必要がある
div_clone.addEventListener('click', copy_code)
// childNodes first・lastChildはtextノードも含み、挿入場所が末尾になる場合があるので、childrenから取得
item.insertBefore(div_clone, get_last_html_node(item))
if(enable_mouseover) {
item.addEventListener('mouseover', show_button)
item.addEventListener('mouseout', hide_button)
}
})
*/
Array.prototype.forEach.call(code_frames, function(item) {
// クローンしないと最後のitemにしか追加されない。trueでアイコンまでクローン
var div_clone = div.cloneNode(true)
// cloneしたものにイベントを登録する必要がある
div_clone.addEventListener('click', copy_code)
// childNodes first・lastChildはtextノードも含み、挿入場所が末尾になる場合があるので、childrenから取得
item.insertBefore(div_clone, get_last_html_node(item))
if(enable_mouseover) {
item.addEventListener('mouseover', show_button)
item.addEventListener('mouseout', hide_button)
}
})
その他
徘徊したら
Qiitaの記事内のコード部分を楽にコピペするユーザースクリプト
もありました。こちらは見た感じコードがすごく綺麗なので、こっちにコピー機能を付け足した方がいいんじゃないでしょうか。
ボタンを隠す場合はvisibility = 'hidden'
にしています。
display = 'none'
の場合は、マウスオーバー時にアイコン分の幅が新たに確保され、ガタッとなります。
ただし、隠している際は余分な幅を確保しないので、適用前と同じレイアウトで閲覧できます。
そちらが良ければ、切り替えは個人用コードのコメントをいじって貰えれば…
個人的にはボタンは常に表示でいいと思うのですが、マウスオーバーまで隠している例が多い気がします。
隠すなら隠すで、レイアウトの邪魔にならない部分に表示するようできればよかったのですが、いかんせん技量やアイデア不足です…