# 【令和最新版】右クリック禁止解除スクリプト（ブックマークレット）

## TL;DR
これを使うと右クリック禁止解除できる

```javascript
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);
}();
```

---

世間では、右クリック禁止を解除するブックマークレットが出回っている。以下のようなものだ。

```javascript
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;';
    }
}();
```

ソースは以下。

https://blog.z0i.net/2016/01/right-click.html

このサイトで参考とされているのはこれ（リンク切れ）

https://kanonji.info/blog/2016/01/18/bookmarklet-removes-all-event-listeners/

とこれ。

https://stabucky.com/wp/archives/3668

## 動作原理
このコードは、主要な右クリック禁止方法である**CSSによる指定**、**JavaScriptによるコードの書き換え**の2つに対応している。

### CSSによる指定
CSSで要素に`user-select`や`-moz-user-select`などのセレクタを指定すると、その要素の右クリックを禁止できる。`b`はそれを列挙する関数である。

そこで上記コードの以下の部分では、`Array.prototype.forEach.call`で`a.all`（documentのすべての子要素）に対して、すべての`b`の全セレクタをの値を`auto`に設定している。
```javascript
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`を返すと、そこでイベントが停止するようになっている。だからブラウザの規定の動作が呼ばれず、選択ができなくなるというわけだ。

```javascript
Array.prototype.forEach.call(a.all, function(a) {
    ...,
    a.onmousedown = a.onmousemove = a.onmouseup = a.onselectstart = ""
}), ...
```
この部分では、まず各要素の`onmousedown`などのイベントハンドラに空文字を代入し、何もしないようにしている。

また、この処理

```javascript
a.onmousedown = a.onmousemove = a.onmouseup = a.onselectstart = a.oncontextmenu = a.body.oncontextmenu = f;
```
では、最初に定義した`a`（つまり`document`であり、`forEach`内の`a`とは異なる）の各イベントハンドラを、最初に定義した`true`を返すだけの関数である`f`に置き換えている。

最後に

```javascript
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
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
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);
}();
```

このコードは

```javascript
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](https://creativecommons.org/publicdomain/zero/1.0/deed.en)で自己責任で利用してください。"AS IS"ってやつですね。

---

- タイトル: 【令和最新版】右クリック禁止解除スクリプト（ブックマークレット）
- 著者: 千本槍みなも@ナタクラゲ
- 作成年: 2025
- URL: https://blog.natakurage.cc/articles/enable-right-click
- ライセンス: CC BY-SA 4.0
