如何在拖放中停止 dragend 的默认行为?

Posted

技术标签:

【中文标题】如何在拖放中停止 dragend 的默认行为?【英文标题】:How to stop dragend's default behavior in drag and drop? 【发布时间】:2017-10-02 11:33:14 【问题描述】:

我很难用语言来概括这个错误。所以我为这个错误创建了video。 基本上,当 dragEnd 被调用时,元素的动画就像去原来的地方一样。如何停止默认行为?我也为此制作了fiddle 和codepen。

我的示例有两个列表,在释放鼠标按钮后调用 dragEnd 时。一个列表中的元素将动画显示到其原始位置的列表,而不是它将要到达的位置。

JS代码:

'use strict';

var source = null; var heightWidth = null; var lastDragOverElementId = null; var dragImageSource = null;

function listItemDragStartHandler(event) 
    source = event.currentTarget;
    heightWidth = [];
    heightWidth.push(source.offsetHeight);
    heightWidth.push(source.offsetWidth);
    event.dataTransfer.setData('text/html', event.currentTarget.innerHTML);
    event.dataTransfer.effectAllowed = 'move';
    // Drag image logic
    dragImageSource = source.cloneNode(true);
    dragImageSource.style.position = 'absolute';
    // Don't show the element
    dragImageSource.style.top = '0px';
    dragImageSource.style.left = '-' + String(window.innerWidth) + 'px';
    // dragImageSource.style.left = '-100px';
    var toTiltElement = dragImageSource.getElementsByClassName('item-list-element')[0];
    toTiltElement.style.transform = 'rotate(5deg)';
    document.body.append(dragImageSource);
    event.dataTransfer.setDragImage(dragImageSource, heightWidth[1]/2, heightWidth[0]/2); 

function dragoverHandler(event) 
    event.preventDefault();
    event.dataTransfer.dropEffect = 'move';
    source.style.display = 'none';
    var currentElement = event.currentTarget;
    var listContainer = currentElement.parentNode;
    if (lastDragOverElementId != currentElement.getAttribute('id')) 
        lastDragOverElementId = currentElement.getAttribute('id');
        if(document.getElementById('grayed-empty-node') !== null) 
            if(currentElement.getAttribute('id') !== 'grayed-empty-node') 
                document.getElementById('grayed-empty-node').remove();
            
        
        var isGrayEmptyNodeThere = document.getElementById('grayed-empty-node');   //
        if(isGrayEmptyNodeThere === null) 
            var emptyNode = document.getElementById('empty-node').cloneNode(true);
            emptyNode.removeAttribute('id');
            emptyNode.setAttribute('id', 'grayed-empty-node');
            emptyNode.setAttribute('class', 'fade-in');
            emptyNode.style.height = String(heightWidth[0]) + 'px';
            emptyNode.style.width = String(heightWidth[1]) + 'px';
            listContainer.insertBefore(emptyNode, currentElement);
        
     

function dragLeaveHandler(event) 
    event.preventDefault();
    var currentElement = event.currentTarget;
    var listContainer = currentElement.parentNode;
    var elementList = listContainer.getElementsByClassName('item-template-container');
    var visibleElementList = [];
    var i = 0;
    for (i=0; i<elementList.length; i++) 
        if (elementList[i].style.display !== 'none') 
            visibleElementList.push(elementList[i]);
        
    
    // Last element ondragleave handler should be delayed, otherwise it
    // would spin off chain reaction.
    var lastVisibleElement = visibleElementList[visibleElementList.length - 1];
    // Basically last element
    if (currentElement.id === lastVisibleElement.getAttribute('id')) 
        lastVisibleElement.removeAttribute('ondragleave');
        if(document.getElementById('grayed-empty-node') !== null) 
            document.getElementById('grayed-empty-node').remove();
        
        var isGrayEmptyNodeThere = document.getElementById('grayed-empty-node');
        if(isGrayEmptyNodeThere === null) 
            var listContainer = currentElement.parentNode;
            var emptyNode = document.getElementById('empty-node').cloneNode(true);
            emptyNode.removeAttribute('id');
            emptyNode.setAttribute('id', 'grayed-empty-node');
            emptyNode.setAttribute('class', 'fade-in');
            emptyNode.style.height = String(heightWidth[0]) + 'px';
            emptyNode.style.width = String(heightWidth[1]) + 'px';
            listContainer.insertBefore(emptyNode, null);
        
        // This delayed the chain reaction
        setTimeout(function() 
            visibleElementList[visibleElementList.length - 1].setAttribute('ondragleave', 'dragLeaveHandler(event);');
        , 500);
    // When event is on last element set the lastDragOverElementId to null
    // then dragover to lastVisibleElement(second last element) can be handled
    // otherwise dragover to second last element won't show placeholder.
    lastDragOverElementId = null;
     

