右クリック、文字選択コピー禁止を解除するブックマークレット(をChatGPTを利用して作った話)

2023年8月11日金曜日

ChatGPT 文章生成AI

t f B! P L

使い方

以下のコードをブックマークレットとして保存。chromeだとブックマークバーを右クリックしてページを追加、もしくはブックマークマネージャを開いて右クリックから新しいブックマークを追加で保存できます。(2023-08-13: 更新)

javascript:(function(){var clone=document.body.cloneNode(true);document.body.parentElement.replaceChild(clone,document.body);document.addEventListener('contextmenu',function(e){e.stopImmediatePropagation()},true);document.addEventListener('selectstart',function(e){e.stopImmediatePropagation()},true);document.addEventListener('keydown',function(e){e.stopImmediatePropagation()},true);document.addEventListener('mousedown',function(e){e.stopImmediatePropagation()},true);const allElements=document.getElementsByTagName('*');for(const element of allElements){element.style.userSelect='auto';element.style.webkitUserSelect='auto';element.style.mozUserSelect='auto';element.style.msUserSelect='auto';element.style.oUserSelect='auto';}const elementsWithAttributes=document.querySelectorAll('[onselectstart], [oncontextmenu], [onmousedown]');for(const element of elementsWithAttributes){element.removeAttribute('onselectstart');element.removeAttribute('oncontextmenu');element.removeAttribute('onmousedown');}})();

画像のようにURLを入力する欄にコードを貼り付けてください。

右クリック禁止などを解除したいページ上でブックマークレットを開くと右クリックなどができるようになります。

すべてのウェブサイトで確実に機能するわけではありません。ご了承ください。

ここまでお読みいただきありがとうございました。

ChatGPTを利用してブックマークレットを作る

動機

単語をドラッグして右クリックで調べようと思ったら右クリックができなかった。悲しい。解除できるブックマークレットを作ろう!

日本語でChatGPT3.5を使います。javascriptなんもわからん。

まずは雑に依頼

入力

以下の要件に合うブックマークレットのコードを書いて
# 要件
* 右クリック禁止を解除する
* dom, javascript, cssの制御による右クリック禁止も解除

結果

javascript:(function() {
    const script = document.createElement('script');
    script.textContent = '(' + function() {
        // 解除対象の右クリック禁止要素を取得する関数
        function findForbiddenElements() {
            const forbiddenElements = [];

            // ドキュメント内の要素を全て取得
            const allElements = document.getElementsByTagName('*');
            
            // 右クリック禁止属性を持つ要素を検索
            for (const element of allElements) {
                const rightClickAttr = element.getAttribute('oncontextmenu');
                if (rightClickAttr !== null && rightClickAttr.trim() !== '') {
                    forbiddenElements.push(element);
                }
            }

            return forbiddenElements;
        }

        // 右クリックイベントを削除して禁止を解除
        function removeContextMenu(event) {
            event.preventDefault();
        }

        // 右クリック禁止を解除する
        function unblockRightClick() {
            const forbiddenElements = findForbiddenElements();

            for (const element of forbiddenElements) {
                element.removeAttribute('oncontextmenu');
                element.removeEventListener('contextmenu', removeContextMenu);
            }
        }

        // 実行
        unblockRightClick();

        // domの変更を監視して追加された右クリック禁止を解除
        const observer = new MutationObserver(function(mutations) {
            for (const mutation of mutations) {
                if (mutation.addedNodes) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            const rightClickAttr = node.getAttribute('oncontextmenu');
                            if (rightClickAttr !== null && rightClickAttr.trim() !== '') {
                                node.removeAttribute('oncontextmenu');
                                node.removeEventListener('contextmenu', removeContextMenu);
                            }
                        }
                    }
                }
            }
        });

        // domの変更を監視
        observer.observe(document, { childList: true, subtree: true });

    } + ')();';
    document.body.appendChild(script);
})();

なんか無駄に長すぎる気がするけどまずは動作確認。右クリックが使えないサイト上でデベロッパーツールを開き(CTRL + SHIFT + I)、Console上でコードをコピペして実行。

