所有分类
  • 所有分类
  • Html5资源
  • React资源
  • Vue资源
  • Php资源
  • ‌小程序资源
  • Python资源

CSS+JavaScript 禁用浏览器复制功能的几种方法

🛡️ 禁用浏览器复制功能完整指南

网页中禁用用户的复制功能,包括 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;
    });
  });
}

🌐 浏览器兼容性

功能ChromeFirefoxSafariEdgeIE11
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可以有效禁用长按菜单。

资源下载
下载价格免费
注意:本网站资源属于虚拟产品,不支持退款。请谨慎购买! 购买后资源无法下载,请联系客服QQ:844475003,微信号:th844475003。
原文链接:https://code.ifrontend.net/archives/865,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?