【问题标题】:Prevent user from copying text on mobile browsers防止用户在移动浏览器上复制文本
【发布时间】:2015-12-24 02:41:45
【问题描述】:

我正在尝试使用 JavaScript 开发打字速度竞赛。人们应该将他们从 div 中看到的所有单词都写到 textarea 中。

为了防止作弊(例如从 div 复制单词),一种方法是仅在按下键盘键时检查书面文字,但我想知道是否有办法阻止用户在浏览器中复制文本?

到目前为止我所尝试的:

  1. 禁用右键单击(不适用于移动浏览器)
  2. 在所有页面中使用 onmousedown 事件显示警报(它也不起作用)

使用任何库都可以。

【问题讨论】:

  • 你不是说要防止粘贴吗?
  • 检测粘贴非常简单:jsfiddle.net/fpkdkpvm
  • 为了不搞乱标准的预期行为,我鼓励您避免粘贴单词而不是复制它们。阻止复制并不像阻止上下文菜单那么糟糕,但遇到它仍然令人沮丧。
  • 请记住,您可以使用 Chrome 中的 Inspect Element 等功能,这将允许用户复制文本,或删除阻止他们复制文本的元素。
  • 请记住,您可以在移动设备上轻松更改输入法。切换到语音识别可能会带来优势。不确定是否可以检测到它,因为它基本上只是另一个输入设备。

标签: javascript php jquery html css


【解决方案1】:

您可以简单地将文字制作成图片。

<style type="text/css">
div.image {
    width: 100px;
    height: 100px;
    background-image: url-to-your-image;
}
</style>

要生成图像,您可以使用服务器端脚本,如 this question 中的 aswers

或类似的东西:

<?php
header("Content-type: image/png");
$im = @imagecreate(210, 30)
or die("Cannot Initialize new GD image stream");
$background_color = imagecolorallocate($im, 255, 255, 255);
$text_color = imagecolorallocate($im, 0, 0, 0);
imagestring($im, 4, 5, 5,  "This is a test", $text_color);
imagepng($im);
imagedestroy($im);
?> 

Test here

【讨论】:

  • 完美解决方案。解决了真正的 XY 问题。
  • 这是最好的解决方案,因为它可以通过在本地修改 javascript 来阻止绕过的尝试。
  • 如果用户正在使用 OCR 则无法正常工作,这对键入的文本应该没有问题
  • @Belgi 是的,如果用户有足够的动力,什么都不会起作用,即用户可以劫持浏览器渲染引擎将 div 的内容放入剪贴板:P
  • @rybo111 一个完美的解决方案是把用户的手机变砖!哈哈
【解决方案2】:

您可以阻止用户实际选择文本,使其无法复制 - 但是我仍然会按照其他人的建议将此与粘贴检测结合起来

.noselect {
    -moz-user-select: none;
    -webkit-user-select: none;
    -ms-user-select: none;
    user-select: none;
}
<p>this can be selected</p>
<p class="noselect">this can NOT be selected</p>

但用户仍然可以打开页面源并从那里复制。

【讨论】:

  • 'user-select' 不是 standard,可能不适合 Mozilla MDN 上所说的使用:非标准此功能是非标准的,是不在标准轨道上。不要在面向 Web 的生产站点上使用它:它不适用于每个用户。实现之间也可能存在很大的不兼容性,并且未来的行为可能会发生变化。
  • @cateyes,很好的信息。同样地(防止用户实际选择文本),可以将pointer-events: none 应用于div,这是标准的并得到广泛支持。
【解决方案3】:

这样做的一种疯狂方式是,在此之上放置另一个绝对定位的元素。但这也将禁止点击链接!也许你可以用position: relative 和更高的z-index 来做到这一点。

.content {position: relative;}
.content .mask {position: absolute; z-index: 1; width: 100%; height: 100%;}
.content a {position: relative; z-index: 3;}
<div class="content">
  <div class="mask"></div>
  <p>Pages that you view in incognito tabs won’t stick around in your browser’s history, cookie store or search history after you’ve closed <strong>all</strong> of your incognito tabs. Any files that you download or bookmarks that you create will be kept. <a href="https://support.google.com/chrome/?p=incognito">Learn more about incognito browsing</a></p>
</div>

尝试使用touchlongpress 事件。