(そのサイトでは)右クリックでコンテキストメニューが出たけど文字列をドラッグできない。ドラッグ禁止解除は指定していないから当然。ChatGPTに修正依頼を出します。

文字列選択禁止を追加

入力

文字列の選択禁止の解除を要件に追加してコードを書いて

結果

javascript:(function() {
    const script = document.createElement('script');
    script.textContent = '(' + function() {
        // 解除対象の右クリック禁止要素を取得する関数
        function findForbiddenElements() {
            const forbiddenElements = [];

            // ドキュメント内の要素を全て取得
            const allElements = document.getElementsByTagName('*');
            
            // 右クリック禁止属性を持つ要素を検索
            for (const element of allElements) {
                const rightClickAttr = element.getAttribute('oncontextmenu');
                if (rightClickAttr !== null && rightClickAttr.trim() !== '') {
                    forbiddenElements.push(element);
                }
            }

            return forbiddenElements;
        }

        // 右クリックイベントを削除して禁止を解除
        function removeContextMenu(event) {
            event.preventDefault();
        }

        // 選択禁止を解除する
        function unblockTextSelection() {
            const styles = document.styleSheets;

            for (const style of styles) {
                const rules = style.cssRules || style.rules;
                if (rules) {
                    for (const rule of rules) {
                        if (rule.style) {
                            rule.style.userSelect = 'auto';
                            rule.style.webkitUserSelect = 'auto';
                            rule.style.mozUserSelect = 'auto';
                            rule.style.msUserSelect = 'auto';
                            rule.style.oUserSelect = 'auto';
                        }
                    }
                }
            }
        }

        // 右クリック禁止を解除する
        function unblockRightClick() {
            const forbiddenElements = findForbiddenElements();

            for (const element of forbiddenElements) {
                element.removeAttribute('oncontextmenu');
                element.removeEventListener('contextmenu', removeContextMenu);
            }
        }

        // 実行
        unblockRightClick();
        unblockTextSelection();

        // domの変更を監視して追加された右クリック禁止を解除
        const observer = new MutationObserver(function(mutations) {
            for (const mutation of mutations) {
                if (mutation.addedNodes) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            const rightClickAttr = node.getAttribute('oncontextmenu');
                            if (rightClickAttr !== null && rightClickAttr.trim() !== '') {
                                node.removeAttribute('oncontextmenu');
                                node.removeEventListener('contextmenu', removeContextMenu);
                            }
                        }
                    }
                }
            }
        });

        // domの変更を監視
        observer.observe(document, { childList: true, subtree: true });

    } + ')();';
    document.body.appendChild(script);
})();

コンソール入力してドラッグテスト!出来ない!やり直し!

文字列選択禁止の解除を少し詳細に追加

それっぽいこと言えばいい感じにしてくれないかなという思いで入力。

文字列の選択禁止を解除する部分ができていなかったのでdom, javascript, cssに作用するコードでもう一度書いて

結果