function dragEndHandler(event) 
    event.preventDefault();
    // Check the dropEffect
    dragImageSource.remove();
    var listElement = document.getElementById('grayed-empty-node')
    var listContainer = listElement.parentNode;
    if (event.dataTransfer.dropEffect === 'none') 
        var grayEmptyNode = document.getElementById('grayed-empty-node');
        // When grayEmptyNode is null, that will append at the end.
        listContainer.insertBefore(source, grayEmptyNode);
        source.style.display = '';
        if(document.getElementById('grayed-empty-node') !== null) 
            document.getElementById('grayed-empty-node').remove();
        
     else if (event.dataTransfer.dropEffect === 'move') 
        var grayEmptyNode = document.getElementById('grayed-empty-node');
        // When grayEmptyNode is null, that will append at the end.
        listContainer.insertBefore(source, grayEmptyNode);
        source.style.display = '';
        if(document.getElementById('grayed-empty-node') !== null) 
            document.getElementById('grayed-empty-node').remove();
        
     

function delete_item(event) 
    var currentElement = event.currentTarget;
    var grandParentOfDelete = currentElement.parentNode.parentNode;
    grandParentOfDelete.remove(); 

function add_item() 
    var item_text_node = document.getElementsByName('add-item-text')[0]
    var item_text = item_text_node.value;
    if (item_text.length > 0) 
        var item_template = document.getElementById('item-template-container');
        var item_clone = item_template.cloneNode(true);
        item_clone.removeAttribute('id');
        var random_id = (new Date().getTime() +
            parseInt(Math.random(0, 1000) * 1000))
        item_clone.setAttribute('id', random_id);
        var clone_text = item_clone.getElementsByClassName('item-text')[0];
        clone_text.textContent = item_text;
        // reset the value
        item_text_node.value = '';
        var item_list = document.getElementById('item-list');
        item_list.appendChild(item_clone);
     else 
        alert('No text?? Add some text!');
     

function add_item_2() 
    var item_text_node = document.getElementsByName('add-item-text-2')[0]
    var item_text = item_text_node.value;
    if (item_text.length > 0) 
        var item_template = document.getElementById('item-template-container');
        var item_clone = item_template.cloneNode(true);
        item_clone.removeAttribute('id');
        var random_id = (new Date().getTime() +
            parseInt(Math.random(0, 1000) * 1000))
        item_clone.setAttribute('id', random_id);
        var clone_text = item_clone.getElementsByClassName('item-text')[0];
        clone_text.textContent = item_text;
        // reset the value
        item_text_node.value = '';
        var item_list = document.getElementById('item-list-2');
        item_list.appendChild(item_clone);
     else 
        alert('No text?? Add some text!');
     

function onEnterInInput() 
    var e = e || window.event;
    if (e.keyCode == 13) 
        add_item()
     

