当前位置: 首页 > Web前端 > HTML5

浏览器拖动API全解析

时间:2023-04-05 14:52:32 HTML5

前言拖动api是HTML5添加的API,提供了元素拖动的原生支持拖动API相对于其他的事件API复杂了一些,原因就在于拖动是被拖动元素与被放置元素的互动涉及到了两个元素,而不是一个导致流程的复杂,但是其中的很多设计是有规律可循的.现在我们来看一个基本的使用场景,这里有两个元素A和B,A是可拖动元素而B是可放置元素,我们将A放置到B中鼠标按下后拖动元素A,触发A的dragstart事件开始移动A元素刚刚触碰到B容器,触发B的dragenter事件移动到B上方触发B容器的dragover事件,该事件类似于mouseover事件是连续触发的松开鼠标放置A触发B容器的drop事件触发A元素的dragend事件基本流程我们来实现上面的几个步骤html<div id="test" draggable="true"></div><div id="display"> 目标区域</div>注意:被拖动的元素需要设置draggable属性csshtml,body { height: 100%;}html,body,div { margin: 0; padding: 0; box-sizing: border-box;}#test { margin: 0 auto; margin-top: 10%; width: 50%; height: 50%; background: #4CB8C4; background: linear-gradient(to right, #3CD3AD, #4CB8C4); box-shadow: 0 0 15px -3px black;}#display { margin: 0 auto; margin-top: 10%; width: 30%; height: 30%; background-color: rgba(0, 0, 0, 0.055);}javascriptlet dargElem = document.getElementById('test');let targetElem = document.getElementById('display');dargElem.addEventListener('dragstart', (event) => { // 被拖动元素开始事件 console.log(event);})// 拖动结束事件dargElem.addEventListener('dragend', (event) => { console.log('拖动结束事件');})// 进入容器的事件targetElem.addEventListener('dragenter', (event) => { event.preventDefault(); console.log('拖动进入事件');})// 悬浮到容器上方的事件targetElem.addEventListener('dragover', (event) => { event.preventDefault();})// 拖动事件targetElem.addEventListener('drop', (event) => { console.log('触发放置事件')})这个例子中dargElem保存的是被拖动元素,我们为他注册了dragstart和dragend事件,另外一个变量targetElem是容器元素我们为他注册了dragenter dragover drop 事件控制台输出:开始拖动拖动进入事件触发放置事件拖动结束事件注意:这个例子中使用了event.preventDefault()用于阻止默认的行为,例如拖动a标签的时候有可能触发默认的跳转行为所有事件列表(不包含厂商事件和浏览器不支持事件):被拖动的对象dragstart 拖动开始触发drag 拖动过程中触发 连续触发dragend 拖动结束后触发被当作容器的元素dragenter 元素进入时候触发dragover 可拖动元素置于容器元素上方时触发 连续触发drop 可拖动元素置入到容器元素中触发dragleave 可拖动元素移动出容器元素时候触发注意:容器元素一定需要监听dragover事件并且一开始调用event.preventDefault()后续的drop才会触发dragover相当于是一个过滤器,只允许指定的元素可以触发drop事件数据的交互前面说道拖动事件是两个元素的互动,但是仅仅使用事件是不够的,我们需要其他的手段在两个元素之间传递信息在dragstart事件触发的时候所提供的event对象中含有一个DataTransfer对象,这个对象允许在多个事件中保存信息且在所有拖动事件中存在简单信息传递操作:被拖动元素调用该对象的setData()方法设置值容器元素在drop事件中使用event对象的DataTransfer属性的getData()方法获取在dragstart事件中设置的值例子:let dargElem = document.getElementById('test');let targetElem = document.getElementById('display');dargElem.addEventListener('dragstart', (event) => { event.dataTransfer.setData('test',[1,2,3]);})// 必须设置targetElem.addEventListener('dragover', (event) => { event.preventDefault();})// 拖动事件targetElem.addEventListener('drop', (event) => { event.preventDefault(); console.log(event.dataTransfer.getData('test'));})可以看到DataTransfer数据的设置就是典型的键值操作而已综合实例一个来自MDN的例子:html<div class="dropzone"> <div id="draggable" draggable="true" ondragstart="event.dataTransfer.setData('text/plain',null)"> This div is draggable </div></div><div class="dropzone"></div><div class="dropzone"></div><div class="dropzone"></div>css#draggable {width: 200px;height: 20px;text-align: center;background: white;}.dropzone {width: 200px;height: 20px;background: blueviolet;margin-bottom: 10px;padding: 10px;}javascriptvar dragged;/* 可拖动的目标元素会触发事件 */document.addEventListener("drag", function( event ) {}, false);document.addEventListener("dragstart", function( event ) { // 保存拖动元素的引用(ref.) dragged = event.target; // 使其半透明 event.target.style.opacity = .5;}, false);document.addEventListener("dragend", function( event ) { // 重置透明度 event.target.style.opacity = "";}, false);/* 放下目标节点时触发事件 */document.addEventListener("dragover", function( event ) { // 阻止默认动作 event.preventDefault();}, false);document.addEventListener("dragenter", function( event ) { // 当可拖动的元素进入可放置的目标高亮目标节点 if ( event.target.className == "dropzone" ) { event.target.style.background = "purple"; }}, false);document.addEventListener("dragleave", function( event ) { // 当拖动元素离开可放置目标节点,重置其背景 if ( event.target.className == "dropzone" ) { event.target.style.background = ""; }}, false);document.addEventListener("drop", function( event ) { // 阻止默认动作(如打开一些元素的链接) event.preventDefault(); // 移动拖动的元素到所选择的放置目标节点 if ( event.target.className == "dropzone" ) { event.target.style.background = ""; dragged.parentNode.removeChild( dragged ); event.target.appendChild( dragged ); }}, false);DataTransfer详解DataTransfer不仅仅用于元素之间信息的传递,同时可以控制拖动的样式,以及传递额外的信息属性:dropEffect 类型 StringeffectAllowed 类型 Stringfiles 类型 FileListtypes 类型 DOMStringList方法:void addElement(in Element element)void clearData([in String type])String getData(in String type)void setData(in String type, in String data)void setDragImage(in nsIDOMElement image, in long x, in long y)这里只讲解一下dropEffect和effectAllowed因为这里有坑files属性用于从浏览器外部拖入文件时候使用types使用较少这里不提了提供MDN链接,看完本文后可以查阅剩下的方法和属性:https://developer.mozilla.org...dropEffect和effectAllowed用于控制拖动的时候光标角标的样式,过滤不同的类型拖动行为dropEffect可能的值:copy: 复制到新的位置move: 移动到新的位置.link: 建立一个源位置到新位置的链接.none: 禁止放置(禁止任何操作).effectAllowed可能的值:copy: 复制到新的位置.move:移动到新的位置 .link:建立一个源位置到新位置的链接.copyLink: 允许复制或者链接.copyMove: 允许复制或者移动.linkMove: 允许链接或者移动.all: 允许所有的操作.none: 禁止所有操作.uninitialized: 缺省值(默认值), 相当于 all.简单来说在被拖动元素的事件中设置effectAllowed属性表示期待的拖动样式,而在容器元素中设置dropEffect表示容器指定的样式,其他的设置都会被忽视对于dropEffect只有在dragover事件中中修改值才会影响样式,我用的是Chrome66对于effectAllowed只有在dragstart事件中有效例子双方都使用link样式:let dargElem = document.getElementById('test');let targetElem = document.getElementById('display');// 拖动开始事件dargElem.addEventListener('dragstart', (event) => { event.dataTransfer.effectAllowed = 'link';})// 悬浮到容器上方的事件targetElem.addEventListener('dragover', (event) => { event.dataTransfer.dropEffect = 'link'; event.preventDefault();})// 拖动事件targetElem.addEventListener('drop', (event) => { console.log('pass') event.preventDefault();})这个例子中drop事件被触发例子effectAllowed设置为copy而dropEffect设置为link:let dargElem = document.getElementById('test');let targetElem = document.getElementById('display');// 拖动开始事件dargElem.addEventListener('dragstart', (event) => { event.dataTransfer.effectAllowed = 'link';})// 悬浮到容器上方的事件targetElem.addEventListener('dragover', (event) => { event.dataTransfer.dropEffect = 'link'; event.preventDefault();})// 拖动事件targetElem.addEventListener('drop', (event) => { console.log('pass') event.preventDefault();})这个例子执行的结果就是拖动时候的角标一直是禁止符号,在容器元素上松开也不会触发drop事件参考链接https://developer.mozilla.org...https://developer.mozilla.org...https://blog.csdn.net/baidu_3...