javascript:(function() {
    const script = document.createElement('script');
    script.textContent = '(' + function() {
        // 解除対象の右クリック禁止要素を取得する関数
        function findForbiddenElements() {
            const forbiddenElements = [];

            // ドキュメント内の要素を全て取得
            const allElements = document.getElementsByTagName('*');
            
            // 右クリック禁止属性を持つ要素を検索
            for (const element of allElements) {
                const rightClickAttr = element.getAttribute('oncontextmenu');
                if (rightClickAttr !== null && rightClickAttr.trim() !== '') {
                    forbiddenElements.push(element);
                }
            }

            return forbiddenElements;
        }

        // 右クリックイベントを削除して禁止を解除
        function removeContextMenu(event) {
            event.preventDefault();
        }

        // 選択禁止を解除する
        function unblockTextSelection() {
            const styles = document.styleSheets;

            for (const style of styles) {
                const rules = style.cssRules || style.rules;
                if (rules) {
                    for (const rule of rules) {
                        if (rule.style) {
                            rule.style.userSelect = 'auto';
                            rule.style.webkitUserSelect = 'auto';
                            rule.style.mozUserSelect = 'auto';
                            rule.style.msUserSelect = 'auto';
                            rule.style.oUserSelect = 'auto';
                        }
                    }
                }
            }

            // インラインスタイルを持つ要素も選択禁止を解除
            const elementsWithInlineStyles = document.querySelectorAll('[style*="user-select"]');
            for (const element of elementsWithInlineStyles) {
                element.style.userSelect = 'auto';
                element.style.webkitUserSelect = 'auto';
                element.style.mozUserSelect = 'auto';
                element.style.msUserSelect = 'auto';
                element.style.oUserSelect = 'auto';
            }
        }

        // 右クリック禁止を解除する
        function unblockRightClick() {
            const forbiddenElements = findForbiddenElements();

            for (const element of forbiddenElements) {
                element.removeAttribute('oncontextmenu');
                element.removeEventListener('contextmenu', removeContextMenu);
            }
        }

        // 実行
        unblockRightClick();
        unblockTextSelection();

        // domの変更を監視して追加された右クリック禁止を解除
        const observer = new MutationObserver(function(mutations) {
            for (const mutation of mutations) {
                if (mutation.addedNodes) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            const rightClickAttr = node.getAttribute('oncontextmenu');
                            if (rightClickAttr !== null && rightClickAttr.trim() !== '') {
                                node.removeAttribute('oncontextmenu');
                                node.removeEventListener('contextmenu', removeContextMenu);
                            }

                            // インラインスタイルを持つ要素も選択禁止を解除
                            const inlineStyles = node.querySelectorAll('[style*="user-select"]');
                            for (const inlineElement of inlineStyles) {
                                inlineElement.style.userSelect = 'auto';
                                inlineElement.style.webkitUserSelect = 'auto';
                                inlineElement.style.mozUserSelect = 'auto';
                                inlineElement.style.msUserSelect = 'auto';
                                inlineElement.style.oUserSelect = 'auto';
                            }
                        }
                    }
                }
            }
        });

        // domの変更を監視
        observer.observe(document, { childList: true, subtree: true });

    } + ')();';
    document.body.appendChild(script);
})();

うまくいくサイトもあったが、ほとんどはダメ。少し真面目に考えることにしよう。

デベロッパーツールを開く。運良く右クリックだけは効くようになったので該当部分で右クリックして検証(inspect)。Stylesを見てuser-selectや-webkit-user-selectがnoneになっていないか確認。なってたらautoにする。まだドラッグできない。

htmlを見る。divの属性にonselectstart="return false"があったので削除してみる。動きそう。

onselectstart属性を削除するコードを依頼

入力

上記のコードは文字列の禁止に関して、htmlのタグにonselectstartがある場合に対応できていませんでした。タグにあるonselectstartを削除も追加して書き直してください

結果

