【问题标题】:html5 draggable hide original elementhtml5可拖动隐藏原始元素
【发布时间】:2016-07-22 14:37:26
【问题描述】:

当开始使用 HTML5 draggable 属性拖动元素时,原始元素仍然可见,所以我最终看到了两个元素而不是一个。

我怎样才能只让被拖动的元素可见(原来的元素应该暂时隐藏)。

<script>
  function startDrag() {
    // hide initial element
  }

  function endDrag() {
    // reset initial element
  }
</script>

<div class="draggable" draggable="true"
  ondragstart="startDrag(event)"
  ondragend="endDrag(event)"
></div>

这是一个显示问题的 jsfiddle https://jsfiddle.net/gjc5p4qp/

【问题讨论】:

    标签: javascript html draggable html5-draggable


    【解决方案1】:

    您可以通过一个 hacky 解决方案来成功。本机可拖动性不允许使用 CSS 样式,例如:opacity:0;visibility:hiddendisplay:none

    但您可以使用:transform:translateX(-9999px)

    function startDrag(e) {
      let element = e.target;
      
      element.classList.add('hide');
    }
    
    function endDrag(e) {
      let element = e.srcElement;
      
      element.classList.remove('hide');
    }
    .draggable {
      border-radius: 4px;
      background: #CC0000;
      width: 40px;
      height: 40px;
    }
    .hide {
      transition: 0.01s;
      transform: translateX(-9999px);
    }
    <div
      class="draggable"
      draggable="true"
      ondragstart="startDrag(event)"
      ondragend="endDrag(event)"
    />

    我已经用解决方案更新了你的JSFiddle

    [编辑]:

    Andrew Hedges 建议更新了JSFiddle 示例,使用requestAnimationFrame 代替setTimeout

    [编辑 2]:

    通过Jason Yin 添加transition CSS 属性而不是使用requestAnimationFrame 更新了更好的解决方案,它将处理从脚本转移到渲染。

    【讨论】:

    • 哇,太好了,你知道如何将拖动元素的不透明度设置为1吗?
    • 哈哈,谢谢。我找不到这样做的方法,似乎拖动的“幽灵元素”在 DOM 上不可用,因此我们受到了一些限制。看看这个answer
    • @julesbou Filipe 这个问题还没有合适的解决方案吗?
    • 为什么一定要使用setTimeout?!没有它就会出现故障。
    • @Anthony 单击元素后,浏览器会“复制”该元素,如果该元素被隐藏,浏览器将复制一个透明元素。超时允许浏览器复制然后才隐藏元素。
    【解决方案2】:

    由于简单地设置visibility: hidden 不起作用,我找到了另一个有点hacky 的解决方案:在拖动事件开始后一毫秒设置visibility: hidden

    function startDrag(e) {
      setTimeout(function() {
        e.target.style.visibility = "hidden";
      }, 1);
    }
    
    function endDrag(e) {
      setTimeout(function() {
        e.target.style.visibility = "";
      }, 1);
    }
    body {
      display: inline-block;
      outline: 1px solid black;
    }
    .draggable {
      border-radius: 4px;
      background: #CC0000;
      width: 40px;
      height: 40px;
    }
    <div class="draggable" draggable="true" ondragstart="startDrag(event)" ondragend="endDrag(event)">
    
    </div>

    【讨论】:

    • 与其使用setTimeout,不如使用window.requestAnimationFramefunction startDrag(e) { window.requestAnimationFrame(function() { e.target.style.visibility = "hidden"; }); }
    • @AndrewHedges 知道如何防止不透明度降低吗?!
    • @Anthony 你有没有找到一个模仿旧方式拖放的解决方案(没有可见的原始元素并且在拖动副本中没有不透明度)?
    • @Rantiev 是的,您基本上必须从头开始创建自己的拖放功能,并实际移动用户正在移动的元素,这并不像看起来那么困难。使用mousedownmouseupmousemoveclientXclientY
    • @anthony 所以,这意味着我们不能模仿,我们只能使用旧方法?我已经有了它...只是在 FF 上遇到了一些奇怪的问题,我必须在其中附加 dragstart 侦听器并在内部调用 e.preventDefault() ,以防止拖动卡在那里。这就是为什么我开始研究新的方式(它的创造者需要永远在地狱中燃烧)
    【解决方案3】:

    这可以在没有以前答案的黑客技巧的情况下实现。

    关键是监听drag事件,而不是dragstart

    //listen to drag event, not dragstart!
    document.querySelector(".draggable").addEventListener("drag", (e)=>{
      e.target.classList.add("dragging");
    });
    document.querySelector(".draggable").addEventListener("dragend", (e)=>{
      e.target.classList.remove("dragging");
    });
    .draggable {
      width: 200px;
      height: 30px;
      background-color:#5856d6;
      text-align:center;
      line-height:30px;
    }
    
    .dragging {
      background: transparent;
      color: transparent;
      border: 1px solid #5856d6;
    }
    &lt;div draggable="true" class="draggable"&gt;Drag me!&lt;/div&gt;

    【讨论】:

      【解决方案4】:

      Filipe 的回答真的很有帮助。如果setTimeoutrequestAnimationFrame 不适合您。尝试添加过渡。

      transition: transform 0.01s;
      transform: translateX(-9999px);
      

      【讨论】:

        【解决方案5】:

        找到这个:https://*.com/a/35120001/1807542

        使用干净的解决方案来隐藏拖动处理程序中的源/原始元素。

        var dragging;
        function dragstart(e) {
          dragging = $(this);
        }
        
        function dragover(e) {
          dragging.hide();
        }
        
        function dragend(e) {
          if (dragging) {
            dragging.show();
            dragging = undefined;
          }
        }
        

        【讨论】:

          【解决方案6】:

          您可以将此代码添加到您的 dragStart 事件中:

          event.dataTransfer.setDragImage(new Image(), 0, 0); // Hide the dragged element
          

          它将隐藏您的目标拖动元素。

          【讨论】:

            【解决方案7】:

            如何隐藏你的元素

            如果要隐藏元素,可以使用 CSS 属性 visibility:hiddendisplay:none

            我推荐使用visibility 属性,因为它隐藏了一个元素而不改变文档的布局。但是,如果您的元素有子元素并且想要隐藏它们,则需要在每个子元素上设置 visibility:inherit

            何时隐藏

            您使用dragstart 事件是正确的。但是由可拖动属性创建的元素的克隆出现在dragstart事件的末尾。

            因为 JavaScript 引擎是单线程解释器,如果您选择将其隐藏在此处,您的元素将被屏蔽为它的克隆,这将复制属性 visibility:hidden。实际上,为避免这种情况,您需要在创建克隆后将其隐藏在Javascript callstack 中。

            为此,请使用 setTimout() 函数并将其设置为 0 毫秒。这样,在创建他的克隆之后,原始元素的掩码被放在堆栈的末尾。

            在拖动结束时,要使其重新出现,您只需通过在dragend 事件中调用visibility:visible 来设置元素可见。

            代码示例

            例如,代码如下所示:

            <div 
                class="draggable" 
                draggable="true" 
                ondragstart="startDrag(event)" 
                ondragend="endDrag(event)" 
                style="background: #e66465; color:white; width:80px; height:20px; text-align:center;">
                Drag me
            </div>
            
            <script>
                function startDrag(e) {
                    setTimeout(function(){
                        e.target.style.visibility = "hidden";
                    }, 0);
                }
                function endDrag(e){
                    e.target.style.visibility = "visible";
                }
            </script>

            【讨论】: