【问题标题】:Child elements are allowing drag/drop子元素允许拖放
【发布时间】:2014-06-10 22:28:28
【问题描述】:

我在 html5 中拖放时遇到了一个奇怪的问题。

面板 A 列出了您可以拖动的元素类型。面板 B 是拖动元素的位置。面板 C 和 D 是您可以拖动元素的其他地方,您可以在面板 B、C 和 D 之间拖动和重新排列元素。

我的问题是我可以将一个元素拖放到其中一个面板内的另一个元素的内部,我不希望用户能够这样做。这些面板的子元素没有附加任何类型的 javascript 或与拖动相关的属性,但它们目前允许在其中拖动元素。

我尝试附加“ondrop='return false;'”和“ondragover='return false;'”,但都没有成功。

肯定有办法关闭元素上的“允许拖入”属性吗?

这是我的代码:

<div id="elem-002" draggable="true" ondragstart="drag(event)">content</div>

主面板:

<div id="panel-b" ondrop="drop(event)" ondragover="allowDrop(event)">
  <div id="elem-001" draggable="true" ondragstart="drag(event)">content</div>
</div>

JS:

function allowDrop(ev) {
    ev.preventDefault();
}

function drag(ev) {
    ev.dataTransfer.setData("Text", ev.target.id);
}

function drop(ev) {
    ev.preventDefault();
    var data = ev.dataTransfer.getData("Text");
    ev.target.appendChild(document.getElementById(data));
}

换一种说法:我可以将我的“elem-002”直接拖到“elem-001”中,这是一个已经被拖入的元素。这会导致我不想发生的元素嵌套。我怎样才能防止这种情况发生?

【问题讨论】:

    标签: javascript html drag-and-drop


    【解决方案1】:

    基本上你必须在allowDrop 中设置正确的ev.dataTransfer.dropEffect。对于您的draggable 元素,它应该是"none"(不允许丢弃),对于其他所有内容(或允许丢弃的任何其他元素),它应该是"all"

    进一步阅读dropEffect

    活生生的例子:

    window.allowDrop = function(ev) {
        ev.preventDefault();
        if (ev.target.getAttribute("draggable") == "true")
            ev.dataTransfer.dropEffect = "none"; // dropping is not allowed
        else
            ev.dataTransfer.dropEffect = "all"; // drop it like it's hot
    };
    
    window.drag = function(ev) {
        ev.dataTransfer.setData("id", ev.target.id);
    };
    
    window.drop = function(ev) {
        ev.preventDefault();
        var id = ev.dataTransfer.getData("id");
    
        var dragged = document.getElementById(id);
        ev.target.appendChild(dragged);
        dragged.className += " dropped";
    };
    .drag {
        display: inline-block;
        padding: 5px;
        background: rgba(0, 0, 0, 0.2);
        margin: 2px;
    }
    
    .dropzone {
        background: #ccc;
        padding: 5px;
        margin-top: 10px;
    }
    <div id="first" class="drag" draggable="true" ondragstart="drag(event)">1</div>
    <div id="second" class="drag" draggable="true" ondragstart="drag(event)">2</div>
    
    <div class="dropzone" ondrop="drop(event)" ondragover="allowDrop(event)"><div id="third" class="drag dropped" draggable="true" ondragstart="drag(event)">3</div></div>

    【讨论】:

    【解决方案2】:

    我不确定这是否是最好的解决方案,但我确实想出了一些办法。

    我把 drop(ev) 改成了这个:

    function drop(ev) {
        ev.preventDefault();
    
        if (!ev.target.getAttribute("ondrop"))
            return false;
    
        var data = ev.dataTransfer.getData("Text");
        ev.target.appendChild(document.getElementById(data));
    }
    

    这个或这个的变体可以防止任何东西被放入一个没有明确定义“ondrop”的元素中。

    【讨论】:

    • 我喜欢这个,因为它不复杂,但不能确认它是否可靠。现在为我工作。
    • 这可行,但浏览器没有显示您所期望的不可放置元素的“请勿放置”图标。
    • 有没有办法获取 ev.target 的父元素,这样这可能会导致总是丢弃专门添加了“ondrop”的元素?
    • @Marc,它没有显示“请勿放置”图标的原因是因为您仍在作为放置目标的元素内。如果该子元素也没有专门作为放置目标,则此代码只是取消放置,如果该放置目标的子元素被放置在该放置目标的子元素内。
    【解决方案3】:

    通过允许用户在放置目标内的任何位置放置,我对@Zerkeras 的回答进行了一些改进,但是,如果用户碰巧放置在放置目标内的元素内,它不会失败,它只是调整放置目标的位置元素被丢弃。

    function drop(ev) {
            ev.preventDefault();
            var data = ev.dataTransfer.getData("text");
            var i = 0;
            var element = ev.target
            while (!element.getAttribute("ondrop") && i < 3) {
                console.log("recalibrating");
                element = element.parentElement;
                i++;
            }
            element.appendChild(document.getElementById(data));
        }
    

    注意,我添加了一个任意的 3 来逃避 while 循环,因为我怀疑不能保证逃避的 while 循环。在这种特殊情况下,这纯粹是我的偏执狂,但在我测试它时非常有帮助。

    我使用新变量element 的原因是因为直接修改ev.target 似乎是不可能的(我认为不变性在JavaScript 中不起作用?如果有人知道这是为什么,我很好奇)。所以我的 while 循环永远不会退出,这就是为什么你会在那里看到我的偏执转义子句。

    【讨论】:

      【解决方案4】:

      我正在使用自定义拖放,将要拖动的元素的鼠标[down|move|up] 与拖动[enter|over|leave] & drop 混合在另一个元素上。我正在使用自定义事件来触发/触发/调度 drop 事件。这对手头的问题并没有真正的影响,只是想给出上下文。

      Anyhoo,我遇到了同样类型的问题,在搜索了一段时间并进行了一些调试后,我意识到 event.[target|srcElement] 实际上是“发送”事件的元素。所以我最终做的是; 拒绝 [target|srcElement] 不是事件的“this”元素且“this”不包含 [target|srcElement](即不是其子元素之一)的任何放置,然后我才允许进一步处理;

      // Drop Event Handler
      function dropEventHandler(event) {
          event = event || window.event;
          var target = event.target || event.srcElement;
      
          if (this !== target && this.contains(target)) {
              // Use if not a proper/normal event, this should stop any further 
              //   processing of the event 
              // if (event.stopPropagation) event.stopPropagation();  // Standard model
              // else event.cancelBubble = true;                       //IE
      
              // Stops the default action of an element from happening
              // Use the event.isDefaultPrevented() method to check whether 
              //   the preventDefault() method was called for the event
              if (event.preventDefault) event.preventDefault();   // Standard model
              else event.returnValue = false;                      //IE
      
              return false;
          }
          // Process drop event
      }
      

      在 Windows 10 上的 Chrome、FF、Opera、IE Edge && IE 11 [仿真 7-11] 中测试, 还测试了 Windows 7 上的 IE v8.0.7601.17514 [IE7 和 IE8 兼容性] 模式。

      【讨论】:

        【解决方案5】:

        如果我理解正确,问题是预期放置目标的子级也可以允许拖放交互。因此,如果您将“this”添加到 ondrop 调用中,您可以知道哪个元素实际上是预期的元素。然后,您可以附加或以其他方式与您想要的元素进行交互。请参阅我的其他问题/解决方案。

        Image tries to add to self on drag/drop interaction

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-12-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-29
          • 1970-01-01
          • 1970-01-01
          • 2019-05-28
          相关资源
          最近更新 更多