当前位置:首页 > 数据库

让拖拽更加人性化-如何自定义 Dragover 样式?

在 web 开发中,让拖经常会碰到需要拖拽的拽更场景。为了更好的加人体验,拖拽区域需要有一定的性化变化提示,告诉用户:"现在可以放在这里了~",何自例如这样的定义。

dragover效果

这次接着探索一下如何自定义 dragover 样式。样式

一、让拖dragenter 和 dragleave

要实现这样的拽更效果,少不了和dragenter和dragleave打交道。加人

当拖动的性化元素进入有效的放置目标时, 将会触发dragenter 事件。何自当拖动的定义元素离开有效的放置目标时,将会触发dragleave 事件。样式

拖拽目标和放置目标

假设现在有这样一个结构,让拖这里 img是拖拽目标,div.content是放置目标。

<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的过程中,肯定会触发dragenter和dragleave这两个事件,如下:

dragenter和dragleave

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

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;

}

效果如下:

dragover效果

站群服务器不是非常容易呢?

实际使用起来其实还存在很多局限性,下面一一介绍。

二、当放置目标有子元素时

大部分情况下,放置目标并不是空的,还有其他子元素,如果采用上面的方式就会有问题了,假设布局是这样的,为了区分,可以给需要放置的元素添加一个属性,比如allowdrop,表示允许放置;

不允许放置</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](empty::after{ "allowdrop") *{

pointer-events: none;

}

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

有子元素的情况,完美

三、多层嵌套放置目标

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

不允许拖拽</div>

如果按照 CSS 的云服务器提供商处理方式(JS 方式没有问题),由于所有子元素都被禁用,里面的结构自然也无法响应了。

多层嵌套结构无响应

那如何让里面的放置目标可以响应呢?其实只需要改一下上面的 CSS 即可,如下:

.content[allowdrop](empty::after{ "allowdrop")>*: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()

}

})

效果如下(注意观察鼠标的变化

分享到:

滇ICP备2023006006号-16