Skip to content

原生拖放 ​

被拖拽元素 ​

默认情况下,图像、链接和文本是可以拖动的,也就是说,图像和链接的 draggable 属性自动被设置成了 true,而其他元素这个属性的默认值都是 false。用户就可以拖动它们。文本只有在被选中的情况下才能拖动,而图像和链接在任何时候都可以拖动。

img
链接默认可拖动
双击选择文本可拖动

要想让其他元素可拖动,或者让图像或链接不能拖动,都可以设置这个属性。

html
<!-- 让这个图像不可以拖动 -->
<img src="smile.gif" draggable="false" alt="Smiley face" />

<!-- 让这个元素可以拖动 -->
<div draggable="true">...</div>

拖动某元素时,将依次触发 下列事件: dragstart -> drag -> dragend

js
const dragElement = document.getElementById('dragElement')

function onDragStart(event) {
  console.log(event.type, event)
}

function onDrag(event) {
  event.preventDefault()
  console.log(event.type, event)
}

function onDragEnd(event) {
  console.log(event.type, event)
}

dragElement.addEventListener('dragstart', onDragStart, false)
dragElement.addEventListener('drag', onDrag, false)
dragElement.addEventListener('dragend', onDragEnd, false)
  • dragstart: 按下鼠标键并开始移动鼠标时,会在被拖放的元素上触发 dragstart 事件。
  • drag: 触发 dragstart 事件后,随即会触发 drag 事件,而且在元素被拖动期间会持续触发该事件。
  • dragend: 当拖动停止时(无 论是把元素放到了有效的放置目标,还是放到了无效的放置目标上),会触发 dragend 事件。

目标元素 ​

当某个元素被拖动到一个有效的放置目标上时,下列事件会依次发生: dragenter -> dragover -> dragleave 或 drop

  • dragenter: 只要有元素被拖动到放置目标上,就会触发 dragenter 事件(类似于 mouseover 事件)。
  • dragover: 在被拖动的元素还在放置目标的范围内移动时,就会持续触发该事件。
  • dragleave 或 drop :如 果元素被拖出了放置目标,dragover 事件不再发生,但会触发 dragleave 事件(类似于 mouseout 事件)。如果元素被放到了放置目标中,则会触发 drop 事件而不是 dragleave 事件。

虽然所有元素都支持放置目标事件,但这些元素默认是不允许放置的。

如果拖动元素经过不允许放置的元素,无论用户如何操作,都不会发生 drop 事件。 不过,你可以把任何元素变成有效的放置 目标,方法是重写 dragenter 和 dragover 事件的默认行为。

js
const dropElement = document.querySelector('.drop')

//  进入可放置元素
function onDragEnter(event) {
  event.preventDefault()
  console.log(event.type, event)
}

// 在放置元素中移动 持续触发
function onDragOver(event) {
  event.preventDefault()
  // console.log(event.type, event);
}

//  移出可放置元素
function onDragLeave(event) {
  event.preventDefault()
  console.log(event.type, event)
}

// 在放置元素中停止拖拽
function onDrop(event) {
  console.log(event.type, event)
}

dropElement.addEventListener('dragenter', onDragEnter, false)
dropElement.addEventListener('dragover', onDragOver, false)
dropElement.addEventListener('dragleave', onDragLeave, false)
dropElement.addEventListener('drop', onDrop, false)

event.preventDefault()重写 dragenter 和 dragover 事件的默认行为,不然无论用户如何操作,都不会发生 drop 事件。

dataTransfer ​

除非数据受影响,否则简单的拖放并没有实际意义。 dataTransfer 对象可以为每种 MIME 类型都保存一个值。换句话说,同时在这个对象中保存一段文本和一个 URL 不会有任何问题。

DataTransfer 对象出现在拖拽事件中,具体包括开始拖拽 dragstart 事件,拖拽进入 dragenter 事件,拖拽离开 dragleave 事件,拖拽经过 dragover 事件,拖拽释放 drop 事件以及拖拽结束 dragend 事件,在拖放事件的事件处理程序外部无法访问 dataTransfer。

在从文本框拖动文本时,浏览器会调用 setData()并将拖动的文本以"text"格式存储起来。类似地,在拖动链接或图片时,浏览器会调用 setData()并把 URL 存储起来。当数据被放置在目标上时,可以使用 getData()获取这些数据。

js
function onDrop(event) {
  // 拖拽文字
  const text = event.dataTransfer.getData('text/plain')
  // 拖拽链接
  const url = event.dataTransfer.getData('text/uri-list')
  // 获取拖拽富文本内容
  const html = event.dataTransfer.getData('text/html')
}

当然,可以在 dragstart 事件中手动调用 setData()存储自定义数据,以便将来使用。

js
/**
 * 可以在 dragstart 事件处理程序中调用 setData()
 * 手工保存自己要传输的数据,以便将来使用。
 */
function onDragStart(event) {
  event.dataTransfer.setData('custom', JSON.stringify(data))
}

/**
 * 保存在 dataTransfer 对象中的数据只能在 drop 事件处理程序中读取。
 * 如果在 ondrop 处理程序中没有读到数据,那就是 dataTransfer 对象已经被销毁,数据也丢失了。
 */
function onDrop(event) {
  // 自定义信息
  const data = event.dataTransfer.getData('custom')
}

DataTransfer.items 可以用来获取拖拽的数据信息。

js
function handleDrop(event) {
  event.preventDefault()

  const items = event.dataTransfer.items

  for (const item of items) {
    const { type, kind } = item
    console.log(`kind: ${kind}, type: ${type}`)

    if (kind === 'string') {
      if (type.includes('text/plain')) {
        item.getAsString((str) => {
          // str是纯文本
        })
      }
      if (type.includes('text/html')) {
        item.getAsString((str) => {
          // str是富文本
        })
      }
      if (type.includes('text/uri-list')) {
        item.getAsString((str) => {
          // str是uri地址
        })
      }
    }

    // 文件类型
    if (kind === 'file') {
      // 如果是图片
      if (type.includes('image/')) {
        const file = items.getAsFile()
        // file就是图片文件对象,可以上传,或者其他处理
      }
    }
  }
}

拖拽文件 ​

用户通过拖入本地文件来上传文件到服务器,是个常见的使用场景

拖拽的本地文件列表。如果拖动操作不涉及拖动文件,则此属性为空列表。

html
<div class="container container-file">
  <h4>拖拽文件到此区域</h4>
</div>
js
const fileContainer = document.querySelector('.container.container-file')

fileContainer.addEventListener('drop', handleDragFile, false)
fileContainer.addEventListener('dragenter', handleDragFile, false)
fileContainer.addEventListener('dragover', handleDragFile, false)

function handleDragFile(event) {
  event.preventDefault()
  if (event.type === 'drop') {
    // 遍历文件信息
    const files = event.dataTransfer.files || []
    console.log('files: ', files)
  }
}

拖拽排序 ​

Reference ​