导读:本期聚焦于杨子江创作的《如何使用jQuery生成网页水印并防止开发者工具删除?》,敬请观看详情。打开浏览器开发者工具直接把水印节点删掉,是水印方案里最常见的绕过方式。要在jQuery项目中降低这种操作成功率,可以同时采用Canvas背景水印和独立DOM水印层两套渲染,再配合MutationObserver对水印节点进行删除与属性变更监听,发现异常后立即重新插入。水印层通常使用固定定位、全屏平铺、pointer-events none和较高z-index,既不影响鼠标操作,也能覆盖主要内容区域。对于修改style隐藏水印的情况,还可在定时任务中检查display、visibility和opacity,必要时强制覆盖样式。前端水印本质上无法绝对阻止开发者工具操作,但通过组合校验和自动恢复,能把删除成本提高到多数用户放弃的程度。实际项目中还应注意水印文字、旋转角度、透明度等参数可配置,避免与业务视觉冲突。

网页水印的作用不是让内容无法被复制,而是给未授权传播增加心理压力与溯源成本。用jQuery生成水印本身并不复杂,真正麻烦的是用户打开开发者工具后,只需选中水印节点按Delete键就能让整层水印消失。如果要让方案具备基本防护能力,通常需要把Canvas背景水印、DOM水印层和MutationObserver自动恢复机制组合起来。下面会从水印渲染方式讲到删除监听,再补充定时校验与样式加固,形成一套前端可用的实现思路。

如何使用jQuery生成网页水印并防止开发者工具删除?

基于Canvas生成平铺背景水印

第一层水印建议放在body的背景上,这样做的好处是它不属于某个独立文本节点,用户很难通过开发者工具直接选中删除。生成方式并不复杂:创建canvas节点,设置宽度和高度作为单个水印块的尺寸,在画布上写入文字,再进行一定角度旋转,最后通过toDataURL导出图片,把这张图作为背景图平铺到body即可。

背景图方案适合承载“内部资料”“禁止外传”这类重复性文字,因为平铺后视觉上比较均匀,对正文的干扰也小。颜色建议使用低透明度,例如rgba(0,0,0,0.08)到rgba(0,0,0,0.15)之间,这样既能在截图中留下痕迹,又不会遮挡主要阅读区域。旋转角度通常在负20度到负30度之间,倾斜的水印比水平文字更难被简单裁剪掉。

该方案有一个明显短板:用户虽然不能直接删掉背景,但可以通过开发者工具修改body的background-image样式把水印去掉。因此它只能作为基础层,不能单独承担防删除职责,需要与后面的DOM监听机制配合。

function createWatermarkDataURL(options) {
  var canvas = document.createElement('canvas');
  var ctx = canvas.getContext('2d');
  var text = options.text || '内部资料';
  var width = options.width || 240;
  var height = options.height || 160;
  canvas.width = width;
  canvas.height = height;
  ctx.font = '16px Arial';
  ctx.fillStyle = options.color || 'rgba(0,0,0,0.08)';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.translate(width / 2, height / 2);
  ctx.rotate(-Math.PI / 6);
  ctx.fillText(text, 0, 0);
  return canvas.toDataURL('image/png');
}

function applyBackgroundWatermark(text) {
  var dataUrl = createWatermarkDataURL({ text: text });
  $('body').css({
    'background-image': 'url(' + dataUrl + ')',
    'background-repeat': 'repeat'
  });
}

用jQuery创建独立DOM水印层

第二层可以采用固定定位的全屏div,也就是常说的DOM水印层。它覆盖在内容上方,通过pointer-events设置为none来保证用户的点击、选择、拖动等操作不会受到影响。水印层内部可以重复放置多个水印块,也可以继续使用background-image平铺,但保留独立节点更便于后续监听和恢复。

创建时要注意z-index的设置。水印需要覆盖正文,但不能盖住弹窗、提示框等关键交互组件,因此z-index需要结合项目中的层级规范,通常设置一个相对较高的值,例如9999。如果项目里有全局弹窗使用更高层级,就需要适当调整,避免水印压住操作按钮或遮罩层。

独立DOM水印层最核心的价值在于可识别、可检测、可重建。用户可以删除它,但删除行为会留下DOM变化记录,这正是后面MutationObserver要捕捉的内容。另一个细节是水印层应当统一使用固定id,例如jq-watermark,这样监听逻辑只需判断该id是否存在,不必遍历所有节点。

function createWatermarkLayer(text) {
  var $layer = $('#jq-watermark');
  if ($layer.length === 0) {
    $layer = $('<div id="jq-watermark"></div>');
    $('body').append($layer);
  }

  var block = $('<div class="watermark-block"></div>').text(text || '内部资料');
  block.css({
    fontSize: '16px',
    color: 'rgba(0,0,0,0.1)',
    transform: 'rotate(-20deg)',
    margin: '20px',
    whiteSpace: 'nowrap'
  });

  $layer.empty();
  for (var i = 0; i < 40; i++) {
    $layer.append(block.clone());
  }

  $layer.css({
    position: 'fixed',
    top: 0,
    left: 0,
    width: '100%',
    height: '100%',
    zIndex: 9999,
    pointerEvents: 'none',
    display: 'block',
    overflow: 'hidden'
  });

  return $layer;
}