function sample_data() 
    for(var i=0;i<10;i++)
        var item_text_node = document.getElementsByName('add-item-text')[0]
        item_text_node.value = i;
        var item_text_node_2 = document.getElementsByName('add-item-text-2')[0]
        item_text_node_2.value = i;
        add_item();
        add_item_2();
     

window.onload = function () 
    sample_data(); 

HTML 代码:

<!DOCTYPE html>
<html>
<head>
    <title>List task</title>
    <script type="text/javascript" src="list-task.js"></script>
    <link rel="stylesheet" type="text/css" href="list-task.css">
    <script>
      document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] +
      ':35729/livereload.js?snipver=1"></' + 'script>')
    </script>
</head>
<body>
    <div id="container">
        <div id="list-1">
            <div id="add-item-div">
                <input type="text" name="add-item-text" onkeypress="onEnterInInput(event);">
                <button id="add-item" onclick="add_item(event)">Add Item</button>
            </div>
            <div id="item-list">
            </div>
        </div>
        <div id="list-2">
            <div id="add-item-div-2">
                <input type="text" name="add-item-text-2" onkeypress="onEnterInInput(event);">
                <button id="add-item-2" onclick="add_item_2(event)">Add Item</button>
            </div>
            <div id="item-list-2">
            </div>
        </div>
        <div id="item-template-container" class="item-template-container" draggable="true" ondragstart="listItemDragStartHandler(event);" ondragover="dragoverHandler(event);" ondragend="dragEndHandler(event);" ondragleave="dragLeaveHandler(event);" ondrop="onDropHandler(event);">
            <div class="item-list-element">
                <div class="item-text"></div>
                <div class="delete-item-div">
                    <button class="delete-item" onclick="delete_item(event);">Delete</button>
                </div>
            </div>
        </div>
        <div id="empty-node">&nbsp;</div>
    </div>
</body>
</html>

描述错误的视频:https://www.youtube.com/watch?v=Uz_ukKdg1O0&feature=youtu.be

注意:我观察了 Mac 上的行为,不确定 Linux 和 Windows。

【问题讨论】:

动画在 jsfiddle 中不起作用。 它可以在带有 chrome 的 pc 中正常工作。 好的,但是你可以在视频中看到它发生在 Mac 的所有浏览器上。我应该制作另一个视频吗? 这很奇怪。我不知道。 嗨,我在我的 Mac 上的 Chrome 上看到了这个问题,但在 Safari 上根本没有拖动图像,因为你使用document.body.append(),你应该改用document.body.appendChild(),它会工作更多浏览器。我知道它不能解决你的问题,但如果你想对 Mac 用户友好,你可以尝试支持 Safari ;) 【参考方案1】:

在您的 dragend 事件处理程序中,您需要检测鼠标是否位于要放入的列表中的灰色框上。请注意,您需要编写查询元素 grayBoxInOtherList 的逻辑,但这就是您想要的需要做的:

if ($(grayBoxInOtherList).parent().find(":hover")) 
    listContainer.insertBefore(source, grayEmptyNode);

仅当您将鼠标悬停在 grayBoxInOtherList 上时才执行此操作:listContainer.insertBefore(source, grayEmptyNode);

【讨论】:

用户体验不需要用户将鼠标悬停在灰色框上,用户可以选择从浏览器的任何位置离开。所以这个解决方案不是我需要的。 从您的视频中,如果您在拖动时当前没有将鼠标悬停在新列表上的灰色框上,我认为您想返回原始位置。你能澄清一下期望的行为吗?

以上是关于如何在拖放中停止 dragend 的默认行为?的主要内容,如果未能解决你的问题,请参考以下文章

如何倾斜/旋转在javascript拖放中拖动的项目?

如果图像是链接,如何从拖放中获取图像 URL?

在 ember 中,如何将视图数据从拖放中传递?

在 Angular Material CDK 拖放中,如何防止项目随着元素移动而自动重新排列?

Qt拖放中的热点是啥意思?

在 Qt 拖放中使用 QMimeData 传递数据