javascript:(function() {
    const script = document.createElement('script');
    script.textContent = '(' + function() {
        // 解除対象の右クリック禁止要素と選択禁止要素を取得する関数
        function findForbiddenElements() {
            const forbiddenElements = [];
            const forbiddenSelectElements = [];

            // ドキュメント内の要素を全て取得
            const allElements = document.getElementsByTagName('*');
            
            // 右クリック禁止属性と選択禁止属性を持つ要素を検索
            for (const element of allElements) {
                const rightClickAttr = element.getAttribute('oncontextmenu');
                const selectStartAttr = element.getAttribute('onselectstart');
                if (rightClickAttr !== null && rightClickAttr.trim() !== '') {
                    forbiddenElements.push(element);
                }
                if (selectStartAttr !== null && selectStartAttr.trim() !== '') {
                    forbiddenSelectElements.push(element);
                }
            }

            return { forbiddenElements, forbiddenSelectElements };
        }

        // 右クリックイベントを削除して禁止を解除
        function removeContextMenu(event) {
            event.preventDefault();
        }

        // 選択禁止を解除する
        function unblockTextSelection() {
            const styles = document.styleSheets;

            for (const style of styles) {
                const rules = style.cssRules || style.rules;
                if (rules) {
                    for (const rule of rules) {
                        if (rule.style) {
                            rule.style.userSelect = 'auto';
                            rule.style.webkitUserSelect = 'auto';
                            rule.style.mozUserSelect = 'auto';
                            rule.style.msUserSelect = 'auto';
                            rule.style.oUserSelect = 'auto';
                        }
                    }
                }
            }

            // インラインスタイルを持つ要素も選択禁止を解除
            const elementsWithInlineStyles = document.querySelectorAll('[style*="user-select"]');
            for (const element of elementsWithInlineStyles) {
                element.style.userSelect = 'auto';
                element.style.webkitUserSelect = 'auto';
                element.style.mozUserSelect = 'auto';
                element.style.msUserSelect = 'auto';
                element.style.oUserSelect = 'auto';
            }
        }

        // 右クリック禁止を解除する
        function unblockRightClick(forbiddenElements) {
            for (const element of forbiddenElements) {
                element.removeAttribute('oncontextmenu');
                element.removeEventListener('contextmenu', removeContextMenu);
            }
        }

        // 選択禁止を解除する
        function unblockSelectStart(forbiddenSelectElements) {
            for (const element of forbiddenSelectElements) {
                element.removeAttribute('onselectstart');
            }
        }

        // 実行
        const { forbiddenElements, forbiddenSelectElements } = findForbiddenElements();
        unblockRightClick(forbiddenElements);
        unblockSelectStart(forbiddenSelectElements);
        unblockTextSelection();

        // domの変更を監視して追加された右クリック禁止を解除
        const observer = new MutationObserver(function(mutations) {
            for (const mutation of mutations) {
                if (mutation.addedNodes) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            const rightClickAttr = node.getAttribute('oncontextmenu');
                            const selectStartAttr = node.getAttribute('onselectstart');
                            if (rightClickAttr !== null && rightClickAttr.trim() !== '') {
                                node.removeAttribute('oncontextmenu');
                                node.removeEventListener('contextmenu', removeContextMenu);
                            }
                            if (selectStartAttr !== null && selectStartAttr.trim() !== '') {
                                node.removeAttribute('onselectstart');
                            }

                            // インラインスタイルを持つ要素も選択禁止を解除
                            const inlineStyles = node.querySelectorAll('[style*="user-select"]');
                            for (const inlineElement of inlineStyles) {
                                inlineElement.style.userSelect = 'auto';
                                inlineElement.style.webkitUserSelect = 'auto';
                                inlineElement.style.mozUserSelect = 'auto';
                                inlineElement.style.msUserSelect = 'auto';
                                inlineElement.style.oUserSelect = 'auto';
                            }
                        }
                    }
                }
            }
        });

        // domの変更を監視
        observer.observe(document, { childList: true, subtree: true });

    } + ')();';
    document.body.appendChild(script);
})();

試す。エラー。Uncaught DOMException: Failed to read the ‘cssRules’ property from ‘CSSStyleSheet’: Cannot access rules。

エラー箇所のコードを見てみる。

const styles = document.styleSheets;

