Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save lunamoth/7fcefe5ccd97240ce3a2efe35de7629e to your computer and use it in GitHub Desktop.

Select an option

Save lunamoth/7fcefe5ccd97240ce3a2efe35de7629e to your computer and use it in GitHub Desktop.
// ==UserScript==
// @name 모든 사이트 우클릭/드래그/선택 차단 해제
// @name:en Enable Right-Click, Drag, and Select for All Sites
// @namespace http://tampermonkey.net/
// @version 1.5
// @description 웹사이트에서 금지된 마우스 오른쪽 클릭, 드래그, 텍스트 선택을 여러 기법을 통해 종합적으로 해제합니다.
// @description:en Comprehensive script to re-enable right-click, drag, and text selection on websites using multiple techniques.
// @author Gemini
// @match *://*/*
// @grant GM_addStyle
// @run-at document-start
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// 1. CSS를 이용한 선택/드래그 방지 무력화 (가장 먼저 실행)
// !important를 사용하여 다른 스타일 규칙을 덮어씁니다.
GM_addStyle(`
*, html, body, div, p, span, a {
-webkit-user-select: auto !important;
-moz-user-select: auto !important;
-ms-user-select: auto !important;
user-select: auto !important;
-webkit-user-drag: auto !important;
-moz-user-drag: auto !important;
-ms-user-drag: auto !important;
user-drag: auto !important;
touch-action: auto !important;
}
`);
// 2. 이벤트 리스너 차단 (가장 핵심적인 부분)
// 이벤트의 '캡처링' 단계에서 먼저 실행하여 후속 이벤트를 중단시킵니다.
const eventsToStop = [
'contextmenu', // 우클릭 메뉴
'selectstart', // 텍스트 선택 시작
'dragstart', // 드래그 시작
'mousedown', // 마우스 버튼 누르기 (일부 사이트는 이걸로 선택 막음)
'keydown', // 키보드 입력 (F12, Ctrl+Shift+I 등 개발자 도구 방지)
'copy', // 복사
'cut', // 잘라내기
'paste' // 붙여넣기
];
// 키보드 이벤트의 경우, 특정 키만 차단하는 로직을 무력화하기 위함
// 예를 들어, event.ctrlKey && event.keyCode == 'C' 와 같은 조건을 막습니다.
const keyEventsToAllow = ['keydown', 'keypress', 'keyup'];
const stopPropagation = e => {
e.stopImmediatePropagation();
};
const allowEvent = e => {
// 일부 사이트는 이벤트를 아예 막는 대신, 기본 동작을 막습니다.
// 이 경우, stopImmediatePropagation()만으로는 부족할 수 있어,
// 이벤트 전파를 허용하되, 기본 동작 방지를 막는 함수를 재정의합니다.
// 이것은 매우 강력하지만, 일부 사이트 기능을 망가뜨릴 수 있어 주석 처리합니다.
// 필요한 경우 주석을 해제하여 사용하세요.
/*
if (typeof e.preventDefault.isHijacked === 'undefined') {
const originalPreventDefault = e.preventDefault;
e.preventDefault = function() {
console.log('preventDefault() is blocked by userscript.');
};
e.preventDefault.isHijacked = true;
}
*/
return true;
};
eventsToStop.forEach(eventName => {
window.addEventListener(eventName, stopPropagation, true); // 캡처링 단계에서 실행
});
keyEventsToAllow.forEach(eventName => {
window.addEventListener(eventName, allowEvent, true);
});
// 3. HTML 인라인 속성 및 자바스크립트 속성 제거
// 페이지 로딩 후 및 동적으로 생성되는 요소에 대해 주기적으로 검사 및 제거합니다.
const cleanupAttributes = (targetNode = document) => {
const elements = targetNode.querySelectorAll('*');
const attributesToRemove = [
'oncontextmenu', 'onselectstart', 'ondragstart', 'onmousedown',
'onkeydown', 'oncopy', 'oncut'
];
elements.forEach(el => {
attributesToRemove.forEach(attr => {
if (el.hasAttribute(attr)) {
el.removeAttribute(attr);
}
// JavaScript로 직접 할당된 속성도 null로 만듭니다.
if (el[attr]) {
el[attr] = null;
}
});
// 일부 라이브러리는 style 속성으로 직접 user-select를 설정하기도 합니다.
if (el.style.userSelect === 'none') {
el.style.userSelect = 'auto';
}
});
};
// 최초 페이지 로딩 시 실행
window.addEventListener('DOMContentLoaded', () => {
cleanupAttributes();
});
// 4. 동적으로 추가되는 콘텐츠에 대한 처리 (MutationObserver)
// AJAX나 SPA(Single Page Application) 환경에서 새로 생긴 요소도 처리합니다.
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1) { // ELEMENT_NODE
cleanupAttributes(node);
}
});
});
});
observer.observe(document.documentElement, {
childList: true, // 자식 노드의 추가/제거 감지
subtree: true // 모든 하위 노드 감지
});
// 5. 일부 사이트에서 사용하는 이벤트 리스너 덮어쓰기 방지 (고급 기법)
// EventTarget.prototype.addEventListener를 재정의하여 특정 이벤트 리스너 등록을 막습니다.
const originalAddEventListener = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener, options) {
if (eventsToStop.includes(type)) {
console.log(`[Userscript] Blocked an attempt to add a '${type}' event listener.`);
return; // 차단하려는 이벤트 리스너의 등록을 아예 막아버림
}
originalAddEventListener.call(this, type, listener, options);
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment