原生拖放总结

191 阅读5分钟

可拖动目标

默认情况下图片、链接、文本(需要选中)是可以拖动的,其他元素也可以拖动,但是需要设置draggable属性,表示属性是否可以拖动,图片和链接的draggable属性自动设置成了true,其他元素默认为false。当需要元素可以拖动或设置图片不可拖动时可以设置dragable属性,例如:

//可拖动的div
<div draggable="true"></div>
//不可拖动的图片
<img src="" draggable="false"></img>

拖动事件

当一个可拖动元素进行拖放时,会按顺序触发下列事件:

  1. dragstart
  2. drag
  3. dragend

当鼠标移动到元素上,按住鼠标并开始移动的那一刻会触发dragstart事件;当拖住元素移动过程中会不断触发drag事件;当松开鼠标,元素被放置到可放置目标或不可放置目标上时会触发dragend事件。

放置事件

当一个元素被放置到放置目标上时放置目标会触发以下事件:

  1. dragenter
  2. dragover
  3. dragleave或drop

当拖动元素拖动到放置目标上时会触发dragenter。

dragenter触发后会立即触发dragover事件,并且元素在放置目标范围内被拖动期间会一直持续触发。

拖动元素离开放置目标会触发dragleave事件。

当元素被放置到目标元素范围内时会触发drop事件。

自定义放置目标

当把元素拖放到无效放置目标上时,会看到一个特殊光标(圆环中间一条斜杠)。所有元素都支持放置,但是默认是不可放置的,如果把元素拖放到不可放置目标上时是不会触发drop事件的。我们可以通过覆盖dragenter和dragover的默认行为让所有元素都转换为有效的放置目标:

const box=document.querySelector(".box")
box.ondragenter=(event)=>{
    event.preventDefault()
}
box.ondragover=(event)=>{
    event.preventDefault()
}

执行上面代码后,把元素拖动到这个div元素上时,可以看到光标变成可放置的样子,并且可以触发drop事件。

在Firefox浏览器中,放置事件的默认行为是导航到放在放置目标上的URL。这意味着拖动图片会导航的图片的URL,拖动文本会导航到无效URL。为阻止这个默认行为,我们在Firefox必须阻止drop的默认行为:

box.ondrop=(event)=>{
    event.preventDefault()
}

dataTransfer对象

dataTransfer对象主要功能是进行拖拽过程中的数据传输,

属性

dropEffect

告诉浏览器允许那种放置行为,有下列值:

  • ”none“:被拖动元素不能放到这里,光标效果:圆环中间一条斜杠
  • "move":被拖动元素应该移动到放置目标,光标效果:虚线方框
  • "copy":被拖动元素应该复制到放置目标,光标效果:虚线方框下一个加号
  • "link":放置目标会导航到被拖动元素,光标效果:虚线方框下一个黑色箭头

dropEffect设置后只会有光标效果,不会自动移动、复制链接,具体移动、复制还是链接被拖动元素还需代码实现。红宝书上面写的”必须在ondragenter中设置dropEffect“,但是也可以在ondragover中设置dropEffect,并且光标会更合逻辑。例如,

  1. 如果在ondragenter中设置dropEffect,dropEffect好像并没有起作用,光标只会和允许的effectAllowed值一样,并且dropEffect为none时也能放置拖拽元素
  2. 如果在ondragover中设置dropEffect,dropEffect值如果和effectAllowed冲突,不会触发drop事件,光标为不可放置(圆环中间一条斜杠),并且dropEffect值为none时不会触发drop事件

需要注意的是:须同时设置effectAllowed,dropEffect才会生效

effectAllowed

表示被拖动元素允许的dropEffect(其实就是允许的光标效果),有如下几个值:

  • “uninitiated”:没有任何效果
  • “none”:被拖动元素没有被允许的操作
  • ”move“:只允许move的dropEffect
  • ”copy“:只允许copy的dropEffect
  • ”link“:只允许link的dropEffect
  • ”copyMove“:允许copy和move的dropEffect
  • ”copyLink“:允许copy和link的dropEffect
  • ”linkMove“:允许link和move的dropEffect
  • ”all“:允许所有dropEffect

要设置effectAllowed,必须在dragstart事件中设置。需要注意的是:如果effectAllowed的值为none时,不会触发drop

files

和fileList对象,如果拖拽的是一个文件,可以在files中获取文件信息

items

items是一个对象数组,每个对象有一个kindtype属性。当通过setData(format,data)传递的数据时,items属性会记录kind:数据的类型,type:format,结构:

[
{kind:string,type:format}
]

types

一个数组,记录传递数据的MIME类型

方法

setData(format,data)

format和data都是字符串类型。用于存储数据,只能在ondragstart中使用

getData(format)

format字符串类型。用于获取setData存储的数据

clearData(format)

format字符串类型。用于清除存储的数据,只能在ondragstart中使用,因为这是拖动操作的数据存储唯一能写入的时间

setDragImage(element,x,y)

element可以是任何元素,光标位置的图片上的x和y坐标。用来设置拖动发生时显示在光标下的图片,通常在ondragstart 事件中调用此方法。

拖动文件上传

  1. 创建一个div

    <div id="drag"></div>
    
  2. 获取div,阻止默认行为将div转换为可放置目标:

    const box=document.querySelector("#drag")
    box.ondragenter=(event)=>{
        event.preventDefault()
    }
    box.ondragover=(event)=>{
        event.preventDefault()
    }
    
  3. 在drop事件中获取文件

    box.ondrop=(event)=>{
    	const file=event.dataTransfer.files[0]//获取拖动的文件
    }
    

demo:

drag.gif

拖动元素

html结构:

<div class="flex">
  <div class="drag">
      <div draggable="true" id="div" class="div"></div>
      <p draggable="true" id="p" class="p">这是一段文本</p>
  </div>
  <div class="drop"></div>
</div>

javascript:

const drag=document.querySelector('.drag')
const drop=document.querySelector('.drop')
drag.ondragstart=(e)=>{
  e.dataTransfer.setData("id",e.target.id)
}

drop.ondragenter=(e)=>{
  e.preventDefault()
}
drop.ondragover=(e)=>{
  e.preventDefault()
}
drop.ondrop=(e)=>{
  const id=e.dataTransfer.getData("id")
  e.target.append(document.querySelector("#"+id))
  e.preventDefault();
}

demo:

dragdiv.gif