for (const style of styles) {
    const rules = style.cssRules || style.rules;

エラー内容でググったりコンソールにdocument.styleSheets[0]などと入れていくとfonts.googleapis.comなどの外部cssのcssRulesにアクセスできない場合にエラーが出ることがわかった。

エラーの内容で修正してもらう

エラー内容を含めることでなんとかならないかと雑に期待。

入力

Uncaught DOMException: Failed to read the 'cssRules' property from 'CSSStyleSheet': Cannot access rulesというエラーが出た。対応して書き直してください

結果

申し訳ありません、ブラウザのセキュリティ上の制約により、クロスオリジンのスタイルシートに対してはcssRules プロパティにアクセスできないため、エラーが発生しました。代わりに、document.styleSheets で取得したスタイルシートの cssRules プロパティへのアクセスを試行する前に、各スタイルシートがクロスオリジンでないことを確認する必要があります。

以下の修正済みのコードは、クロスオリジンのスタイルシートにはアクセスせずに選択禁止を解除します。
(以下略)

ダメです。同じエラーが出た。修正できとらんやんけ。

例外処理を依頼

外部のcssはきっと右クリックやドラッグの制御と関係ないだろう。例外処理で無視することにする。

入力

上記のコードはクロスオリジンのスタイルシートに対応できていませんでした。cssRulesを取得する部分に関して、例外処理を用いて書き直してください

結果

javascript:(function() {
    const script = document.createElement('script');
    script.textContent = '(' + function() {
        // 解除対象の右クリック禁止要素と選択禁止要素を取得する関数
        function findForbiddenElements() {
            const forbiddenElements = [];
            const forbiddenSelectElements = [];

            // ドキュメント内の要素を全て取得
            const allElements = document.getElementsByTagName('*');
            
            // 右クリック禁止属性と選択禁止属性を持つ要素を検索
            for (const element of allElements) {
                const rightClickAttr = element.getAttribute('oncontextmenu');
                const selectStartAttr = element.getAttribute('onselectstart');
                if (rightClickAttr !== null && rightClickAttr.trim() !== '') {
                    forbiddenElements.push(element);
                }
                if (selectStartAttr !== null && selectStartAttr.trim() !== '') {
                    forbiddenSelectElements.push(element);
                }
            }

            return { forbiddenElements, forbiddenSelectElements };
        }

        // 右クリックイベントを削除して禁止を解除
        function removeContextMenu(event) {
            event.preventDefault();
        }

        // 選択禁止を解除する
        function unblockTextSelection() {
            const styles = document.styleSheets;

            for (const style of styles) {
                try {
                    const rules = style.cssRules || style.rules;
                    if (rules) {
                        for (const rule of rules) {
                            if (rule.style) {
                                rule.style.userSelect = 'auto';
                                rule.style.webkitUserSelect = 'auto';
                                rule.style.mozUserSelect = 'auto';
                                rule.style.msUserSelect = 'auto';
                                rule.style.oUserSelect = 'auto';
                            }
                        }
                    }
                } catch (e) {
                    // クロスオリジンのスタイルシートは例外が発生するため無視
                }
            }

            // インラインスタイルを持つ要素も選択禁止を解除
            const elementsWithInlineStyles = document.querySelectorAll('[style*="user-select"]');
            for (const element of elementsWithInlineStyles) {
                element.style.userSelect = 'auto';
                element.style.webkitUserSelect = 'auto';
                element.style.mozUserSelect = 'auto';
                element.style.msUserSelect = 'auto';
                element.style.oUserSelect = 'auto';
            }
        }

        // 右クリック禁止を解除する
        function unblockRightClick(forbiddenElements) {
            for (const element of forbiddenElements) {
                element.removeAttribute('oncontextmenu');
                element.removeEventListener('contextmenu', removeContextMenu);
            }
        }

        // 選択禁止を解除する
        function unblockSelectStart(forbiddenSelectElements) {
            for (const element of forbiddenSelectElements) {
                element.removeAttribute('onselectstart');
            }
        }

        // 実行
        const { forbiddenElements, forbiddenSelectElements } = findForbiddenElements();
        unblockRightClick(forbiddenElements);
        unblockSelectStart(forbiddenSelectElements);
        unblockTextSelection();

        // domの変更を監視して追加された右クリック禁止を解除
        const observer = new MutationObserver(function(mutations) {
            for (const mutation of mutations) {
                if (mutation.addedNodes) {
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            const rightClickAttr = node.getAttribute('oncontextmenu');
                            const selectStartAttr = node.getAttribute('onselectstart');
                            if (rightClickAttr !== null && rightClickAttr.trim() !== '') {
                                node.removeAttribute('oncontextmenu');
                                node.removeEventListener('contextmenu', removeContextMenu);
                            }
                            if (selectStartAttr !== null && selectStartAttr.trim() !== '') {
                                node.removeAttribute('onselectstart');
                            }

                            // インラインスタイルを持つ要素も選択禁止を解除
                            const inlineStyles = node.querySelectorAll('[style*="user-select"]');
                            for (const inlineElement of inlineStyles) {
                                inlineElement.style.userSelect = 'auto';
                                inlineElement.style.webkitUserSelect = 'auto';
                                inlineElement.style.mozUserSelect = 'auto';
                                inlineElement.style.msUserSelect = 'auto';
                                inlineElement.style.oUserSelect = 'auto';
                            }
                        }
                    }
                }
            }
        });

        // domの変更を監視
        observer.observe(document, { childList: true, subtree: true });

    } + ')();';
    document.body.appendChild(script);
})();