<!DOCTYPE html>
<html>
<head>
  <script>
    function absorbEvent_(event) {
      var e = event || window.event;
      e.preventDefault && e.preventDefault();
      e.stopPropagation && e.stopPropagation();
      e.cancelBubble = true;
      e.returnValue = false;
      return false;
    }

    function preventLongPressMenu(node) {
      node.ontouchstart = absorbEvent_;
      node.ontouchmove = absorbEvent_;
      node.ontouchend = absorbEvent_;
      node.ontouchcancel = absorbEvent_;
    }

    function init() {
      preventLongPressMenu(document.getElementById('theimage'));
    }
  </script>
</head>
<body onload="init()">
  <img id="theimage" src="http://www.google.com/logos/arthurboyd2010-hp.jpg" width="400">
</body>
</html>

Source

【讨论】:

  • 在 Firefox 桌面的第一个示例中,我可以通过在文本上单击鼠标并向下拖动来复制段落。我想它也可以在移动浏览器上复制。
  • 我总是用“检查元素”然后“删除节点”或添加“显示:无”来摆脱它在 CSS 中。
  • @Max 为什么不反编译破坏事物并做事!大声笑。
  • @PraveenKumar 我正在考虑编写一个浏览器插件来自动删除不可见的图层...
  • @PraveenKumar 爱与和平 :)
【解决方案4】:

尝试在文本上放置一个透明的 div。 我在这里使用了 jQuery。 应该可以的。

var position = $('#textInHere').position();
$('#noClickThroughThis').css({
    height: ($('#textInHere').height()),
    width:  ($('#textInHere').width()),
    position: 'absolute',
    top: position.top,
    left: position.left,
    'z-index': 100
});

这是一个小提琴 http://jsfiddle.net/lacrioque/tc4bwejn/

【讨论】:

  • 按 Ctrl + A 仍然会选择文本。
【解决方案5】:

使用 jQuery 可以很容易地禁用粘贴功能。例如,如果您有这样的编辑字段:

<p id='someInput' contenteditable='true'>Here is the text</p>

然后,这段 jQuery 代码将禁用其上的粘贴功能:

$('#someInput').on('paste', function(e) {
  return false;
});

