【令和最新版】右クリック禁止解除スクリプト(ブックマークレット)
目次
世間では、右クリック禁止を解除するブックマークレットが出回っている。以下のようなものだ。
javascript: ! function() {
var a = document,
b = ["userSelect", "khtmlUserSelect", "MozUserSelect", "MsUserSelect", "WebkitUserSelect"];
f = function() {
return true;
};
Array.prototype.forEach.call(a.all, function(a) {
a.style &&
b.forEach(function(b) {
a.style[b] = "auto"
}), a.onmousedown = a.onmousemove = a.onmouseup = a.onselectstart = ""
}), a.onmousedown = a.onmousemove = a.onmouseup = a.onselectstart = a.oncontextmenu = a.body.oncontextmenu = f;
obs = document.all;
for (i = 0; i < obs.length; i++) {
obs[i].oncontextmenu = 'return true;';
obs[i].onselectstart = 'return true;';
}
}();
ソースは以下。

右クリック禁止を無効に – ブログ運営のためのブログカスタマイズ
・知ってそうで知らないブックマークレットの仕組みと使い方(と便利な11点まとめ) | ライフハッカー[日本版]ブックマークのURLのところにjavascriptを書いておくと、そのブックマークを呼び出すとjavascriptが実行されるとい
このサイトで参考とされているのはこれ(リンク切れ)
とこれ。

右クリックと文字列選択を禁止する方法とそれを無効にするブックマークレット | You Look Too Cool
ウェブページの文字列をコピーされないようにする方法として、右クリックを禁止する方法と文字列の選択を禁止する方法が考えられます。

動作原理
このコードは、主要な右クリック禁止方法であるCSSによる指定、JavaScriptによるコードの書き換えの2つに対応している。
CSSによる指定
CSSで要素にuser-select
や-moz-user-select
などのセレクタを指定すると、その要素の右クリックを禁止できる。b
はそれを列挙する関数である。
そこで上記コードの以下の部分では、Array.prototype.forEach.call
でa.all
(documentのすべての子要素)に対して、すべてのb
の全セレクタをの値をauto
に設定している。
Array.prototype.forEach.call(a.all, function(a) {
a.style &&
b.forEach(function(b) {
a.style[b] = "auto"
}), ...
JavaScriptによるコードの書き換え
また、JavaScriptでは、要素でマウスを押下するとelement.onmousedown
が、マウスを動かすとelement.onmousemove
が、というようにイベントハンドラの関数が呼び出される。そして、この関数がfalse
を返すと、そこでイベントが停止するようになっている。だからブラウザの規定の動作が呼ばれず、選択ができなくなるというわけだ。
Array.prototype.forEach.call(a.all, function(a) {
...,
a.onmousedown = a.onmousemove = a.onmouseup = a.onselectstart = ""
}), ...
この部分では、まず各要素のonmousedown
などのイベントハンドラに空文字を代入し、何もしないようにしている。
また、この処理
a.onmousedown = a.onmousemove = a.onmouseup = a.onselectstart = a.oncontextmenu = a.body.oncontextmenu = f;
では、最初に定義したa
(つまりdocument
であり、forEach
内のa
とは異なる)の各イベントハンドラを、最初に定義したtrue
を返すだけの関数であるf
に置き換えている。
最後に
obs = document.all;
for (i = 0; i < obs.length; i++) {
obs[i].oncontextmenu = 'return true;';
obs[i].onselectstart = 'return true;';
}
ここで全要素のイベントハンドラoncontextmenu
とonselectstart
に文字列としての"return true"
を代入して終わり。
問題点
しかしこのコード、よくよく考えてみると不可解な点、改善できる点が多い。10年くらい前に書かれたコードっぽいので仕方ないのだが、複数の解除スクリプトを適当に並べただけ感もあり、あまり綺麗とはいえない。以下に列挙してみる。
varを使用している
const
を使いましょう。
document.all
を使用している
VSCodeに貼り付けて気づいたが、document.all
というプロパティは非推奨で、調べてみるとIE時代の亡霊らしい。IEはもうこの世にいないので、もっと新しい方法を使う。ここではdocument.querySelectorAll("*")
に置き換える。ついでにforEach
のやり方も少し変わる。
イベントハンドラに文字列を代入している
イベントハンドラに文字列を代入するのはeval
的で、セキュリティリスクがあるとされている。今回は任意のユーザ入力を扱うようなものではないが、なるべくならやめておきたい。
その他
obs
にvar
もlet
もconst
もない。obs
をなぜかa.all
ではなくdocument.all
と書き直している- ループ内で
onstartselect
を空文字にしたのに、最後に改めて'return true;'
を代入し直している - 区切りがコンマになっている部分がある。ブックマークレットならではの事情でもあるのかと思ったが、セミコロンの部分もある以上あまり関係ないと思われる
そもそも令和になっても右クリック禁止に効果があると思っているのが一番の不具合であるという点は置いておいて。
改善後のコード
リファクタリングした結果。
javascript:!function() {
const d = document;
const s = [
"userSelect",
"khtmlUserSelect",
"MozUserSelect",
"MsUserSelect",
"WebkitUserSelect"
];
const f = () => true;
d.querySelectorAll('*').forEach(e => {
e.style && s.forEach(b => {e.style[b] = "auto"});
e.onmousedown = e.onmousemove = e.onmouseup = e.onselectstart = e.oncontextmenu = f;
});
d.onmousedown = d.onmousemove = d.onmouseup = d.onselectstart = d.oncontextmenu = d.body.oncontextmenu = f;
}();
テストはあまりしていないので、性能が維持されているかどうかはわからない。少なくとも進化はしてないと思う。
もっとうまくできる?
今は全要素のイベントハンドラに() => true
を割り当てているが、これには問題があるかもしれない。アプリケーション固有のイベントハンドラを全て無視するということでもあるからだ。
代わりに、現在の動作をラップして実行し、返り値はtrue
であるような関数を作れば、元の動作を保ったまま右クリックを解放できるのではないか?
つまりこういうことだ。
javascript:!function() {
const d = document;
const s = [
"userSelect",
"khtmlUserSelect",
"MozUserSelect",
"MsUserSelect",
"WebkitUserSelect"
];
const f = (g) => {
if (typeof g !== 'function') {
return () => true;
}
return function(...args) {
g.apply(this, args);
return true;
};
};
d.querySelectorAll('*').forEach(e => {
e.style && s.forEach(b => {e.style[b] = "auto"});
e.onmousedown = f(e.onmousedown);
e.onmousemove = f(e.onmousemove);
e.onmouseup = f(e.onmouseup);
e.onselectstart = f(e.onselectstart);
e.oncontextmenu = f(e.oncontextmenu);
});
d.onmousedown = f(d.onmousedown);
d.onmousemove = f(d.onmousemove);
d.onmouseup = f(d.onmouseup);
d.onselectstart = f(d.onselectstart);
d.oncontextmenu = f(d.oncontextmenu);
d.body.oncontextmenu = f(d.body.oncontextmenu);
}();
このコードは
const f = (g) => {
if (typeof g !== 'function') {
return () => true;
}
return function(...args) {
g.apply(this, args);
return true;
};
};
この部分がキモで、この関数f
は、関数(かもしれない変数)g
を受け取り、g
を実行しつつもtrue
を返す新たな関数を作成する。もしg
が関数でなければ、true
を返すだけの関数を作る。
元の動作が失われると問題が出るようなサイトを見たことがないため、果たしてこのコードがうまく動くのかは分かりません。本記事のコードに関してはCC0で自己責任で利用してください。"AS IS"ってやつですね。