コンソールで実行。動いた!ドラッグも右クリックもできて検索やコピーができる!めでたしめでたし!完!劇終!

うまく動かないサイトがあった。

イベントリスナーを調べる

htmlとcssの問題ではなさそう。デベロッパーツールを開きElementsタブで適当なタグを選択してEvent Listenersを見る。selectstart, copy, cut, mousedown, contextmenuにイベントリスナーが登録されていた。ソースを見てみる。

e.preventDefault();
return false;

イベントを握りつぶされている。デベロッパーツールはイベントリスナーを消すことができるので、取り除いて挙動を確かめると、文字列を選択してコピーすることができた。あとはこれをブックマークレットで使えるように表現すればよい。

addEventListenerで登録しているのだからremoveEventListenerを使えばいいと思っていた時期がありました。だがremoveEventListenerは登録した関数を知らないと使えない。getEventListenersで得られると思ったがこれはデベロッパーツールでしか動かずブックマークレットとしては不適。

ググるとJavascript/DOM: How to remove all event listeners of a DOM object?var clone = element.cloneNode(true);とすればいいとあった。stackoverflowは頼りになります。

cloneNodeとreplaceChildでイベントリスナーを消す

入力。cloneして入れ替えるコードを直接指定した。

上記のコードのではcontextmenu, copy, cut, selectstartにeventlistenerが設定されている要素がある場合に対応できませんでした。
eventlistenerを削除するために
var clone = document.body.cloneNode(true);
document.body.parentElement.replaceChild(clone, document.body);
を先頭に追加してください

結果

// イベントリスナーをクローンを使って置き換える
function replaceEventListeners(oldElement, newElement) {
    const events = ['contextmenu', 'copy', 'cut', 'selectstart'];

    for (const event of events) {
        const oldListener = oldElement[event];
        if (oldListener) {
            newElement[event] = oldListener;
            oldElement[event] = null;
        }
    }
}
(略)
// イベントリスナーをクローンを使って置き換える
const clonedNode = clone.querySelector(`[data-uid="${node.getAttribute('data-uid')}"]`);
if (clonedNode) {
    replaceEventListeners(node, clonedNode);
}

ただ指定した2行を先頭に追加してほしかったのによくわからないコードを出してきた挙げ句うまく動かなかった。

書き直し

ChatGPTに注文したり調べたりするうちに何をすればいいかなんとなく分かってきたので書き直してもらう。

入力

右クリック禁止を解除するブックマークレットを要件に従って書いてください
# 要件
* イベントリスナーを解除するためにコード1のコードを先頭に配置
* htmlのすべての要素に対してuser-selectをautoに変更
* htmlの属性にあるonselectstart, oncontextmenuの削除


# コード1
var clone = document.body.cloneNode(true);
document.body.parentElement.replaceChild(clone, document.body);

結果

javascript:(function() {
    // コード1: イベントリスナーを解除するためにクローンを使って置き換える
    var clone = document.body.cloneNode(true);
    document.body.parentElement.replaceChild(clone, document.body);

    // すべての要素のuser-selectをautoに変更
    const allElements = document.getElementsByTagName('*');
    for (const element of allElements) {
        element.style.userSelect = 'auto';
        element.style.webkitUserSelect = 'auto';
        element.style.mozUserSelect = 'auto';
        element.style.msUserSelect = 'auto';
        element.style.oUserSelect = 'auto';
    }

    // htmlの属性にあるonselectstartとoncontextmenuを削除
    const elementsWithAttributes = document.querySelectorAll('[onselectstart], [oncontextmenu]');
    for (const element of elementsWithAttributes) {
        element.removeAttribute('onselectstart');
        element.removeAttribute('oncontextmenu');
    }

    // domの変更を監視して追加された右クリック禁止を解除
    const observer = new MutationObserver(function(mutations) {
        for (const mutation of mutations) {
            if (mutation.addedNodes) {
                for (const node of mutation.addedNodes) {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        const rightClickAttr = node.getAttribute('oncontextmenu');
                        const selectStartAttr = node.getAttribute('onselectstart');
                        if (rightClickAttr !== null && rightClickAttr.trim() !== '') {
                            node.removeAttribute('oncontextmenu');
                        }
                        if (selectStartAttr !== null && selectStartAttr.trim() !== '') {
                            node.removeAttribute('onselectstart');
                        }

                        // インラインスタイルを持つ要素も選択禁止を解除
                        const inlineStyles = node.querySelectorAll('[style*="user-select"]');
                        for (const inlineElement of inlineStyles) {
                            inlineElement.style.userSelect = 'auto';
                            inlineElement.style.webkitUserSelect = 'auto';
                            inlineElement.style.mozUserSelect = 'auto';
                            inlineElement.style.msUserSelect = 'auto';
                            inlineElement.style.oUserSelect = 'auto';
                        }
                    }
                }
            }
        }
    });

    // domの変更を監視
    observer.observe(document, { childList: true, subtree: true });

})();

