乐趣区

关于前端:让拖拽更加人性化如何自定义dragover样式

欢送关注微信公众号:前端侦探

在 web 开发中,常常会碰到须要拖拽的场景。为了更好的体验,拖拽区域须要有肯定的变动提醒,通知用户:” 当初能够放在这里了~”,例如这样的

之前在这篇文章讲述了如何自定义 drag 款式,这次接着摸索一下如何自定义 dragover 款式。

一、dragenter 和 dragleave

要实现这样的成果,少不了和 dragenterdragleave打交道。

当拖动的元素 进入 无效的搁置指标时,将会触发dragenter 事件

当拖动的元素 来到 无效的搁置指标时,将会触发dragleave 事件

假如当初有这样一个构造,这里 img是拖拽指标,div.content是搁置指标。

<img>
<div class="content"></div>

而后在 document 监听一下

document.addEventListener('dragleave', function(ev) {console.log('dragleave', ev.target)
})
document.addEventListener('dragenter', function(ev) {console.log('dragenter', ev.target)
})

那么,将 img 拖入 div.content 的过程中,必定会触发 dragenterdragleave这两个事件,如下

如果页面比较简单,要自定义拖拽过程就比拟容易了

document.addEventListener('dragleave', function(ev) {ev.target.toggleAttribute('over',false);
})
document.addEventListener('dragenter', function(ev) {ev.target.toggleAttribute('over',true);
})

通过增加 over 属性自定义款式

.content[over]{outline: 4px solid slateblue;}

成果如下

是不是非常容易呢?

理论应用起来其实还存在很多局限性,上面一一介绍

二、当搁置指标有子元素时

大部分状况下,搁置指标并不是空的,还有其余子元素,如果采纳下面的形式就会有问题了,假如布局是这样的,为了辨别,能够给须要搁置的元素增加一个属性,比方allowdrop,示意容许搁置

<img>
<div class="content" allowdrop>
    <div> 不容许搁置 </div>
</div>

这里通过属性辨别一下

document.addEventListener('dragleave', function(ev) {if (ev.target.getAttribute('allowdrop')!==null) {ev.target.toggleAttribute('over',false);
  }
})
document.addEventListener('dragenter', function(ev) {if (ev.target.getAttribute('allowdrop')!==null) {ev.target.toggleAttribute('over',true);
  }
})

成果如下

能够看到,当拖拽指标通过子元素时,里面的款式曾经失落了。起因其实很简略,在通过子元素时,搁置指标也触发了 dragleave 事件!

那有没有方法不触发呢?这里有两种形式:

首先能够勾销 dragleave 的监听,因为在执行 dragleave 时,元素自身是不晓得行将进入哪一个区域 ,很容易“误伤”。取而代之的是每次dragenter 时,先移除上一次搁置指标的属性,而后再增加新的,有点相似选项卡的操作,具体实现如下:

var lastDrop = null;
document.addEventListener('dragenter', function(ev) {if (lastDrop) {lastDrop.toggleAttribute('over',false);
  }
  const dropbox = ev.target.closest('[allowdrop]'); // 获取最近的搁置指标
  if (dropbox) {dropbox.toggleAttribute('over',true);
    lastDrop = dropbox;
  }
})

还有另一种形式:借助 CSS 就非常容易了

这里有一个非常简单粗犷的形式,间接将子元素禁用鼠标响应,如下

.content[allowdrop][over] *{pointer-events: none;}

这样,在滑过任何子元素都不会有响应了,完满😁

三、多层嵌套搁置指标

下面这种形式其实能够解决大多数问题了,毕竟大部分场景都是扁平的。不过有时候也会碰到多层构造,比方那种可视化编辑工具,尤其是目前比拟火的低代码平台,就会波及到多层构造,假如 HTML 是这样的

<img>
<div class="content" allowdrop>
  <div class="content" allowdrop></div>
    <div class="content"> 不容许拖拽 </div>
  <div class="content" allowdrop></div>
</div>

如果依照 CSS 的解决形式(JS 形式没有问题),因为所有子元素都被禁用,外面的构造天然也无奈响应了

那如何让外面的搁置指标能够响应呢?其实只须要改一下下面的 CSS 即可,如下

.content[allowdrop][over]>*:not([allowdrop]){pointer-events: none;}

