如何实现拖放功能,使得拖动子元素时也会拖动整个父元素

11

我正在为WordPress开发一个媒体导入插件。我试图实现拖放功能,以重新排序用户导入的媒体元素(音频和/或图像)。我有一个<div>标签(我们称之为媒体 div),可容纳多达三个其他<span><img><audio>标签。因此,我将所有已导入的媒体资产显示在一行中,并希望能够拖放以重新排序media divs。我没有问题使用html5实现基本的拖放。我遇到的问题是,当我点击media divs内的媒体子元素(<audio><img>)时,子元素成为拖动事件的目标。是否有任何方法可以重置拖动事件的目标,使整个media div被拖动而不仅仅是媒体元素?我已经研究过e.stopPropogation()并阅读了冒泡和捕获的相关知识,但是我尝试利用这些的每种方式都没有解决我的问题。我是否缺少了什么?如果可能的话,我更喜欢避免使用jQuery,绝不能使用任何库或框架。
简短总结:当单击子元素时,如何使父元素成为拖动事件的目标?

<div class="npr-import-media npr-import-audio npr-import-images" id="media-container">

    <div class="npr-import-media-container" draggable="true">
        <audio src="<?php echo $audio->format->mp4->{'$text'}; ?>" controls></audio>
        <span class="npr-media-delete">X</span>
    </div>

    <div class="npr-import-image-container npr-import-media-container" draggable="true">
        <img class="npr-import-image" src="<?php echo $image->src; ?>" >
        <span class="npr-import-image-caption"><?php echo $image->caption->{'$text'} ?></span>
        <span class="npr-media-delete">X</span>
    </div>

    <div class="npr-import-add-media npr-import-media-container">
        Add Media+
    </div>

</div>

这是我的代码的HTML部分。原本还有一些PHP功能来循环遍历原始源材料以显示从原始文章导入的所有媒体元素,但我认为没有必要包括它们。


为什么不让父级元素可拖动呢? - mst4ash
@MustafaShujaie 父元素(即 _media div_)是可拖动的。我设置了 draggable = true。但是当我点击 media div 的子元素时,它也必须是可拖动的,因为它成为了被拖动的对象,而不是整个 _media div_。 - heavygl0w
请插入您的HTML标记。 - mst4ash
据我理解,您想重新排序媒体div元素,对吗?此外,您不需要使子元素可拖动。当您单击子元素并开始拖动时,事件将冒泡到父元素,并由分配给它的事件处理程序处理。 - mst4ash
@MustafaShujaie 我也是这么想的,但是不管我怎么尝试启用/禁用冒泡或捕获,都没有改变任何东西。而且无论我如何尝试使用 stopPropagation(),都没有改变任何东西。我现在想要使用捕获,并在 ondragstart 事件开始时检查 ev.target.className 是否是我想要的。如果不是,我将退出函数并让捕获处理其余部分。 - heavygl0w
现在我想我理解了你的问题:),请看看我的答案是否解决了你的问题! - mst4ash
6个回答

9

我也遇到了这个问题。没有使用 JavaScript,我通过在每个子元素中添加 draggable="false" 来解决它。

<div class="npr-import-image-container npr-import-media-container" draggable="true" ondragstart="drag(event)" style="border:1px solid blue;padding:10px;">
    <img draggable="false" class="npr-import-image" src="img.jpg" >
    <span draggable="false" class="npr-import-image-caption">Caption</span>
    <span draggable="false" class="npr-media-delete">X</span>
</div>

2
我曾经遇到过同样的问题,无法处理img标签,所以转而使用了div标签。
您可以使用

<div style="background-image: url('img.jpg')"></div>

替代

<img class="npr-import-image" src="img.jpg" >

1
您可以将ondragstart事件的处理程序设置为返回false,如下所示:
<div class="npr-import-media npr-import-audio npr-import-images" id="media-container">
    <div class="npr-import-image-container npr-import-media-container" draggable="true" ondragstart="drag(event)" style="border:1px solid blue;padding:10px;">
        <img class="npr-import-image" src="img.jpg" >
        <span class="npr-import-image-caption">Caption</span>
        <span class="npr-media-delete">X</span>
    </div>

    <div class="npr-import-add-media npr-import-media-container">
        Add Media+
    </div>
</div>
<script>
function drag(e) {
    console.log(e);
}
var images = document.getElementsByTagName('img');
for(i = 0 ; i < images.length; i++) 
    images[i].ondragstart = function() { return false; }
</script>

在控制台输出中,您可以看到:
>> DragEvent {isTrusted: true, dataTransfer: DataTransfer, screenX: 66, screenY: 161, clientX: 66}

更新 通过在父元素上设置拖动事件处理程序,可以通过如上所示的HTML属性或通过以下代码附加事件侦听器:

document.getElementById('draggable').addEventListener('dragstart', function(e) {
   console.log(e);
});

2
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - heavygl0w

0
也许有点晚了,但我发现了一种简单的方法来获取具有特定CSS类的最近父元素。
根据你的示例尝试使用。
    const handleDragEnter = (e) => {
    let desiredTarget = e.target.closest('.npr-import-image-container');
    console.log(desiredTarget);
     // do stuff...
}

0

对于Orion's answer的进一步解释,当拖动一个元素穿过页面时,一旦鼠标遇到可拖动的元素,立即用户选择将会改变。一旦发生这种变化,它将为先前的当前目标元素触发一个dragleave事件,并将其更新为您正在悬停的当前元素 - 实质上是新的放置目标。由于图像(和链接)默认情况下是可拖动的元素,因此当它们是您的拖放解决方案的子元素时,可能会出现问题。

这就是为什么在图像上使用draggable="false"可以禁用原生功能的原因;然而,我发现将pointer-events: none CSS属性应用于图像也可以起作用。虽然,如果您使用CSS解决方案,您可能需要JS来切换该样式,以便在图像本身上实现悬停效果。提供这些信息只是为了防止无法更新图像属性的情况。


0
我面临了类似的情况。我有一个带有两个子元素(抓手和内容)的父元素,需要在拖动抓手时整个父元素一起被拖动,但是当拖动内容时不能拖动整个父元素。
让父元素和内容都可以拖动,但在内容的 ondragstart 事件中取消拖动,这样只有当鼠标不在内容区域(而是在抓手上)时才会开始拖动。

  const catchAndPreventDragStartEventGettingToParent = (event) => {
    //prevent drag behaviour in content item, 
    console.log("no dragging");
    event.preventDefault();
  };

  const dragStart = (event) => {
    console.log("dragging");
    //event.dataTransfer.effectAllowed = "move";
    //event.dataTransfer.setData("text/plain", JSON.stringify(data));
  };
#parent{
  padding: 1rem;
  background-color:teal;
}

.gripper{
  margin: 1rem;
  padding:1rem;
  background-color: linen;
}
.content{
  margin: 1rem;
  padding:1rem;
  background-color: pink;
}
<div 
  id="parent"
  draggable = true
  ondragstart= "dragStart(event)">
  <span class ="gripper">Gripper:</span>
  <span
    class = "content"   
    draggable= true 
    ondragstart="catchAndPreventDragStartEventGettingToParent(event)">
    CONTENT
  </span>
</div>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接