摘要:拖拽元素时想改变样式,以前得靠JavaScript监听dragstart和dragend事件来回切换类名。现在CSS工作组打算整一个新活,直接搞个:drag伪类,让拖拽中的样式纯CSS就能搞定。更离谱的是还琢磨了个::drag-image伪元素,连拖拽时那张跟随鼠标的预览图都能自定义样式,再也不用折腾setDragImage那一大坨代码了。这篇文章就掰扯掰扯这俩新家伙怎么用,跟传统JS方案比到底香在哪。
这玩意儿是啥
:drag是CSS正在讨论的一个新伪类,专门用来命中正在被拖拽的元素。啥叫拖拽?就是用鼠标点着元素不松手,然后满屏幕划拉的那个过程。以前CSS压根没法感知这个状态,想做拖拽中的视觉反馈,比如半透明、加阴影、放大缩小啥的,都得靠JS在dragstart时给元素塞个类名,dragend时再给它摘掉。
硬核拖拽咋整
先拿JS这套流程走一遍,这样才能明白新伪类省了多少事。
<div class="task-list" draggable="true">
<div class="task-item">写周报</div>
<div class="task-item">开会</div>
<div class="task-item">回邮件</div>
</div>要给这整个任务面板加拖拽样式,JS得监听俩事件:
let dragPanel = document.querySelector(".task-list");
dragPanel.addEventListener("dragstart", (ev) => {
ev.target.classList.add("dragging-active");
});
dragPanel.addEventListener("dragend", (ev) => {
ev.target.classList.remove("dragging-active");
});CSS那边就等着这个类名出现:
.dragging-active {
opacity: 0.6;
box-shadow: 0 8px 20px rgba(0,0,0,0.2);
transform: scale(0.98);
cursor: grabbing;
}这么写没啥毛病,就是每次想做拖拽样式都得重复这套监听逻辑,项目里拖拽元素一多,到处都在addEventListener,看着都头大。
纯CSS横着走
有了:drag伪类之后,上面那堆JS全都可以扔了。HTML里只要给元素加上draggable=”true”,CSS直接写:
.task-list:drag {
opacity: 0.6;
box-shadow: 0 8px 20px rgba(0,0,0,0.2);
transform: scale(0.98);
cursor: grabbing;
}这玩意儿在浏览器里跑起来,只要鼠标点着元素开始拖,样式立马生效,松手就恢复,完全不需要JS介入。代码量直接从十几行压缩到五行,维护起来也省心,想改拖拽样式直接去CSS里找:drag就完事了。
偷梁换柱
拖拽的时候浏览器默认会生成一个元素的半透明“幽灵图”跟着鼠标跑,这图叫预览图像。以前想换掉它,得用dataTransfer.setDragImage方法手动造一个div塞进去:
let customPreview = document.createElement("div");
customPreview.textContent = "📁 正在移动...";
customPreview.style.cssText = `
background: #f0f0f0;
border: 2px solid #333;
border-radius: 8px;
padding: 8px 16px;
font-size: 14px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
`;
document.body.appendChild(customPreview);
event.dataTransfer.setDragImage(customPreview, 10, 10);
setTimeout(() => customPreview.remove(), 0);这代码看着就啰嗦,还得自己手动管理这个div的创建和销毁,稍微不注意就会在页面上留下垃圾节点。
换个思路
社区里有人提议搞个::drag-image伪元素,直接在CSS里定义拖拽预览图长啥样:
::drag-image {
content: "📁 正在移动...";
background: #f0f0f0;
border: 2px solid #333;
border-radius: 8px;
padding: 8px 16px;
font-size: 14px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
}这里有个细节,::drag-image是伪元素,不是伪类。伪类代表状态,比如:drag表示“正在拖拽中”这个状态;伪元素代表一个具体的东西,预览图像就是拖拽过程中实际存在的一个独立元素。按这个逻辑,自定义预览图用伪元素更贴切,它就是个可以被样式化的对象。
真要能用上这个,拖拽预览图的定制就变成了纯CSS的活,不用再跟JS里的setDragImage较劲,性能更好,代码也更干净。
