🛡️ 禁用浏览器复制功能完整指南
网页中禁用用户的复制功能,包括 CSS 方法、JavaScript 方法、综合解决方案以及实际应用场景。适用于需要保护内容版权、防止恶意爬取或提升用户体验的场景。
📋 目录
🚀 快速开始
如果您想快速实现基本的复制保护,可以直接使用以下代码:
<!DOCTYPE html>
<html>
<head>
<style>
/* 基础保护样式 */
.protected {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-touch-callout: none;
}
</style>
</head>
<body class="protected">
<script>
// 基础保护脚本
document.addEventListener("contextmenu", (e) => e.preventDefault());
document.addEventListener("keydown", (e) => {
if (e.ctrlKey && (e.keyCode === 67 || e.keyCode === 65)) {
e.preventDefault();
}
});
</script>
<h1>受保护的内容</h1>
<p>这里的内容无法被轻易复制</p>
</body>
</html>
🎨 CSS 方法
CSS 方法是最基础且高效的保护方式,通过样式属性来禁用用户的选择和交互行为。
1. 禁用文本选择
基础语法
/* 禁用所有文本选择 - 基础版本 */
.no-select {
-webkit-user-select: none; /* Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* 标准语法 */
}
/* 应用到整个页面 */
body {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
高级配置
/* 完整的文本选择保护 */
.protected-content {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
/* 移动端优化 */
-webkit-touch-callout: none; /* 禁用iOS Safari的长按菜单 */
-webkit-tap-highlight-color: transparent; /* 禁用点击高亮 */
-webkit-appearance: none; /* 移除默认样式 */
/* 禁用文本选择手柄 */
-webkit-text-size-adjust: none;
}
/* 选择性允许文本选择 */
.selectable {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
/* 只允许选择文本内容,不允许选择元素 */
.text-only {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
}
/* 允许全部选择 */
.select-all {
-webkit-user-select: all;
-moz-user-select: all;
-ms-user-select: all;
user-select: all;
}
实际使用示例
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<style>
/* 保护主要内容 */
.article-content {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
padding: 20px;
border: 1px solid #ddd;
background: #f9f9f9;
}
/* 允许输入框正常使用 */
.user-input {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
padding: 10px;
border: 1px solid #ccc;
width: 100%;
margin: 10px 0;
}
/* 代码块可以选择 */
.code-block {
-webkit-user-select: all;
-moz-user-select: all;
-ms-user-select: all;
user-select: all;
background: #f4f4f4;
padding: 10px;
font-family: monospace;
}
</style>
</head>
<body>
<div class="article-content">
<h2>受保护的文章标题</h2>
<p>这段内容无法被选择和复制</p>
<img src="protected-image.jpg" alt="受保护的图片" />
</div>
<input
type="text"
class="user-input"
placeholder="这里可以正常输入和选择文本"
/>
<div class="code-block">console.log('这段代码可以被选择复制');</div>
</body>
</html>
2. 禁用拖拽功能
/* 禁用图片和媒体元素的拖拽 */
img,
video,
audio,
canvas,
svg {
-webkit-user-drag: none;
-khtml-user-drag: none;
-moz-user-drag: none;
-o-user-drag: none;
user-drag: none;
pointer-events: none; /* 完全禁用鼠标事件 */
}
/* 禁用所有元素的拖拽 */
* {
-webkit-user-drag: none;
-khtml-user-drag: none;
-moz-user-drag: none;
-o-user-drag: none;
user-drag: none;
}
/* 恢复必要的交互元素 */
.interactive,
button,
input,
textarea,
select,
a {
pointer-events: auto;
-webkit-user-drag: auto;
-khtml-user-drag: auto;
-moz-user-drag: auto;
-o-user-drag: auto;
user-drag: auto;
}
/* 特殊处理:可拖拽的元素 */
.draggable {
-webkit-user-drag: element;
-khtml-user-drag: element;
-moz-user-drag: element;
-o-user-drag: element;
user-drag: element;
}
3. 移动端特殊优化
/* 移动端复制保护 */
.mobile-protected {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
/* iOS Safari 特殊处理 */
-webkit-touch-callout: none; /* 禁用长按菜单 */
-webkit-tap-highlight-color: transparent; /* 禁用点击高亮 */
/* Android 特殊处理 */
-webkit-text-size-adjust: none; /* 禁用文本大小调整 */
/* 禁用双击缩放 */
touch-action: manipulation;
/* 禁用选择手柄 */
-webkit-appearance: none;
}
/* 禁用文本选择高亮 */
.mobile-protected::selection {
background: transparent;
}
.mobile-protected::-moz-selection {
background: transparent;
}
/* 禁用iOS Safari的选择工具栏 */
.mobile-protected::-webkit-selection {
background: transparent;
}
⚡ JavaScript 方法
JavaScript 方法提供了更灵活和强大的保护功能,可以动态控制用户行为并提供反馈。
1. 禁用右键菜单
基础实现
// 方法1: 全局禁用右键菜单
document.addEventListener("contextmenu", function (e) {
e.preventDefault();
return false;
});
// 方法2: 针对特定元素
document
.getElementById("protected-content")
.addEventListener("contextmenu", function (e) {
e.preventDefault();
alert("右键功能已被禁用");
return false;
});
// 方法3: 使用事件委托
document.addEventListener("contextmenu", function (e) {
if (e.target.classList.contains("protected")) {
e.preventDefault();
console.log("尝试右键点击受保护内容");
return false;
}
});
高级实现
// 智能右键菜单禁用
class ContextMenuProtection {
constructor(options = {}) {
this.options = {
showWarning: true,
warningMessage: "此内容受到保护,无法使用右键菜单",
allowedElements: ["input", "textarea"],
...options,
};
this.init();
}
init() {
document.addEventListener("contextmenu", (e) => this.handleContextMenu(e));
}
handleContextMenu(e) {
const tagName = e.target.tagName.toLowerCase();
// 允许特定元素使用右键菜单
if (this.options.allowedElements.includes(tagName)) {
return true;
}
// 检查是否有特殊标记
if (e.target.dataset.allowContextMenu === "true") {
return true;
}
e.preventDefault();
if (this.options.showWarning) {
this.showWarning(this.options.warningMessage);
}
return false;
}
showWarning(message) {
// 创建自定义提示
const warning = document.createElement("div");
warning.textContent = message;
warning.style.cssText = `
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #ff4444;
color: white;
padding: 10px 20px;
border-radius: 5px;
z-index: 10000;
font-size: 14px;
`;
document.body.appendChild(warning);
setTimeout(() => {
document.body.removeChild(warning);
}, 2000);
}
}
// 使用示例
const contextProtection = new ContextMenuProtection({
showWarning: true,
warningMessage: "内容受保护,禁止右键操作",
allowedElements: ["input", "textarea", "select"],
});
2. 禁用键盘快捷键
基础实现
// 禁用常见的复制快捷键
document.addEventListener("keydown", function (e) {
// 禁用 Ctrl+C (复制)
if (e.ctrlKey && e.keyCode === 67) {
e.preventDefault();
return false;
}
// 禁用 Ctrl+A (全选)
if (e.ctrlKey && e.keyCode === 65) {
e.preventDefault();
return false;
}
// 禁用 Ctrl+V (粘贴)
if (e.ctrlKey && e.keyCode === 86) {
e.preventDefault();
return false;
}
// 禁用 Ctrl+X (剪切)
if (e.ctrlKey && e.keyCode === 88) {
e.preventDefault();
return false;
}
// 禁用 Ctrl+S (保存)
if (e.ctrlKey && e.keyCode === 83) {
e.preventDefault();
return false;
}
// 禁用 F12 (开发者工具)
if (e.keyCode === 123) {
e.preventDefault();
return false;
}
// 禁用 Ctrl+Shift+I (开发者工具)
if (e.ctrlKey && e.shiftKey && e.keyCode === 73) {
e.preventDefault();
return false;
}
// 禁用 Ctrl+U (查看源代码)
if (e.ctrlKey && e.keyCode === 85) {
e.preventDefault();
return false;
}
});
高级键盘保护
class KeyboardProtection {
constructor(options = {}) {
this.options = {
disableCopy: true,
disablePaste: true,
disableSelectAll: true,
disableSave: true,
disableDevTools: true,
disablePrint: true,
allowedInputs: ["input", "textarea"],
showWarnings: true,
...options,
};
this.forbiddenKeys = this.buildForbiddenKeys();
this.init();
}
buildForbiddenKeys() {
const keys = [];
if (this.options.disableCopy) {
keys.push({ ctrl: true, key: 67, name: "Ctrl+C (复制)" });
}
if (this.options.disablePaste) {
keys.push({ ctrl: true, key: 86, name: "Ctrl+V (粘贴)" });
}
if (this.options.disableSelectAll) {
keys.push({ ctrl: true, key: 65, name: "Ctrl+A (全选)" });
}
if (this.options.disableSave) {
keys.push({ ctrl: true, key: 83, name: "Ctrl+S (保存)" });
}
if (this.options.disableDevTools) {
keys.push(
{ key: 123, name: "F12 (开发者工具)" },
{ ctrl: true, shift: true, key: 73, name: "Ctrl+Shift+I (开发者工具)" },
{ ctrl: true, shift: true, key: 74, name: "Ctrl+Shift+J (控制台)" },
{ ctrl: true, key: 85, name: "Ctrl+U (查看源代码)" }
);
}
if (this.options.disablePrint) {
keys.push({ ctrl: true, key: 80, name: "Ctrl+P (打印)" });
}
return keys;
}
init() {
document.addEventListener("keydown", (e) => this.handleKeyDown(e));
}
handleKeyDown(e) {
const target = e.target;
const tagName = target.tagName.toLowerCase();
// 允许在输入框中使用某些快捷键
if (this.options.allowedInputs.includes(tagName)) {
// 在输入框中只禁用开发者工具相关快捷键
if (this.options.disableDevTools) {
const devToolsKeys = this.forbiddenKeys.filter(
(key) =>
key.name.includes("开发者工具") ||
key.name.includes("控制台") ||
key.name.includes("查看源代码")
);
for (let forbidden of devToolsKeys) {
if (this.matchesKeyCombo(e, forbidden)) {
e.preventDefault();
this.showWarning(`${forbidden.name} 已被禁用`);
return false;
}
}
}
return true;
}
// 检查所有禁用的快捷键
for (let forbidden of this.forbiddenKeys) {
if (this.matchesKeyCombo(e, forbidden)) {
e.preventDefault();
if (this.options.showWarnings) {
this.showWarning(`${forbidden.name} 已被禁用`);
}
return false;
}
}
}
matchesKeyCombo(event, combo) {
return (
(!combo.ctrl || event.ctrlKey) &&
(!combo.shift || event.shiftKey) &&
(!combo.alt || event.altKey) &&
event.keyCode === combo.key
);
}
showWarning(message) {
if (this.options.showWarnings) {
console.warn(message);
// 可以在这里添加自定义的警告显示逻辑
}
}
}
// 使用示例
const keyboardProtection = new KeyboardProtection({
disableCopy: true,
disablePaste: true,
disableSelectAll: true,
disableSave: true,
disableDevTools: true,
disablePrint: true,
allowedInputs: ["input", "textarea"],
showWarnings: true,
});
3. 禁用文本选择
// 方法1: 禁用选择事件
document.addEventListener("selectstart", function (e) {
e.preventDefault();
return false;
});
// 方法2: 清除已选择的文本
document.addEventListener("mouseup", function () {
if (window.getSelection) {
window.getSelection().removeAllRanges();
} else if (document.selection) {
document.selection.empty();
}
});
// 方法3: 禁用拖拽选择
document.addEventListener("dragstart", function (e) {
e.preventDefault();
return false;
});
// 方法4: 高级文本选择保护
class TextSelectionProtection {
constructor(options = {}) {
this.options = {
clearSelectionInterval: 100, // 清除选择的间隔时间(ms)
allowedElements: ["input", "textarea"],
...options,
};
this.init();
}
init() {
// 禁用选择开始事件
document.addEventListener("selectstart", (e) => this.handleSelectStart(e));
// 禁用拖拽事件
document.addEventListener("dragstart", (e) => this.handleDragStart(e));
// 定期清除选择
if (this.options.clearSelectionInterval > 0) {
setInterval(
() => this.clearSelection(),
this.options.clearSelectionInterval
);
}
// 监听鼠标事件
document.addEventListener("mouseup", () => this.clearSelection());
document.addEventListener("keyup", () => this.clearSelection());
}
handleSelectStart(e) {
const tagName = e.target.tagName.toLowerCase();
if (this.options.allowedElements.includes(tagName)) {
return true;
}
e.preventDefault();
return false;
}
handleDragStart(e) {
const tagName = e.target.tagName.toLowerCase();
if (this.options.allowedElements.includes(tagName)) {
return true;
}
e.preventDefault();
return false;
}
clearSelection() {
try {
if (window.getSelection) {
const selection = window.getSelection();
if (selection.rangeCount > 0) {
// 检查选择是否在允许的元素中
const range = selection.getRangeAt(0);
const container = range.commonAncestorContainer;
const element =
container.nodeType === Node.TEXT_NODE
? container.parentElement
: container;
const tagName = element.tagName ? element.tagName.toLowerCase() : "";
if (!this.options.allowedElements.includes(tagName)) {
selection.removeAllRanges();
}
}
} else if (document.selection) {
document.selection.empty();
}
} catch (e) {
// 忽略错误
}
}
}
// 使用示例
const textProtection = new TextSelectionProtection({
clearSelectionInterval: 50,
allowedElements: ["input", "textarea", "select"],
});
4. 监控开发者工具
// 基础开发者工具检测
let devtools = {
open: false,
orientation: null,
};
const threshold = 160;
function detectDevTools() {
if (
window.outerHeight - window.innerHeight > threshold ||
window.outerWidth - window.innerWidth > threshold
) {
if (!devtools.open) {
devtools.open = true;
console.log("开发者工具已打开");
// 可以在这里添加警告或重定向
// window.location.href = 'about:blank';
}
} else {
devtools.open = false;
}
}
setInterval(detectDevTools, 500);
// 高级开发者工具检测
class DevToolsDetection {
constructor(options = {}) {
this.options = {
checkInterval: 1000,
onDetected: () => console.warn("检测到开发者工具"),
onClosed: () => console.log("开发者工具已关闭"),
threshold: 160,
redirectUrl: null,
showWarning: true,
...options,
};
this.isOpen = false;
this.init();
}
init() {
setInterval(() => this.check(), this.options.checkInterval);
// 检测控制台输出
this.detectConsoleOutput();
// 检测调试器
this.detectDebugger();
}
check() {
const widthThreshold =
window.outerWidth - window.innerWidth > this.options.threshold;
const heightThreshold =
window.outerHeight - window.innerHeight > this.options.threshold;
if (widthThreshold || heightThreshold) {
if (!this.isOpen) {
this.isOpen = true;
this.handleDetected();
}
} else {
if (this.isOpen) {
this.isOpen = false;
this.handleClosed();
}
}
}
detectConsoleOutput() {
// 重写console方法来检测控制台使用
const originalLog = console.log;
console.log = (...args) => {
this.handleDetected();
return originalLog.apply(console, args);
};
}
detectDebugger() {
// 定期检查调试器
setInterval(() => {
const start = performance.now();
debugger;
const end = performance.now();
if (end - start > 100) {
this.handleDetected();
}
}, 3000);
}
handleDetected() {
this.options.onDetected();
if (this.options.showWarning) {
alert("检测到开发者工具,页面功能可能受限");
}
if (this.options.redirectUrl) {
window.location.href = this.options.redirectUrl;
}
}
handleClosed() {
this.options.onClosed();
}
}
// 使用示例
const devToolsDetection = new DevToolsDetection({
checkInterval: 1000,
onDetected: () => {
console.warn("开发者工具被检测到");
document.body.style.display = "none";
},
onClosed: () => {
document.body.style.display = "block";
},
threshold: 160,
showWarning: true,
});
🔧 综合解决方案
将 CSS 和 JavaScript 方法结合起来,创建一个完整的复制保护系统。
完整的保护类
class CopyProtection {
constructor(options = {}) {
this.options = {
// 基础保护选项
disableRightClick: true,
disableSelection: true,
disableKeyboardShortcuts: true,
disableDeveloperTools: true,
disableDragDrop: true,
disablePrint: true,
// 用户体验选项
showWarnings: true,
allowedElements: ["input", "textarea", "select"],
warningDuration: 2000,
// 高级选项
clearSelectionInterval: 100,
devToolsCheckInterval: 1000,
redirectOnDevTools: false,
redirectUrl: "about:blank",
// 自定义消息
messages: {
rightClick: "右键功能已被禁用",
keyboard: "该快捷键已被禁用",
devTools: "检测到开发者工具",
selection: "文本选择已被禁用",
},
...options,
};
this.isDevToolsOpen = false;
this.init();
}
init() {
// 添加CSS样式
this.addProtectionStyles();
// 初始化各种保护功能
if (this.options.disableRightClick) {
this.initRightClickProtection();
}
if (this.options.disableSelection) {
this.initSelectionProtection();
}
if (this.options.disableKeyboardShortcuts) {
this.initKeyboardProtection();
}
if (this.options.disableDeveloperTools) {
this.initDevToolsDetection();
}
if (this.options.disableDragDrop) {
this.initDragDropProtection();
}
if (this.options.disablePrint) {
this.initPrintProtection();
}
console.log("🛡️ 复制保护系统已启动");
}
addProtectionStyles() {
const style = document.createElement("style");
style.id = "copy-protection-styles";
style.textContent = `
/* 基础保护样式 */
.copy-protected {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-touch-callout: none;
-webkit-tap-highlight-color: transparent;
-webkit-user-drag: none;
-khtml-user-drag: none;
-moz-user-drag: none;
-o-user-drag: none;
user-drag: none;
}
/* 媒体元素保护 */
.copy-protected img,
.copy-protected video,
.copy-protected audio,
.copy-protected canvas {
pointer-events: none;
-webkit-user-drag: none;
-khtml-user-drag: none;
-moz-user-drag: none;
-o-user-drag: none;
user-drag: none;
}
/* 选择高亮禁用 */
.copy-protected::selection {
background: transparent;
}
.copy-protected::-moz-selection {
background: transparent;
}
/* 警告提示样式 */
.copy-protection-warning {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: linear-gradient(135deg, #ff6b6b, #ee5a24);
color: white;
padding: 15px 25px;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
z-index: 999999;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-size: 14px;
font-weight: 500;
text-align: center;
animation: copyProtectionFadeIn 0.3s ease-out;
}
@keyframes copyProtectionFadeIn {
from {
opacity: 0;
transform: translate(-50%, -50%) scale(0.8);
}
to {
opacity: 1;
transform: translate(-50%, -50%) scale(1);
}
}
/* 允许的元素恢复正常功能 */
.copy-protected input,
.copy-protected textarea,
.copy-protected select {
-webkit-user-select: text;
-moz-user-select: text;
-ms-user-select: text;
user-select: text;
pointer-events: auto;
}
`;
document.head.appendChild(style);
// 应用保护样式到body
document.body.classList.add("copy-protected");
}
initRightClickProtection() {
document.addEventListener("contextmenu", (e) => {
const tagName = e.target.tagName.toLowerCase();
if (this.options.allowedElements.includes(tagName)) {
return true;
}
if (e.target.dataset.allowContextMenu === "true") {
return true;
}
e.preventDefault();
if (this.options.showWarnings) {
this.showWarning(this.options.messages.rightClick);
}
return false;
});
}
initSelectionProtection() {
// 禁用选择开始
document.addEventListener("selectstart", (e) => {
const tagName = e.target.tagName.toLowerCase();
if (this.options.allowedElements.includes(tagName)) {
return true;
}
e.preventDefault();
return false;
});
// 定期清除选择
if (this.options.clearSelectionInterval > 0) {
setInterval(() => {
this.clearSelection();
}, this.options.clearSelectionInterval);
}
// 监听鼠标和键盘事件
document.addEventListener("mouseup", () => this.clearSelection());
document.addEventListener("keyup", () => this.clearSelection());
}
clearSelection() {
try {
if (window.getSelection) {
const selection = window.getSelection();
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
const container = range.commonAncestorContainer;
const element =
container.nodeType === Node.TEXT_NODE
? container.parentElement
: container;
const tagName = element.tagName ? element.tagName.toLowerCase() : "";
if (!this.options.allowedElements.includes(tagName)) {
selection.removeAllRanges();
}
}
} else if (document.selection) {
document.selection.empty();
}
} catch (e) {
// 忽略错误
}
}
initKeyboardProtection() {
const forbiddenKeys = [
{ ctrl: true, key: 67, name: "Ctrl+C (复制)" },
{ ctrl: true, key: 86, name: "Ctrl+V (粘贴)" },
{ ctrl: true, key: 65, name: "Ctrl+A (全选)" },
{ ctrl: true, key: 88, name: "Ctrl+X (剪切)" },
{ ctrl: true, key: 83, name: "Ctrl+S (保存)" },
{ ctrl: true, key: 80, name: "Ctrl+P (打印)" },
{ key: 123, name: "F12 (开发者工具)" },
{ ctrl: true, shift: true, key: 73, name: "Ctrl+Shift+I (开发者工具)" },
{ ctrl: true, shift: true, key: 74, name: "Ctrl+Shift+J (控制台)" },
{ ctrl: true, key: 85, name: "Ctrl+U (查看源代码)" },
];
document.addEventListener("keydown", (e) => {
const target = e.target;
const tagName = target.tagName.toLowerCase();
// 允许在输入框中使用某些快捷键
if (this.options.allowedElements.includes(tagName)) {
// 在输入框中只禁用开发者工具相关快捷键
const devToolsKeys = forbiddenKeys.filter(
(key) =>
key.name.includes("开发者工具") ||
key.name.includes("控制台") ||
key.name.includes("查看源代码")
);
for (let forbidden of devToolsKeys) {
if (this.matchesKeyCombo(e, forbidden)) {
e.preventDefault();
if (this.options.showWarnings) {
this.showWarning(this.options.messages.keyboard);
}
return false;
}
}
return true;
}
// 检查所有禁用的快捷键
for (let forbidden of forbiddenKeys) {
if (this.matchesKeyCombo(e, forbidden)) {
e.preventDefault();
if (this.options.showWarnings) {
this.showWarning(this.options.messages.keyboard);
}
return false;
}
}
});
}
matchesKeyCombo(event, combo) {
return (
(!combo.ctrl || event.ctrlKey) &&
(!combo.shift || event.shiftKey) &&
(!combo.alt || event.altKey) &&
event.keyCode === combo.key
);
}
initDragDropProtection() {
document.addEventListener("dragstart", (e) => {
const tagName = e.target.tagName.toLowerCase();
if (this.options.allowedElements.includes(tagName)) {
return true;
}
e.preventDefault();
return false;
});
}
initPrintProtection() {
// 禁用打印快捷键已在键盘保护中处理
// 这里可以添加额外的打印保护逻辑
window.addEventListener("beforeprint", (e) => {
if (this.options.showWarnings) {
this.showWarning("打印功能已被禁用");
}
e.preventDefault();
return false;
});
}
initDevToolsDetection() {
setInterval(() => {
const widthThreshold = window.outerWidth - window.innerWidth > 160;
const heightThreshold = window.outerHeight - window.innerHeight > 160;
if (widthThreshold || heightThreshold) {
if (!this.isDevToolsOpen) {
this.isDevToolsOpen = true;
this.handleDevToolsDetected();
}
} else {
this.isDevToolsOpen = false;
}
}, this.options.devToolsCheckInterval);
}
handleDevToolsDetected() {
if (this.options.showWarnings) {
this.showWarning(this.options.messages.devTools);
}
if (this.options.redirectOnDevTools) {
setTimeout(() => {
window.location.href = this.options.redirectUrl;
}, 1000);
}
}
showWarning(message) {
// 移除已存在的警告
const existingWarning = document.querySelector(".copy-protection-warning");
if (existingWarning) {
existingWarning.remove();
}
// 创建新的警告
const warning = document.createElement("div");
warning.className = "copy-protection-warning";
warning.textContent = message;
document.body.appendChild(warning);
// 自动移除警告
setTimeout(() => {
if (warning.parentNode) {
warning.parentNode.removeChild(warning);
}
}, this.options.warningDuration);
}
// 公共方法:启用保护
enable() {
document.body.classList.add("copy-protected");
console.log("🛡️ 复制保护已启用");
}
// 公共方法:禁用保护
disable() {
document.body.classList.remove("copy-protected");
console.log("🔓 复制保护已禁用");
}
// 公共方法:销毁保护
destroy() {
// 移除样式
const style = document.getElementById("copy-protection-styles");
if (style) {
style.remove();
}
// 移除类名
document.body.classList.remove("copy-protected");
// 移除警告
const warnings = document.querySelectorAll(".copy-protection-warning");
warnings.forEach((warning) => warning.remove());
console.log("🗑️ 复制保护已销毁");
}
}
// 使用示例
const protection = new CopyProtection({
// 基础保护选项
disableRightClick: true,
disableSelection: true,
disableKeyboardShortcuts: true,
disableDeveloperTools: true,
disableDragDrop: true,
disablePrint: false,
// 用户体验选项
showWarnings: true,
allowedElements: ["input", "textarea", "select"],
warningDuration: 3000,
// 高级选项
clearSelectionInterval: 50,
devToolsCheckInterval: 1000,
redirectOnDevTools: false,
// 自定义消息
messages: {
rightClick: "🚫 右键功能已被禁用",
keyboard: "⌨️ 该快捷键已被禁用",
devTools: "🔧 检测到开发者工具",
selection: "📝 文本选择已被禁用",
},
});
🎯 高级技巧
1. 动态内容保护
// 动态添加保护到新内容
function protectNewContent() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
node.classList.add("copy-protected");
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
});
}
2. 图片水印保护
// 为图片添加水印
function addWatermarkToImages() {
document.querySelectorAll("img").forEach((img) => {
img.addEventListener("load", function () {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = this.naturalWidth;
canvas.height = this.naturalHeight;
// 绘制原图
ctx.drawImage(this, 0, 0);
// 添加水印
ctx.font = "20px Arial";
ctx.fillStyle = "rgba(255, 255, 255, 0.5)";
ctx.fillText("© 版权所有", 10, 30);
// 替换原图
this.src = canvas.toDataURL();
});
});
}
3. 内容加密显示
// 简单的内容混淆
function obfuscateContent() {
const textNodes = document.createTreeWalker(
document.body,
NodeFilter.SHOW_TEXT,
null,
false
);
const nodes = [];
let node;
while ((node = textNodes.nextNode())) {
if (node.textContent.trim()) {
nodes.push(node);
}
}
nodes.forEach((node) => {
const original = node.textContent;
const obfuscated = btoa(original); // Base64编码
node.textContent = obfuscated;
// 鼠标悬停时显示原文
node.parentElement.addEventListener("mouseenter", () => {
node.textContent = original;
});
node.parentElement.addEventListener("mouseleave", () => {
node.textContent = obfuscated;
});
});
}
🌐 浏览器兼容性
功能 | Chrome | Firefox | Safari | Edge | IE11 |
---|---|---|---|---|---|
user-select | ✅ | ✅ | ✅ | ✅ | ✅ |
contextmenu 事件 | ✅ | ✅ | ✅ | ✅ | ✅ |
selectstart 事件 | ✅ | ✅ | ✅ | ✅ | ✅ |
dragstart 事件 | ✅ | ✅ | ✅ | ✅ | ✅ |
keydown 事件 | ✅ | ✅ | ✅ | ✅ | ✅ |
touch-callout | ❌ | ❌ | ✅ | ❌ | ❌ |
tap-highlight-color | ✅ | ❌ | ✅ | ✅ | ❌ |
❓ 常见问题解答
Q1: 这些方法能 100%防止复制吗?
A: 不能。这些方法只能防止普通用户的意外复制,技术熟练的用户仍然可以通过多种方式绕过这些限制。
Q2: 会影响 SEO 吗?
A: 适当使用不会影响 SEO。搜索引擎爬虫通常不会执行 JavaScript,所以 CSS 的user-select: none
不会影响内容索引。
Q3: 如何在保护内容的同时保持可访问性?
A:
- 确保输入框和表单元素可以正常使用
- 为屏幕阅读器用户提供替代的内容访问方式
- 不要完全禁用键盘导航
Q4: 移动端效果如何?
A: 移动端的保护效果相对较好,特别是 iOS Safari 的-webkit-touch-callout: none
可以有效禁用长按菜单。
原文链接:https://code.ifrontend.net/archives/865,转载请注明出处。
评论0