html5 drag 拖放原理

浏览器坐标系

浏览器中大概有以下几个坐标系:

  • screenX, screenY,以显示器(屏幕)为坐标系的坐标
  • clientX, clientY(简写是 x,y),以浏览器窗口(可视区域)为坐标系的坐标
  • pageX, pageY,以文档窗口(可滚动)为坐标系的坐标
  • offsetX, offsetY,以元素为坐标系的坐标
  • layerX, layerY,返回点击的目标对象相对于当前层的坐标

screenX, screenY

clientX, clientY

这两个值是指可视区域,并且从可视区域的左上角开始计算距离。就算页面存在滚动,也不管。那么谁管呢,pageX, pageY 管!如果页面不存在滚动,client 和 page 的值是一样的。如果垂直方向上存在滚动,那么有以下公式:

js 复制代码
pageY = scrollTop + clientY

offsetX, offsetY

如果点击粉色快,则 offsetX, offsetY 是以自己本身左上顶角为原点坐标计算当前值。

layerX, layerY

  • 返回相对点击的元素相对于其父级元素(有定位属性)的坐标
  • 如果点击的元素以及父级元素都没有定位属性,那就返回相对于body标签的坐标
  • 如果点击的元素自身有定位属性返回的就是相对于自身的坐标

拖放与坐标系之间有什么关系呢,因为以前是通过这些坐标系进行模拟拖放的,但是现在 Html5 中已经有了相应的拖放 api 了,变的十分方便了。

drag 事件

问?将大象装进冰箱需要几步?

  • 首先你得有大象和冰箱
  • 再者执行装这个操作

如果我们将此问题类比与拖放事件,那么拖放事件也得经过两个步骤:

  • 大象------被拖放的元素,将想要拖放的元素的 draggable 设为 truedraggable="true"。这样才能将元素进行拖放。另外 imga 标签默认允许拖放。
  • 冰箱------目标元素并执行装这个操作------编写拖放有关的代码。

执行装这个操作可以细分为:

  • 大象开始拖放
  • 大象拖放中
  • 大象开始进入冰箱
  • 大象完全进入冰箱,还翻了个身
  • 大象从冰箱出来了
  • 大象被放进了冰箱
  • 关门

那么用拖放有关的 api 描述则为:

  • 大象开始拖放------dragstart
  • 大象拖放中------drag
  • 大象开始进入冰箱------dragenter
  • 大象完全进入冰箱,还翻了个身------dragover
  • 大象从冰箱出来了------dragleave
  • 大象被放进了冰箱------drop
  • 关门------dragend

那么动作的发生一定和主体有关,那么这些 api 的主体就不是一样的。

我们将大象称为 drag 元素,冰箱称为 drop 元素

  • drag 元素------dragstart------开始拖放操作
  • drag 元素------drag------拖放过程中
  • drop 元素------dragenter------被拖放的元素进入目标元素范围(更精确的是鼠标进入)
  • drop 元素------dragover------被拖放的元素在目标元素内移动
  • drop 元素------dragleave------被拖放的元素离开目标元素范围
  • drop 元素------drop------被拖放的元素放在目标元素范围
  • drag 元素------dragend------拖放操作结束

案例

代码如下:

html 复制代码
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>drag</title>
    </head>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-boxss;
        }

        .link {
            display: block;
            margin: 20px auto;
            width: 40px;
            height: 20px;
            text-align: center;
            color: #fff;
            background-color: pink;
        }
        .box {
            margin: 20px auto;
            width: 100px;
            height: 100px;
            border: 1px solid;
        }
        .text {
            text-align: center;
            font-size: 30px;
        }
    </style>
    <body>
        <a class="link" href="https://juejin.cn/" draggable="true">掘金</a>

        <div class="box"></div>
    </body>

    <script>
        let source = document.querySelector('.link')

        let target = document.querySelector('.box')

        source.addEventListener('dragstart', event => {
            event.dataTransfer.setData('text/plain', event.target.className)
        })

        target.addEventListener('dragover', event => {
            // 浏览器默认是不允许拖放,要阻止默认事件
            event.preventDefault()
        })

        target.addEventListener('drop', event => {
            // 阻止默认事件
            event.preventDefault()
            const className = event.dataTransfer.getData('text/plain')
            target.appendChild(document.querySelector(`.${className}`))
        })
    </script>
</html>

整体思路就是:

  • dragstart 时,保存一些数据到 dataTransfer,使用的是 setData
  • drop 时,获取保存的数据,得到元素,插入到目标元素中。

这里面出现了一些新的属性:

  • dataTransfer:对象用于保存拖动并放下过程中的数据,它可以保存一项或多项数据,这些数据项可以是一种或者多种数据类型
  • dataTransfer.setData(format, data) 方法用来设置拖放操作的数据和类型。设置的类型 format要与保存时 getData 一致,不然取不到值的。

要特别注意的是:

  • 浏览器默认不允许拖放,因此在目标元素上要在 dragover 时,阻止这一特性,只能在这个周期。

dataTransfer.dropEffect,dataTransfer.effectAllowed

  • dropEffect:属性控制在拖放操作中给用户的反馈(copy、move、link、none)。项目可能禁止拖放
  • effectAllowed:属性指定拖放操作所允许的一个效果

这两个属性如果要设置的话,需要注意一下,可能会导致无法拖放。 设置为 none 时,也无法拖放。

dataTransfer 更详细的信息

相关推荐
猫不易2 分钟前
Webpack 与 Vite:从 Loader / Plugin 到 Rolldown 统一引擎
前端·vite
YHL14 分钟前
🎯 Danci —— 用 AI 驱动开发一个全栈英语单词学习平台
前端·后端
平头哥~15 分钟前
Day 20 _ 3D 翻卡_perspective 写错人,卡片就穿帮
前端·3d·css3·学习资料
小聪70823 分钟前
elpis-core 前端 Webpack 工程化实践
前端
2601_9539880724 分钟前
Ricon组态实时监控 - 毫秒级数据可视化
前端·物联网·数学建模·信息可视化·架构·前端框架
V158897262011 小时前
从滴滴模式看机器人租赁:撮合型租赁平台开发源码的调度系统设计思路
linux·前端·机器人
szarron1 小时前
国产手持式频谱分析仪选型攻略:TFN RC系列 vs HTOOL SA8T频谱分析仪 专业参数对比(军工/路测/调试全覆盖)
开发语言·前端·状态模式
开开心心就好1 小时前
免费桌签打印工具,支持批量导入名字
前端·javascript·人工智能·docker·jupyter·智能手机·语音识别
涛涛ing1 小时前
2026 年 9 月,整个 npm 生态的「心脏」都被 Rust 换了
前端
IT_陈寒1 小时前
明明设了默认值,为什么我的JavaScript函数参数还是undefined?
前端·人工智能·后端