这里应用了 > 选择器,示意只抉择子元素,不蕴含后辈元素,而后排除掉 搁置指标,这样就能实现多层嵌套了,成果如下

是不是出其不意的简略呢?

四、其余交互细节

不晓得大家发现没,下面的例子在拖拽开始,鼠标就始终处于这种“可搁置”状态,不论是在搁置指标内部还是外部,如下

这是因为设置了 dragover 属性,所以整个 document 都变成了可搁置指标,都容许触发 drop 事件

document.addEventListener('dragover', function(ev){ev.preventDefault()
})

如果心愿交互更加细腻,体验更好,那么在鼠标批示上也能够进一步的优化,能够在进入搁置指标后才变成这种状态,实现如下

document.addEventListener('dragover', function(ev){const dropbox = ev.target.closest('[allowdrop]');
  if (dropbox) {ev.preventDefault()
  }
})

成果如下(留神察看鼠标的变动🔽)

除此之外,还应该在 drop 完结后移除掉 over 属性

document.addEventListener('drop', function(ev){const dropbox = ev.target.closest('[allowdrop]');
  if (dropbox) {dropbox.toggleAttribute('over',false);
  }
})

这样就实现了一个齐全通用的自定义 dragover成果,区区数十行,划重点,残缺代码如下:

document.addEventListener('dragover', function(ev){const dropbox = ev.target.closest('[allowdrop]');
  if (dropbox) {ev.preventDefault()
  }
})

document.addEventListener('drop', function(ev){ev.target.toggleAttribute('over',false);
})

document.addEventListener('dragleave', function(ev) {if (ev.target.getAttribute('allowdrop')!==null) {ev.target.toggleAttribute('over',false);
  }
})
document.addEventListener('dragenter', function(ev) {if (ev.target.getAttribute('allowdrop')!==null) {ev.target.toggleAttribute('over',true);
  }
})

// 或者以下形式,无需 dragleave,无需额定 CSS
var lastDrop = null;
document.addEventListener('dragenter', function(ev) {if (lastDrop) {lastDrop.toggleAttribute('over',false);
  }
  const dropbox = ev.target.closest('[allowdrop]'); // 获取最近的搁置指标
  if (dropbox) {dropbox.toggleAttribute('over',true);
    lastDrop = dropbox;
  }
})

当然还少不了 CSS 的配合,同样重要

[allowdrop]:empty::after{content: '拖放此处';}
[allowdrop][over]:empty::after{content: '松开搁置';}
[allowdrop][over]{/* 自定义款式 */}
[allowdrop][over]>*:not([allowdrop]){pointer-events: none;}

这里有个 CSS 小技巧,下面例子在拖放过程中的文字提醒变动其实是通过伪元素实时变动的~

你也能够查看在线链接:自定义 dragover (codepen.io)或者自定义 dragover (juejin.cn)

另外,如果须要齐全自定义拖拽,能够参考这个我的项目:https://github.com/XboxYan/draggable-polyfill,十分轻量,100 来行代码,不影响业务逻辑,非常适合学习和时应用,欢送 star~

五、总结和阐明

以上就是自定义 dragover 成果的残缺实现了,不算简单,但也有一些小技巧,特地是借助了 CSS 的能力。其实在这一版实现之前,我还尝试过很多别的实现,但都不如这种形式简洁明了,上面总结一下:

  1. 为了更好的体验,能够在拖拽过程中给与用户适当的变动提醒
  2. 次要实现办法在于 dragenter 和 dragleave
  3. 当搁置指标存在子元素时,也会触发 dragleave 事件,烦扰原有逻辑
  4. 能够移除 dragleave 去除子元素的烦扰,dragenter 须要先移除再增加 over
  5. 通过 CSS pointer-events 能够去除子元素的烦扰
  6. 如果有多层可搁置构造,能够通过 :not 过滤可搁置指标
  7. 通过鼠标指针也能够改善交互体验
  8. 在 DOM 操作中千万不要遗记了 CSS,这点很重要

当然,拖拽在页面中的交互细节还有很多,比方拖拽排序过程中的挤压动画成果,前面有空再钻研吧,争取出一个通用的解决方案。最初,如果感觉还不错,对你有帮忙的话,欢送点赞、珍藏、转发❤❤❤

欢送关注微信公众号:前端侦探

退出移动版