コードがシンプルになり、文字列選択と右クリックが効くようになった。

役に立っているかわからない部分を削除

最初から気になっていたobserberの部分を消す。無くても動きそう。

入力

domの変更監視部分を削除してください

結果

javascript:(function() {
    // コード1: イベントリスナーを解除するためにクローンを使って置き換える
    var clone = document.body.cloneNode(true);
    document.body.parentElement.replaceChild(clone, document.body);

    // すべての要素のuser-selectをautoに変更
    const allElements = document.getElementsByTagName('*');
    for (const element of allElements) {
        element.style.userSelect = 'auto';
        element.style.webkitUserSelect = 'auto';
        element.style.mozUserSelect = 'auto';
        element.style.msUserSelect = 'auto';
        element.style.oUserSelect = 'auto';
    }

    // htmlの属性にあるonselectstartとoncontextmenuを削除
    const elementsWithAttributes = document.querySelectorAll('[onselectstart], [oncontextmenu]');
    for (const element of elementsWithAttributes) {
        element.removeAttribute('onselectstart');
        element.removeAttribute('oncontextmenu');
    }
})();

見やすく、わかりやすくなった。ちゃんと動く。あとはブックマークレットで使えるようにコメントや改行を適当に削除。

javascript:(function(){var clone=document.body.cloneNode(true);document.body.parentElement.replaceChild(clone,document.body);const allElements=document.getElementsByTagName('*');for(const element of allElements){element.style.userSelect='auto';element.style.webkitUserSelect='auto';element.style.mozUserSelect='auto';element.style.msUserSelect='auto';element.style.oUserSelect='auto';}const elementsWithAttributes=document.querySelectorAll('[onselectstart], [oncontextmenu], [onmousedown]');for(const element of elementsWithAttributes){element.removeAttribute('onselectstart');element.removeAttribute('oncontextmenu');element.removeAttribute('onmousedown');}})();

ヨシッ!

documentにイベントリスナーがあったとき

個人的な目的は達したが、調べていくうちにまだ右クリックが効かないページがあった。documentに直接イベントリスナーを登録している場合が抜けていた。

document.addEventListener('contextmenu', function(e){e.stopImmediatePropagation()}, true);を追加する。

javascript:(function(){var clone=document.body.cloneNode(true);document.body.parentElement.replaceChild(clone,document.body);document.addEventListener('contextmenu',function(e){e.stopImmediatePropagation()},true);const allElements=document.getElementsByTagName('*');for(const element of allElements){element.style.userSelect='auto';element.style.webkitUserSelect='auto';element.style.mozUserSelect='auto';element.style.msUserSelect='auto';element.style.oUserSelect='auto';}const elementsWithAttributes=document.querySelectorAll('[onselectstart], [oncontextmenu], [onmousedown]');for(const element of elementsWithAttributes){element.removeAttribute('onselectstart');element.removeAttribute('oncontextmenu');element.removeAttribute('onmousedown');}})();

感想

要件並べて注文するとそれっぽいコードが出力される。それをとっかかりにして調整を加えていくと動くものが作れた。雑に注文して作り直しを遠慮なく要求できるのはChatGPTの利点。

一方で要所で具体的な指定ができないと役に立たないものが出てくるというわかりきった経験が得られた。検索や調査も必要。

あまり得意じゃない言語でもそれなりのスピードでコードが書ける興味深い体験でした。

QooQ