上面的循环只是为了直观演示重复节点生成。实际项目如果水印数量过多,可能造成DOM节点膨胀,建议使用CSS背景图平铺代替循环插入几十个节点。比如在单个水印层内设置background-image和background-repeat,这样整个页面只需要一个div,既轻量又便于监听。若业务需要不同行不同列错位排列,还可以在每一行放置一个div,再通过CSS控制间距。

用MutationObserver实现删除后自动恢复

MutationObserver是浏览器原生的DOM变化监听接口,jQuery本身没有对它的封装,但两者可以自然配合。监听body的子节点变化,当发现id为jq-watermark的元素不存在时,就重新创建并插入。这个逻辑要在监听回调中执行,同时要避免重复插入,插入前先检查当前页面是否已经有该节点。

除了childList删除,还要关注attributes变化。开发者工具里不一定要删除节点,也可以把水印层的style改为display:none,或者把opacity改成0。监听属性变化后,可以读取目标节点的样式,如果发现被隐藏,就强制恢复显示。attributeFilter建议包含style、class和id,减少不必要的回调触发。

MutationObserver的监听范围设置成document.body,并开启subtree,是因为水印节点可能不是body的直接子元素,某些框架会把挂载点放在更深的容器中。当然,如果水印层始终由body直接管理,关闭subtree可以减少开销。回调函数中不要做复杂DOM操作,只处理水印恢复,避免因为高频变化造成性能问题。

function protectWatermark(text) {
  var targetNode = document.body;
  var observer = new MutationObserver(function(mutations) {
    mutations.forEach(function(mutation) {
      if ($('#jq-watermark').length === 0) {
        $('body').append(createWatermarkLayer(text));
      }

      if (mutation.type === 'attributes' && $(mutation.target).attr('id') === 'jq-watermark') {
        var $target = $(mutation.target);
        if ($target.css('display') === 'none' || $target.css('visibility') === 'hidden') {
          $target.css({
            display: 'block',
            visibility: 'visible'
          });
        }
      }
    });
  });

  observer.observe(targetNode, {
    childList: true,
    attributes: true,
    subtree: true,
    attributeFilter: ['style', 'class', 'id']
  });
}

定时校验与样式加固

MutationObserver能覆盖大多数通过开发者工具修改DOM的行为,但有些用户会直接在控制台执行脚本,或者通过浏览器扩展绕过监听。此时可以再增加一个低频定时任务,每隔两秒或三秒检查水印层是否仍然存在且可见。定时器不必太频繁,否则会给页面带来不必要的计算压力。

样式加固主要针对隐藏水印的场景。恢复样式时,可以使用cssText直接写入带!important的完整样式,提高被再次修改的难度。虽然CSS的!important并不能阻止用户在开发者工具里手动修改,但可以防止页面内置的其他样式覆盖水印层基础样式。恢复时的样式最好包含定位、尺寸、层级、事件穿透和显示方式几个必要项。

需要明确的是,前端水印无法做到绝对安全。只要内容能在浏览器中渲染,用户总可以通过截图、拍照、抓包、修改脚本等方式绕过。前端能做的是让删除成本变高,并为后续溯源保留一定线索。因此在实际项目中,不要把水印当作唯一的内容保护手段,更不要因为加了水印就忽略接口鉴权、权限控制和文件访问策略。

function startWatermarkGuard(text) {
  setInterval(function() {
    var $layer = $('#jq-watermark');
    if ($layer.length === 0) {
      $('body').append(createWatermarkLayer(text));
    }

    var display = $layer.css('display');
    var visibility = $layer.css('visibility');
    var opacity = parseFloat($layer.css('opacity'));
    if (display === 'none' || visibility === 'hidden' || opacity === 0) {
      $layer.attr('style', 'position:fixed !important;top:0 !important;left:0 !important;width:100% !important;height:100% !important;z-index:9999 !important;pointer-events:none !important;display:block !important;visibility:visible !important;opacity:1 !important;');
    }
  }, 2000);
}

综合来看,Canvas背景水印负责提供最基础的视觉覆盖,DOM水印层负责可监听和可恢复的节点,MutationObserver和定时校验共同组成删除后的补救机制。jQuery在这里主要承担DOM操作和样式控制,监听部分依赖原生API。这套方案可以在不大幅增加页面复杂度的前提下,把开发者工具删除水印的成功率降到较低水平。

jQuery水印网页水印防删除MutationObserver修改时间:2026-09-30 05:58:26

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0930/63704.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。