【讨论】:

    【解决方案6】:

    判断用户是否作弊的一个好方法是将当前输入长度与上次输入长度进行比较。您可以使用数据属性来存储之前的值(或长度):

    <textarea class="typing-only" data-temp=""></textarea>
    

    jQuery:

    $(document).on('input', '.typing-only', function(){
        if((this.value.length - 1) > $(this).data('temp').length){
            alert('Cheat!');
        }
        $(this).data('temp', this.value);
    });
    

    JSFiddle demo

    【讨论】:

      【解决方案7】:

      pointer-events: none

      CSS pointer-events 允许您控制元素和鼠标之间的交互。当设置为none 时,该元素永远不会是鼠标事件的目标。

      MDN definition page

      【讨论】:

        【解决方案8】:

        您可以尝试在 css 中使用 :after 标签并使用 content: "Text"; 对其进行样式设置,AFAIK 您无法选择 :before 和 :after 的内容。

        【讨论】:

          【解决方案9】:

          感谢您提供出色的解决方案。我对所有这些都进行了测试,总之有些只能在 PC 上运行,有些只能在 Chrome 和 Firefox 上运行,有些只能在 Safari 上运行,但不幸的是,它们都没有 100% 运行。

          虽然@Max 答案可能是最安全的,但我没有在问题中使用 PHP 标记,因为如果我使用此解决方案来处理答案,这将很困难,因为我无法在客户端访问单词!

          所以我提出的最终解决方案是将所有提供的答案以及一些新方法(例如每秒清除剪贴板)组合到一个 jQuery 插件中。现在它也适用于多种元素,并且 100% 在 PC 浏览器、Firefox、Chrome 和 Safari 上运行。


          这个插件的作用

          1. 防止粘贴(可选)
          2. 清除剪贴板(好像不太好用)
          3. 吸收所有触摸事件
          4. 禁用右键单击
          5. 禁用用户选择
          6. 禁用指针事件
          7. 在任何选定的 DOM 中添加带有 z-index 的掩码
          8. 在任何选定的 DOM 上添加透明 div

          A jsFiddle:

          (function($) {
          
              $.fn.blockCopy = function(options) {
          
                  var settings = $.extend({
                      blockPasteClass    : null
                  }, options);
          
                  if(settings.blockPasteClass){
                      $("." + settings.blockPasteClass ).bind('copy paste cut drag drop', function (e) {
                          e.preventDefault();
                          return false;
                      });
                  }
          
                  function style_appender(rule){
                      $('html > head').append($('<style>'+rule+'</style>'));
                  }
          
                  function html_appender(html){
                      $("body").append(html);
                  }
          
                  function clearClipboard() {
                      var $temp = $("#bypasser");
                      $temp.val("You can't cheat !").select();
                      document.execCommand("copy");
                  }
          
                  function add_absolute_div(id) {
                      html_appender("<div id='noClick"+id+"' onclick='return false;' oncontextmenu='return false;'>&nbsp;</div>");
                  }
          
                  function absorbEvent_(event) {
                      var e = event || window.event;
                      e.preventDefault && e.preventDefault();
                      e.stopPropagation && e.stopPropagation();
                      e.cancelBubble = true;
                      e.returnValue = false;
                      return false;
                  }
          
                  function preventLongPressMenu(node) {
                      node.ontouchstart = absorbEvent_;
                      node.ontouchmove = absorbEvent_;
                      node.ontouchend = absorbEvent_;
                      node.ontouchcancel = absorbEvent_;
                  }
          
                  function set_absolute_div(element,id){
                      var position = element.position();
                      var noclick = "#noClick" + id;
          
                      $(noclick).css({
                          height: (element.height()),
                          width:    (element.width()),
                          position: 'absolute',
                          top: position.top,
                          left: position.left,
                          'z-index': 100
                      })
                  }
          
          
                  $("body").bind("contextmenu", function(e) {
                      e.preventDefault();
                  });
          
                  //Append needed rules to CSS
                  style_appender(
                      "* {-moz-user-select: none !important; -khtml-user-select: none !important;   -webkit-user-select: none !important; -ms-user-select: none !important;   user-select: none !important; }"+
                      ".content {position: relative !important; }" +
                      ".content .mask {position: absolute !important ; z-index: 1 !important; width: 100% !important; height: 100%!important;}" +
                      ".content a {position: relative !important; z-index: 3 !important;}"+
                      ".content, .content .mask{ pointer-events: none;}"
                  );
          
          
                  //Append an input to clear the clipboard
                  html_appender("<input id='bypasser' value='nothing' type='hidden'>");
          
                  //Clearing clipboard Intervali
                  setInterval(clearClipboard,1000);
          
                  var id = 1;
          
                  return this.each( function() {
          
                      //Preventing using touch events
                      preventLongPressMenu($(this));
          
                      //Add CSS preventer rules to selected DOM & append mask to class
                      $(this).addClass("content").append("<div class='mask'></div>");
          
                      //Append an absolute div to body
                      add_absolute_div(id);
          
                      //Set position of the div to selected DOM
                      set_absolute_div($(this),id);
          
                      id++;
                  });
              }
          }(jQuery));
          

          用法

          $(document).ready(function(){
          
              $(".words").blockCopy({
                  blockPasteClass : "noPasting"
              });
          
          });
          

          演示用 HTML:

          <div class="words">Test1: Can you copy me or not?</div><br>
          <div class="words">Test2: Can you <br> copy me or not?</div><br>
          <textarea class="words">Test3: Can you <br>copy me or not?</textarea><br>
          
          
          <textarea  class="noPasting"   placeholder="Test1: Paste content if you can"   ></textarea><br>
          
          <textarea  class="noPasting"   placeholder="Test2: Paste content if you can"   ></textarea>
          

          让我知道你的意见。谢谢。

          来源

          【讨论】:

            【解决方案10】:

            比公认的更简单的解决方案是简单地使用带有filltext 的画布元素

            var canvas = document.getElementById("myCanvas");
            var ctx = canvas.getContext("2d");
            ctx.fillText("Can't copy this", 5, 30);
            &lt;canvas id="myCanvas"&gt;&lt;/canvas&gt;

            JSFiddle example

            【讨论】:

              【解决方案11】:

              您可以在 jQuery 的 cut copy paste 事件上 return false

              <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
              <script>
              $(document).on("cut copy paste", function(event){
                return false;
              });
              </script>
              <textarea>Try to copy my text</textarea>

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2012-09-11
                • 2013-11-17
                • 2019-08-04
                • 1970-01-01
                • 1970-01-01
                • 2016-10-28
                • 2015-10-15
                相关资源
                最近更新 更多