深入理解javascript原生拖放
- 格式:rtf
- 大小:135.12 KB
- 文档页数:9
前端开发中的元素拖拽实现技术随着互联网的蓬勃发展,前端开发在网页设计和实现中起着至关重要的作用。
其中,元素拖拽实现技术在用户交互体验方面扮演着重要的角色。
本文将探讨前端开发中的元素拖拽实现技术,并深入探讨其工作原理和应用场景。
一、什么是元素拖拽实现技术元素拖拽实现技术是指通过前端代码逻辑和用户交互,实现在网页中拖动元素的功能。
通过鼠标或触摸屏拖拽元素,既可以改变元素的位置,也可以实现元素之间的交互和数据传递。
这种技术主要应用于网页设计、图形编辑器、拖放购物车等功能中。
二、基于原生JavaScript实现元素拖拽在前端开发中,可以使用原生JavaScript来实现元素的拖拽功能。
通过获取拖拽元素的位置和鼠标的坐标,结合鼠标事件和DOM操作方法,可以实现元素在页面中的拖拽效果。
例如,通过监听拖拽元素的mousedown、mousemove和mouseup 事件,可以控制元素随着鼠标的移动而改变位置,从而实现元素的拖拽。
三、借助第三方库实现元素拖拽为了提高开发效率和代码可维护性,前端开发者可以借助一些开源的第三方库来实现元素拖拽。
在市面上有许多成熟和强大的拖拽库,如jQuery UI、Sortable.js 等。
这些库提供了丰富的API和预定义的样式,使得开发者可以通过简单的引入和配置,即可实现复杂的拖拽功能。
使用第三方库可以减少开发者的工作量,同时也增加了代码的可维护性和可扩展性。
四、HTML5拖放APIHTML5提供了内置的拖放API,使得元素拖拽的实现更加简单和直观。
通过使用HTML5拖放API中的draggable和droppable属性,可以轻松地将元素设置为可拖拽或可放置的目标。
拖动元素时,浏览器会自动处理拖放相关的事件,开发者只需要编写相应的逻辑代码即可。
HTML5拖放API的出现,进一步促进了元素拖拽技术在前端开发中的普及和应用。
五、元素拖拽的应用场景元素拖拽技术在前端开发中有广泛的应用场景。
JavaScript原型和原型链深度剖析知识点JavaScript是一种基于对象的编程语言,它使用原型和原型链的概念来实现继承和共享属性。
了解JavaScript原型和原型链的工作原理对于深入理解JavaScript的核心概念至关重要。
在本文中,将对JavaScript原型和原型链进行深度剖析,以帮助读者更好地理解这一知识点。
一、JavaScript原型的概念JavaScript中的每一个对象都有一个原型(prototype),它是一个指向另一个对象或null的内部引用。
当我们访问一个对象的属性时,如果该属性不存在,JavaScript会沿着原型链向上查找,直到找到该属性或到达原型链的末尾(null)。
原型可以被视为一个对象的模板,它定义了该对象的共享属性和方法。
当我们创建一个新对象时,该对象会默认继承其构造函数的原型。
通过原型,我们可以实现属性和方法的共享,避免在每个实例中重复定义相同的属性和方法。
二、JavaScript的原型链原型链是一种机制,用于实现对象之间的继承关系。
每个JavaScript对象都有一个原型,而原型本身也是一个对象,它也有自己的原型。
这样构成的链状结构就是原型链。
原型链的最终目的是让我们能够在一个对象中访问另一个对象的属性和方法。
当我们访问一个对象的属性时,JavaScript会按照从下到上的顺序沿着原型链进行查找,直到找到相应的属性或到达原型链的末尾。
这样就实现了属性的继承。
三、原型链的构建过程原型链的构建过程可以简单地概括为以下几个步骤:1. 创建一个构造函数:通过构造函数创建一个对象实例,同时该实例会自动继承构造函数的原型。
2. 给原型添加属性和方法:我们可以通过构造函数的原型属性,为其添加属性和方法。
3. 创建另一个构造函数:通过创建另一个构造函数,我们可以将其原型指向第一个构造函数的实例,从而实现继承。
4. 继续添加属性和方法:通过第二个构造函数的原型,我们可以为其添加新的属性和方法。
js 拖动的基本原理
JavaScript的拖动基本原理是通过事件处理程序来实现的。
拖动操作通常涉及三个事件:mousedown、mousemove和mouseup。
当用户按下鼠标按钮时,触发mousedown事件。
在mousedown事件处理程序中,我们可以记录鼠标当前位置和待拖动元素的初始位置。
接下来,在mousemove事件处理程序中,我们会根据鼠标移动的距离来改变待拖动元素的位置。
我们可以通过计算鼠标当前位置与初始位置之间的差值来获取拖动的距离,并将其应用到待拖动元素的CSS属性中,使其随着鼠标移动而移动。
最后,当用户释放鼠标按钮时,触发mouseup事件。
在mouseup事件处理程序中,我们可以清除mousedown事件处理程序中保存的数据,并完成拖动操作。
通常情况下,我们还需要通过一些CSS样式,如position属性和z-index属性来确保待拖动元素能够正确地显示在页面上,并在拖动过程中不被其他元素遮盖。
需要注意的是,拖动操作还可能涉及其他的一些细节,如限制元素拖动的范围和处理元素之间的碰撞等。
这些细节的实现方式会根据具体的需求而有所不同。
js拖拽实现原理-概述说明以及解释1.引言1.1 概述概述在现代的网页开发中,实现拖拽效果已经成为了一个常见的需求。
通过使用JavaScript语言,我们可以轻松地实现拖拽功能,使网页更加交互性和用户友好。
拖拽是指用户通过鼠标或触摸手势来移动一个对象或元素,使其改变位置或状态。
在Web开发中,拖拽通常用于实现诸如拖拽调整元素顺序、拖拽改变窗口布局等功能。
JS拖拽的实现原理主要涉及三个关键方面:鼠标事件监听、位置计算与更新以及元素样式调整。
通过监听鼠标事件,我们可以捕捉到用户的拖拽动作,并获取到相应的鼠标位置信息。
通过计算鼠标的位置差值和元素的初始位置,我们可以确定元素应该被移动的距离,并更新元素的位置。
同时,还需要根据拖拽的动态变化来调整元素的样式,以使用户获得即时的反馈效果。
实现拖拽效果的前提是要满足一些必要条件。
首先,需要确定拖拽的目标元素,即被拖拽的对象。
其次,需要确保目标元素的位置属性是可调整的,例如,通过设置元素的位置为"absolute"或"relative"等方式。
最后,还需要明确拖拽区域,限定用户可以拖拽的范围。
具体实现拖拽效果的步骤包括:监听鼠标的按下、移动和松开事件,以及相应的处理函数。
当鼠标按下时,记录下鼠标的初始位置和被拖拽元素的初始位置。
随后,在鼠标移动过程中,通过计算鼠标的位移差值和元素的初始位置,更新元素的位置属性,实现拖拽的效果。
最后,在鼠标松开时,取消对鼠标事件的监听,完成拖拽功能。
总之,通过了解JS拖拽的基本原理,我们可以更好地应用这一技术,为网页增加交互性和用户体验。
1.2 文章结构:本篇文章将围绕JS拖拽实现的原理展开讨论,主要分为引言、正文和结论三个部分。
引言部分主要概述了文章的背景和重要性。
我们将首先介绍拖拽操作在Web开发中的广泛应用,并探讨为什么JS拖拽实现成为了一个重要的技术。
接着,我们将简要叙述本篇文章的结构,并明确我们的目的。
js拖拽方法一、拖拽方法概述在JavaScript中,拖拽功能是用户与网页交互的一种常见方式。
通过拖拽,用户可以轻松地完成元素的重排、移动、缩放等操作。
本文将介绍JavaScript拖拽方法的分类及具体实现步骤,并给出两个实用的第三方库拖拽实例。
二、JavaScript拖拽方法分类1.原生拖拽原生拖拽是指使用JavaScript原生实现拖拽功能。
它通过监听鼠标事件(mousedown、mousemove、mouseup)来实现元素的拖拽。
2.第三方库拖拽第三方库拖拽是指使用一些成熟的库(如Dragula、Sortable.js等)来实现拖拽功能。
这些库封装了拖拽的细节,使用起来简单便捷,且具有丰富的功能和良好的可扩展性。
三、原生拖拽实现步骤1.获取元素首先,需要获取需要拖拽的元素。
可以使用getElementById、getElementsByClassName等方法获取。
2.添加事件监听器在获取到元素后,为元素添加mousedown、mousemove、mouseup 事件监听器。
3.实现拖拽功能在mousemove事件中,根据鼠标的移动方向和速度,计算元素的新位置,并更新元素样式。
4.释放事件监听器在mouseup事件中,释放mousedown事件监听器。
四、第三方库拖拽实例1.使用Dragula实现拖拽Dragula是一个轻量级的拖拽库,支持原生拖拽和惯性拖拽。
首先,引入Dragula库,然后为需要拖拽的元素添加dragula事件监听器。
具体使用方法可参考Dragula官方文档。
2.使用Sortable.js实现拖拽Sortable.js是一个基于HTML的拖拽库,支持拖拽列表、网格等多种布局。
首先,引入Sortable.js库,然后根据需求配置相关选项。
具体使用方法可参考Sortable.js官方文档。
五、拖拽应用场景拖拽功能广泛应用于网页设计中,如组件拖拽、文件上传、图片排序等。
通过使用拖拽功能,可以提高用户体验,使界面更加灵活。
原⽣javascript实现模拟拖拽事件今天学习了使⽤原⽣js来实现拖拽重点记录如下:1. 拖拽模拟:mousedown mousemove mouseup拖拽体验:在要移动的元素上,按下⿏标的左键,移动⿏标,元素跟着⿏标移动实现拖拽3. 涉及的样式?盒⼦定位1. 确定盒⼦位置公式的原理ev.pageXev.pageY 这连个是⿏标在页⾯上的位置box.offsetWidthbox.offsetHeight 盒⼦距离⽗级别参照物的距离唯⼀不变的就是,⿏标在box上的开始按下的点距离盒⼦左上⾓的位置.公式移动后⿏标位置 - 移动后box的offset值 = 移动前的⿏标位置 - 移动后box的offset值2. 如何防⽌⿏标丢失?在document上来绑定事件,这样⿏标就不会跑出去了..............全部代码<!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>Document</title><link rel="stylesheet" href="./css/reset.min.css"><style>html,body {height: 100%;overflow: hidden;}.box {position: absolute;top: 0;left: 0;width: 100px;height: 100px;background-color: red;cursor: move;font-size: 16px;text-align: center;line-height: 100px;}</style></head><body><!-- 盒⼦的⽗级参照物是body --><div class="box" id="box">box</div><script>// 将值存在⾃定义属性上box.onmousedown = down;function move(ev) {// 随时获取当前⿏标的信息,计算盒⼦的最新位置let curL = ev.pageX - this.startX + this.startL,curT = ev.pageY - this.startY + this.startT;let HTML = document.documentElement,minL = 0,minT = 0,maxL = HTML.clientWidth - this.offsetWidth,maxT = HTML.clientHeight - this.offsetHeight;// 边界判断 (0,HTML.clientWidth - this.offsetWidth)// (0,HTML.clientHeight - this.offsetHeight)curL = curL < minL ? minL : (curL > maxL ? maxL : curL);curT = curT < minT ? minT : (curT > maxT ? maxT : curT);this.style.left = curL + "px";this.style.top = curT + "px";}function down(ev) {// 存储⿏标起始位置的信息和盒⼦的起始位置存储在盒⼦的⾃定义属性上 this.startX = ev.pageX;this.startY = ev.pageY;this.startL = this.offsetLeft;this.startT = this.offsetTop;// ⿏标按下的时,给mousemove和mouseup绑定⽅法// 使⽤bind绑定this为当前元素document.onmousemove = move.bind(this);document.onmouseup = up.bind(this);// this.setCapture(); // chrome不⽀持}function up(ev) {// ⿏标抬起,将move和up的⽅法绑定的⽅法移除document.onmousemove = null;document.onmouseup = null;}</script></body></html>。
js原⽣拖拽的两种⽅法⼀.mousedown、mousemove和mouseup 拖着⽬标元素在页⾯任意位置如果要设置物体拖拽,那么必须使⽤三个事件,并且这三个事件的使⽤顺序不能颠倒。
1.onmousedown:⿏标按下事件2.onmousemove:⿏标移动事件3.onmouseup:⿏标抬起事件重点:1、⼀定要绝对定位,脱离⽂档流才可以移动。
2、绑定拖拽的元素,移动和⿏标松开后是对document的绑定,因为移动的是整个div。
3、点击:a= 获取当前⿏标坐标、b =div距浏览器距离、c = ⿏标在div内部距离=a-b。
移动:通过 a - c 建⽴⿏标与div的关系,防⽌⿏标超出div基本思路:拖拽状态 = 0⿏标在元素上按下的时候{拖拽状态 = 1记录下⿏标的x和y坐标记录下元素的x和y坐标}⿏标在元素上移动的时候{如果拖拽状态是0就什么也不做。
如果拖拽状态是1,那么元素y = 现在⿏标y - 原来⿏标y + 原来元素y元素x = 现在⿏标x - 原来⿏标x + 原来元素x}⿏标在任何时候放开的时候{拖拽状态 = 0}贴上代码:<div class="box" id="drag"></div>.box{position: absolute;width: 100px;height: 100px;background: red;cursor: move;}window.onload = function(){var drag = document.getElementById('drag');// //点击某物体时,⽤drag对象即可,move和up是全局区域,// 也就是整个⽂档通⽤,应该使⽤document对象⽽不是drag对象(否则,采⽤drag对象时物体只能往右⽅或下⽅移动)drag.onmousedown = function(event){var event = event || window.event; //兼容IE浏览器// ⿏标点击物体那⼀刻相对于物体左侧边框的距离=点击时的位置相对于浏览器最左边的距离-物体左边框相对于浏览器最左边的距离var diffX = event.clientX - drag.offsetLeft;var diffY = event.clientY - drag.offsetTop;if(typeof drag.setCapture !== 'undefined'){drag.setCapture();}document.onmousemove = function(event){var event = event || window.event;var moveX = event.clientX - diffX;var moveY = event.clientY - diffY;if(moveX < 0){moveX = 0}else if(moveX > window.innerWidth - drag.offsetWidth){moveX = window.innerWidth - drag.offsetWidth}if(moveY < 0){moveY = 0}else if(moveY > window.innerHeight - drag.offsetHeight){moveY = window.innerHeight - drag.offsetHeight}drag.style.left = moveX + 'px';drag.style.top = moveY + 'px'}document.onmouseup = function(event){this.onmousemove = null;this.onmouseup = null;//修复低版本ie bugif(typeof drag.releaseCapture!='undefined'){drag.releaseCapture();}}}}⼆.HTML5元素拖拽drag与拖放drop 元素拖拽浏览器默认允许我们拖拽图像、⽂本以及链接让其它元素被拖动也是可以实现的只需要在元素标签上添加⼀个属性<div class="box1" draggable="true" id="source"></div>拖拽事件拖拽事件应该分为两类⼀类是被拖拽元素触发的事件另⼀类是拖放⽬标元素触发的事件<div class="box1" draggable="true" id="source"></div><br><div class="box2" id="target"></div>拖拽元素拖拽元素的时候,被拖拽元素会触发以下事件dragstartdragdragend当⿏标点中元素并且开始移动时,就会触发dragstart事件(类⽐mousedown)拖拽过程中会持续不断地触发drag事件(类⽐mousemove)松开⿏标取消拖拽时就会触发dragend事件(类⽐mouseup)source.ondragstart = function(event){var e = event || window.eventconsole.log('开始拖拽');}source.ondrag = function(){console.log('拖拽中');}source.ondragend = function(){console.log('拖拽结束')}⽬标元素当拖拽的元素拖到⼀个⽬标元素上时,⽬标元素会触发以下事件dragenterdragoverdragleavedrop拖拽元素到⽬标上,就会触发dragenter事件(类⽐mouseover)当拖动元素在⽬标元素中,就会持续触发dragover事件离开⽬标元素,触发dragleave事件(类⽐mouseout)若拖放元素到了⽬标元素中(在⽬标元素中松开⿏标),就会触发drop事件⽽不会触发dragleave事件 target.ondragenter = function(){console.log('进⼊⽬标元素')}target.ondragover = function(event){var event = event || window.event;console.log('在⽬标元素中拖拽');event.preventDefault()}target.ondragleave = function(){console.log('拖放离开⽬标元素')}target.ondrop = function(event){console.log('拖放');var e = event || window.event;}注意:元素默认是不能够拖放只要我们在⽬标元素的dragover事件中取消默认事件就可以解决问题数据交换数据交换的对象就是事件对象的属性dataTransferdataTransfer的两个核⼼⽅法是setData()和getData()setData()⽤于设置数据,getData()⽤语接收数据举个拖放的例⼦:<div class="box1" draggable="true" id="source"></div><br><div class="box2" id="target"></div>.box1{width: 100px;height: 100px;background: salmon}.box2{width: 300px;height: 300px;border: 1px solid black}window.onload = function(){var source = document.getElementById('source');var target = document.getElementById('target');source.ondragstart = function(event){var e = event || window.eventconsole.log('开始拖拽');e.dataTransfer.setData('text',e.target.id);}target.ondragenter = function(){console.log('进⼊⽬标元素')}target.ondragover = function(event){var event = event || window.event;console.log('在⽬标元素中拖拽');event.preventDefault()}target.ondragleave = function(){console.log('拖放离开⽬标元素')}target.ondrop = function(event){console.log('拖放');var e = event || window.eventvar data = e.dataTransfer.getData('text');e.target.appendChild(document.getElementById(data));}}。
JavaScript实现拖放效果JavaScript实现拖放效果笔者实现该效果也是套⽤别⼈的轮⼦的。
然后厚颜⽆耻的贴别⼈的readme~,笔者为了⽅便查阅就直接贴了,有不想移步的可以看这篇。
不过还是最好请到原作者的GitHub去查看,⽀持⼀下原作者。
⽂章最后有贴dnd.js的源码。
使⽤介绍dnd.js拖放库 drag and drop不依赖任何第三⽅库的拖放库,兼容低版本浏览器,兼容移动端,⾃带常⽤动画效果,,可以拷贝上⾯vue⽂件夹中已经封装好的组件直接使⽤.安装⽅法⽅式⼀npm install dnd.js --save⽅式⼆下载项⽬中的dist/dnd.js, 然后⽤script标签插⼊到你的项⽬中, 如下这种⽅式可以通过window.dnd访问<script type="text/javascript" src="dist/dnd.js"></script>使⽤⽅法最简单的使⽤⽅法可以可以查看也可以查看import { Drag, Drop } from 'dnd.js'new Drag(element, options)new Drop(element, options)如果是使⽤⽅式⼆引⼊的话, 可以这样使⽤var Drag = dnd.Dragvar Drop = dnd.Dropnew Drag(element, options)new Drop(element, options)最⼩demo代码展⽰<!DOCTYPE html><html><head><meta charset="utf-8"><title>最⼩化demo</title><script type="text/javascript" src="../dist/dnd.js"></script><style>html, body { width:100%; height:100%; }body { margin:0; padding:20px; box-sizing:border-box; }.drop { margin:20px auto; width:100%; height:100px; border:1px solid #000; }</style></head><body><div class="drop"></div><div class="drag">拖动我到上⽅框框</div></body><script>var Drag = dnd.Dragvar Drop = dnd.Dropnew Drag('.drag')new Drop('.drop', {onDrop: function (params) {params.el.appendChild(params.sourceNode)}})</script></html>参数说明创建new Drag(element, options)new Drop(element, options)参数是否必填类型说明element是String or htmlElementObject可以传⼊类名class 或者 id名或者 dom节点options是Object相关参数和回调函数,具体说明看下⾯options说明 (Drag)例如:let element = '.drag-container'let options = {data: '本次拖动希望传递给Drop对象的参数',onDragStart: function (params) {console.log('监听到拖动开始')},onDragEnd: function (params) {console.log('监听到拖动结束')}}new Drag(element, options)options属性说明:参数是否必填类型说明data否任意本次拖动希望传递给Drop对象的参数onDragStart否Function拖动开始回调函数onDragEnd否Function拖动结束回调函数options说明 (Drop)例如:let element = '.drop-container'let options = {name: '当前Drop对象的名字',onDragStart (params) {console.log('监听到拖动开始')},onDragEnter (params) {console.log('监听到被拖元素进⼊')},onDragOver (params) {console.log('监听到被拖动元素在⾃⼰上⽅移动')console.log('这个函数会被连续调⽤')},onDragLeave (params) {console.log('监听被拖动元素离开')},onDrop (params) {console.log('监听到被拖动元素在⾃⼰上⽅放下')},onDragEnd (params) {console.log('监听到拖动结束')}}new Drop(element, options)options属性说明:属性是否必填类型说明回调函数参数说明name否String定义当前Drop对象的名字-onDragStart否Function拖动开始时调⽤见下⽅说明回调函数的参数params 说明:回调函数的参数中的methods 对象说明:提供⼀些⽅法供回调函数调⽤showStateIcon: 显⽰状态图标例如:hideStateIcon: 隐藏状态图标removeDragedNode: 移除跟随⿏标移动的被拖元素例如:removeDragedNode('fade')removeDragedNode('back')removeDragedNode('blost')getStateIconNode: 获取跟随⿏标移动的状态图标dom 节点destroyDrop: 销毁当前Drop 对象onDragEnter 否Function 拖动进⼊时调⽤见下⽅说明onDragOver 否Function 被拖动元素在⾃⼰上⽅移动时候调⽤, 这个函数会被连续调⽤见下⽅说明onDragLeave 否Function 拖动离开时候调⽤见下⽅说明onDrop 是Function 被拖动元素在⾃⼰上⽅放下时调⽤见下⽅说明onDragEnd否Function拖动结束时候调⽤见下⽅说明属性类型描述data 不定被拖动元素定义的data 属性, 类型由Drag 对象被创建的时候传⼊的data 属性决定el Object 当前dom 节点enter Boolean 是否进⼊当前范围的标志位, 布尔值methods Object 见下⽅ 回调函数参数中的methods 说明name String Drop 名称, 在销毁当前Drag 对象时候需要⽤到sourceNodeObject被拖动元素的dom 节点⽅法名⽰例参数说明描述showStateIcon params.methods.showStateIcon('add')参数类型: String, 内置三种常⽤图标: 添加('add'), 错误('error'), 删除('delete'), 拒绝(reject),传⼊对应的名字即可, 也可以⾃定义图标,直接传⼊图⽚的完整地址显⽰状态图标, 调⽤后会在⿏标旁边出现⼀个跟随⿏标移动的⼩图标, 如果要隐藏只需要调⽤hideStateIcon 函数即可hideStateIcon params.methods.hideStateIcon()⽆参数隐藏跟随⿏标移动的状态图标(如果没有调⽤showStateIcon 函数的话图标默认不显⽰)removeDragedNodeparams.methods.removeDragedNode(animationName,time)animationName(动画类型), 参数类型:String, ⾮必填, 可选: fade / blost /back, time(动画时间,单位毫秒), 参数类型:Number, ⾮必填移除跟随⿏标移动的被拖元素, 如果没有参数则直接消失, 有参数表⽰执⾏消失动画后再消失 ⽬前⽀持三种动画, 分别是: 褪⾊(fade),爆炸(blost), 反弹(back), 三种动画对应不同也应⽤场景, 例如:removeDragedNode('blost',300)getStateIconNode params.methods.getStateIconNode()⽆参数返回跟随⿏标移动的状态图标dnd.js 源码(function webpackUniversalModuleDefinition(root, factory) { if(typeof exports === 'object' && typeof module === 'object') module.exports = factory();else if(typeof define === 'function' && define.amd) define("dnd", [], factory);else if(typeof exports === 'object') exports["dnd"] = factory(); elseroot["dnd"] = factory();})(this, function() {return /******/ (function(modules) { // webpackBootstrap /******/ // The module cache/******/ var installedModules = {};/******//******/ // The require function/******/ function __webpack_require__(moduleId) {/******//******/ // Check if module is in cache /******/ if(installedModules[moduleId])/******/ return installedModules[moduleId].exports;/******//******/ // Create a new module (and put it into the cache)/******/ var module = installedModules[moduleId] = {/******/ i: moduleId,/******/ l: false,/******/ exports: {}/******/ };/******//******/ // Execute the module function/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);/******//******/ // Flag the module as loaded /******/ module.l = true;/******//******/ // Return the exports of the module /******/ return module.exports;/******/ }/******//******//******/ // expose the modules object (__webpack_modules__)/******/ __webpack_require__.m = modules;/******//******/ // expose the module cache/******/ __webpack_require__.c = installedModules;/******//******/ // identity function for calling harmony imports with the correct context /******/ __webpack_require__.i = function(value) { return value; };/******//******/ // define getter function for harmony exports/******/ __webpack_require__.d = function(exports, name, getter) {/******/ if(!__webpack_require__.o(exports, name)) {/******/ Object.defineProperty(exports, name, {/******/ configurable: false,/******/ enumerable: true,/******/ get: getter /******/ });/******/ }/******/ };/******//******/ // getDefaultExport function for compatibility with non-harmony modules /******/ __webpack_require__.n = function(module) {/******/ var getter = module && module.__esModule ?/******/ function getDefault() { return module['default']; } :/******/ function getModuleExports() { return module; };/******/ __webpack_require__.d(getter, 'a', getter);/******/ return getter;/******/ };/******//******/ // Object.prototype.hasOwnProperty.call/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };/******//******/ // __webpack_public_path__/******/ __webpack_require__.p = "";dom 节点destroyDrop params.methods.destroyDrop({name})参数类型: String,Drag 对象的名字销毁Drop 对象,匹配所有名字跟传⼊参数⼀致的Drop 对象并销毁,销毁后将不能接收Drag 对象/******//******/ // Load entry module and return exports/******/ return __webpack_require__(__webpack_require__.s = 7);/******/ })/************************************************************************//******/ ([/* 0 *//***/ (function(module, exports, __webpack_require__) {"use strict";Object.defineProperty(exports, "__esModule", {value: true});var DOCUMENT_ADDR = exports.DOCUMENT_ADDR = 'https:///qgh810/dnd';var REMOVE_ANIMATION_TYPES = exports.REMOVE_ANIMATION_TYPES = {'fade': 'animation_fade','blost': 'animation_blost','back': 'animation_back'};/***/ }),/* 1 *//***/ (function(module, exports, __webpack_require__) {"use strict";Object.defineProperty(exports, "__esModule", {value: true});exports.methods = undefined;var _dragStore;var _iconImages = __webpack_require__(5);var _iconImages2 = _interopRequireDefault(_iconImages);var _config = __webpack_require__(0);function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writab le: true }); } else { obj[key] = value; } return obj; }var dragStore = (_dragStore = {/* ********** 被拖元素drag设置的变量 *************///isMobile: false,data: null,draggedNode: null,sourceNode: null,markNode: null,stateIcon: null,mousePosition: null,/* ********** drop设置的变量 *************/targets: [],targetOnDragStarts: [],targetOnDragEnds: [],onDragEnters: [],onDragLeaves: [],onDragOvers: [],onDrops: [],targetPositions: [],/* ********** store设置的变量 *************/targetIndex: -1,_prevValidIndex: -1,_inTarget: false,/*** 监听拖动开始*/onDragStart: function onDragStart(data, el) {var _this = this;this._initStore();this.sourceNode = el;this.data = data;// ⼴播拖动开始this.targetOnDragStarts.forEach(function (fn, index) { return fn && fn({data: data,enter: index === _this.targetIndex,el: _this.targets[index].el,sourceNode: _this.sourceNode,name: _this.targets[index].name,expand: _this.targets[index].expand,methods: methods});});},/*** 初始化store状态*/_initStore: function _initStore() {this._inTarget = false;this._prevValidIndex = -1;this.targetIndex = -1;this.hideStateIcon();},/*** 监听拖动*/onDragOver: function onDragOver(pageX, pageY) {this.mousePosition = [pageX, pageY];this.targetIndex = this.collision(pageX, pageY);this.setIconPosition(pageX, pageY);if (this.targetIndex >= 0) {// 判断是否在⽬标外是的话表⽰刚刚进⼊if (!this._inTarget) {this._prevValidIndex = this.targetIndex;this._inTarget = true;var _params = {enter: true,data: this.data,el: this.targets[this.targetIndex].el,sourceNode: this.sourceNode,name: this.targets[this.targetIndex].name,expand: this.targets[this.targetIndex].expand,methods: methods};this.onDragEnters[this.targetIndex](_params);}// 调⽤回调var params = {enter: true,data: this.data,el: this.targets[this.targetIndex].el,sourceNode: this.sourceNode,name: this.targets[this.targetIndex].name,expand: this.targets[this.targetIndex].expand,pageX: pageX,pageY: pageY,methods: methods};this.onDragOvers[this.targetIndex](params);} else {// 判断是否在⽬标内是的话表⽰刚刚离开if (this._inTarget) {this._inTarget = false;var _params2 = {enter: false,data: this.data,el: this.targets[this._prevValidIndex].el,sourceNode: this.sourceNode,name: this.targets[this._prevValidIndex].name,expand: this.targets[this._prevValidIndex].expand, methods: methods};this.onDragLeaves[this._prevValidIndex](_params2);}}},/*** 监听拖动结束*/onDragEnd: function onDragEnd() {var _this2 = this;// 触发放置事件if (this.targetIndex >= 0) {var params = {enter: true,data: this.data,el: this.targets[this.targetIndex].el,sourceNode: this.sourceNode,name: this.targets[this.targetIndex].name,expand: this.targets[this.targetIndex].expand,methods: methods};this.targetIndex >= 0 && this.onDrops[this.targetIndex](params);}// ⼴播拖动结束事件this.targetOnDragEnds.forEach(function (fn, index) {if (!fn) return;var params = {enter: index === _this2.targetIndex,data: _this2.data,el: _this2.targets[index].el,sourceNode: _this2.sourceNode,name: _this2.targets[index].name,expand: _this2.targets[index].expand,methods: methods};fn(params);});},/*** 碰撞检测函数*/collision: function collision(pageX, pageY) {var targetIndex = -1;// 碰撞检测for (var i = 0; i < this.targetPositions.length; i++) {var position = this.targetPositions[i];if (!position) continue;var index = i;if (pageX >= position.left && pageY >= position.top && pageX <= position.right && pageY <= position.bottom) { targetIndex = index;break;}}return targetIndex;},/*** 设置状态icon位置跟随⿏标*/setIconPosition: function setIconPosition(x, y) {var style = this.stateIcon.style;style.left = x + 8 + 'px';style.top = y + 'px';},/*** 显⽰状态icon* url 可以是图⽚绝对路径也可以是 add | error | delete*/showStateIcon: function showStateIcon(url) {var _this3 = this;setTimeout(function () {if (_this3.isMobile) return console.warn('showStateIcon仅在pc端⼝可⽤请参考相关说明' + _config.DOCUMENT_ADDR); url = _iconImages2.default[url] || url || 'add';var iconStyle = _this3.stateIcon.style;iconStyle.display = 'block';iconStyle.background = 'no-repeat url(' + url + ') center center / 100% auto';}, 0);},/*** 隐藏状态icon*/hideStateIcon: function hideStateIcon() {try {this.stateIcon.style.display = 'none';} catch (e) {}},/*** 移除被拖动的节点* type 动画类型不传则⽆动画直接消失可选 fade | blost | back* time 动画持续时长⾮必填*/removeDragedNode: function removeDragedNode(type, time) {var _this4 = this;if (!type) return this.removeMark();if (!_config.REMOVE_ANIMATION_TYPES[type]) return this.removeMark();setTimeout(function () {clearTimeout(_this4.removeMarkTid);}, 0);this[_config.REMOVE_ANIMATION_TYPES[type]](time);}}, _defineProperty(_dragStore, _config.REMOVE_ANIMATION_TYPES.fade, function () {var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 150;var style = this.draggedNode && this.draggedNode.style;if (!style) return;style.transition = 'all ' + time / 1000 + 's ease';style.opacity = '0';setTimeout(this.removeMark.bind(this), time);}), _defineProperty(_dragStore, _config.REMOVE_ANIMATION_TYPES.blost, function () {var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 150;var style = this.draggedNode && this.draggedNode.style;if (!style) return;style.transition = 'all ' + time / 1000 + 's ease';style.boxShadow = '0 0 50px 30px rgba(0,0,0,0.3)';style.opacity = '0';setTimeout(this.removeMark.bind(this), time);}), _defineProperty(_dragStore, _config.REMOVE_ANIMATION_TYPES.back, function () {var time = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 400;var style = this.draggedNode && this.draggedNode.style;if (!style) return;style.transition = 'all ' + time / 1000 + 's cubic-bezier(0.2,0.4,0.25,1.1)';style.transform = 'translate(0,0)';setTimeout(this.removeMark.bind(this), time);}), _defineProperty(_dragStore, 'removeMark', function removeMark() {var _this5 = this;clearTimeout(this.removeMarkTid);this.removeMarkTid = setTimeout(function () {try {document.body.removeChild(_this5.markNode);_this5.draggedNode = null;} catch (e) {// console.log('出错', e)}}, 10);}), _defineProperty(_dragStore, 'destroyDrop', function destroyDrop(name) {var _this6 = this;var result = false;this.targets.forEach(function (item, i) {if ( === name) {_this6.removeDrop(i);result = true;}});return result;}), _defineProperty(_dragStore, 'removeDrop', function removeDrop(index) {delete this.targets[index];delete this.targetOnDragStarts[index];delete this.targetOnDragEnds[index];delete this.onDragEnters[index];delete this.onDragLeaves[index];delete this.onDragOvers[index];delete this.onDrops[index];delete this.targetPositions[index];}), _defineProperty(_dragStore, 'getStateIconNode', function getStateIconNode() {return this.stateIcon;}), _dragStore);exports.default = dragStore;/*** 供⽤户调⽤的静态⽅法*/var methods = exports.methods = {showStateIcon: dragStore.showStateIcon.bind(dragStore),hideStateIcon: dragStore.hideStateIcon.bind(dragStore),getStateIconNode: dragStore.getStateIconNode.bind(dragStore),removeDragedNode: dragStore.removeDragedNode.bind(dragStore),destroyDrop: dragStore.destroyDrop.bind(dragStore)};/***/ }),/* 2 *//***/ (function(module, exports, __webpack_require__) {"use strict";Object.defineProperty(exports, "__esModule", {value: true});var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };exports.checkNode = checkNode;function checkNode(el) {var result = el;if (!result) {return console.error('找不到当前节点', el);}if (typeof el === 'string') {var domName = el;result = document.querySelector(domName);if (!result) {return console.error('找不到当前节点', el);}} else if ((typeof el === 'undefined' ? 'undefined' : _typeof(el)) === 'object') {if (!el.nodeName) {return console.error('找不到当前节点', el);}}return result;}/***/ }),/* 3 *//***/ (function(module, exports, __webpack_require__) {"use strict";var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.e numerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty (target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototyp e, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();var _check = __webpack_require__(2);var _store = __webpack_require__(1);var _store2 = _interopRequireDefault(_store);__webpack_require__(6);function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } e lse { return Array.from(arr); } }function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }var Drag = function () {function Drag(el, options) {_classCallCheck(this, Drag);this.initData(el, options) && this.init();}/*** 检查和初始化传⼊参数*/_createClass(Drag, [{key: 'initData',value: function initData(el, options) {this.el = (0, _check.checkNode)(el);if (!this.el) return;this.el.style.MozUserSelect = 'none'; // 兼容⽕狐erSelect = 'none';this.el.style.cursor = 'default';options = this.checkOptions(options);this.options = options;this.data = options.data;this.mouseDownPosition = { left: -1, top: -1 };this.mouseDragging = false;this.mark = null;this.position = { left: 0, top: 0 };return true;}// 初始化}, {key: 'init',value: function init() {this.addEventListenerPC();this.addEventListenerMobile();}/*** 事件监听*/}, {key: 'addEventListenerPC',value: function addEventListenerPC() {var dom = this.el;// 监听当前节点的⿏标点击事件function onMouseDowm(e) {var pageX = e.pageX,pageY = e.pageY;this.mouseDownPosition = { left: pageX, top: pageY };dom.onmousemove = this.onElMousemove.bind(this);dom.onmouseup = this.onElMouseUp.bind(this);document.addEventListener('mouseup', this.onElMouseUp.bind(this));}dom.addEventListener('mousedown', onMouseDowm.bind(this));}}, {key: 'addEventListenerMobile',value: function addEventListenerMobile() {// 兼容移动端this.el.addEventListener('touchmove', this.onElTouchMove.bind(this));this.el.addEventListener('touchend', this.onElTouchEnd.bind(this));}/*** 监听拖动开始*/}, {key: 'onElMousemove',value: function onElMousemove(e) {var pageX = e.pageX,pageY = e.pageY;var _mouseDownPosition = this.mouseDownPosition,left = _mouseDownPosition.left,top = _mouseDownPosition.top;var EMIT_LENGTH = 3;if (Math.abs(pageX - left) < EMIT_LENGTH && Math.abs(pageY - top) < EMIT_LENGTH) return; if (this.mouseDragging) return;this.mouseDragging = true;_store2.default.onDragStart(this.data, this.el);this.position = this.el.getBoundingClientRect();// 创建蒙层this.mark = document.createElement('div');this.mark.className = 'x-drag-mark';this.setMarkStyle();this.mark.onmousemove = this.onMarkMouseMove.bind(this);this.mark.onmouseup = this.onMarkMouseUp.bind(this);this.mark.onmouseleave = this.onMarkMouseUp.bind(this);_store2.default.markNode = this.mark;document.body.appendChild(this.mark);// 创建复制元素_store2.default.draggedNode = this.el.cloneNode(true);this.setCloneNodeStyle();this.mark.appendChild(_store2.default.draggedNode);// 创建状态icon_store2.default.stateIcon = document.createElement('i');_store2.default.stateIcon.className = 'x-state-icon';this.setIconStyle();this.mark.appendChild(_store2.default.stateIcon);this.emit('onDragStart', {el: this.el,data: this.data,methods: _store.methods});}/*** 监听拖动结束*/}, {key: 'onElMouseUp',value: function onElMouseUp() {this.mouseDragging = false;this.el.onmousemove = null;this.el.onmouseup = null;this.mark && (this.mark.onmousemove = null);this.mark && (this.mark.onmouseup = null);_store.methods.hideStateIcon();_store.methods.removeDragedNode();document.removeEventListener('mouseup', this.onElMouseUp.bind(this));}/*** 监听蒙层⿏标移动*/}, {key: 'onMarkMouseMove',value: function onMarkMouseMove(e) {if (!_store2.default.draggedNode) return;pageX = _ref.pageX,pageY = _ref.pageY;var translateX = pageX - this.mouseDownPosition.left;var translateY = pageY - this.mouseDownPosition.top;_store2.default.draggedNode.style.transform = 'translate(' + translateX + 'px,' + translateY + 'px)'; _store2.default.onDragOver(pageX, pageY);}/*** 监听蒙层⿏标放开*/}, {key: 'onMarkMouseUp',value: function onMarkMouseUp() {document.removeEventListener('mouseup', this.onElMouseUp.bind(this));this.mouseDragging = false;this.mark.onmousemove = null;this.el.onmousemove = null;this.mouseDownPosition = { left: -1, top: -1 };this.emit('onDragEnd', {el: this.el,data: this.data,target: _store2.default.targets[_store2.default.targetIndex],methods: _store.methods});_store2.default.onDragEnd();}}, {key: 'onElTouchMove',value: function onElTouchMove(e) {e.preventDefault();_store2.default.isMobile = true;if (this.mouseDownPosition.left === -1) {var _e$touches$ = e.touches[0],pageX = _e$touches$.pageX,pageY = _e$touches$.pageY;this.mouseDownPosition.left = pageX;this.mouseDownPosition.top = pageY;}this.onElMousemove();this.onMarkMouseMove(e);}}, {key: 'onElTouchEnd',value: function onElTouchEnd(e) {this.onMarkMouseUp();}/*** 检查并且初始化options*/}, {key: 'checkOptions',value: function checkOptions(options) {options = options || {};var baseOptions = {data: '这⾥可以放需要丢给⽬标的内容',el: this.el,removeanimationtype: 1};for (var option in baseOptions) {!options[option] && (options[option] = baseOptions[option]);}return options;}/*** 设置蒙层样式*/}, {key: 'setMarkStyle',value: function setMarkStyle() {position: 'absolute',top: '0',left: '0',width: '100%',height: '100%',zIndex: '10'};for (var style in markStyle) {this.mark.style[style] = markStyle[style];}}/*** 设置克隆节点样式*/}, {key: 'setCloneNodeStyle',value: function setCloneNodeStyle() {var dom = _store2.default.draggedNode;var style = dom.style;var _position = this.position,left = _position.left,top = _position.top;var _el$getBoundingClient = this.el.getBoundingClientRect(),width = _el$getBoundingClient.width,height = _el$getBoundingClient.height;style.position = 'absolute';style.left = left + 'px';style.top = top + 'px';style.width = width + 'px';style.height = height + 'px';style.textAlign = this.el.currentStyle.textAlign;style.transform = 'translate(0,0)';style.zIndex = 1000;style.margin = 0;}/*** 设置状态icon样式*/}, {key: 'setIconStyle',value: function setIconStyle() {var style = _store2.default.stateIcon.style;style.display = 'none';style.position = 'absolute';style.width = '20px';style.height = '20px';style.zIndex = '10001';}/*** 发布事件*/}, {key: 'emit',value: function emit() {var _options;var args = Array.from(arguments);var functionName = args.shift();typeof this.options[functionName] === 'function' && (_options = this.options)[functionName].apply(_options, _toConsumableArray(args)); }}]);return Drag;}();module.exports = Drag;/***/ }),/* 4 *//***/ (function(module, exports, __webpack_require__) {"use strict";var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.e numerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty (target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototyp e, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();var _check = __webpack_require__(2);var _config = __webpack_require__(0);var _store = __webpack_require__(1);var _store2 = _interopRequireDefault(_store);function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } e lse { return Array.from(arr); } }function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }var Drop = function () {function Drop(el, options) {_classCallCheck(this, Drop);this.initData(el, options) && this.init();}/*** 检查并初始化传⼊参数*/_createClass(Drop, [{key: 'initData',value: function initData(el, options) {this.el = (0, _check.checkNode)(el);if (!this.el) return;this.options = this.checkOptions(options);if (!this.options) return;this.methods = _store.methods;this.index = -1; // 当前索引return true;}/*** 检查并且初始化options*/}, {key: 'checkOptions',value: function checkOptions(options) {if (!options) {return console.error(this.el, '未检测到options 请参考相关说明' + _config.DOCUMENT_ADDR);}if (typeof options.onDrop !== 'function') {return console.error(this.el, 'onDrop 必须是⼀个函数请参考相关说明' + _config.DOCUMENT_ADDR);}var baseOptions = {name: null};for (var option in baseOptions) {!options[options] && (options[options] = baseOptions[option]);}return options;}/*** 初始化*/}, {key: 'init',value: function init() {this.setStore();。
JS拖拽元素原理及实现代码
一、拖拽的流程动作
①鼠标按下
②鼠标移动
③鼠标松开
二、拖拽流程中对应的JS事件
①鼠标按下会触发onmousedown事件
②鼠标移动会触发onmousemove事件
③鼠标松开会触发onmouseup事件
三、实现的原理讲解
拖拽其实是通过获取鼠标移动的距离来实现的,即计算移动前的位置的坐标(x,y)与移动中的位置的坐标(x,y)差值。
当鼠标按下或鼠标移动时,都可以获取到当前鼠标的位置,即移动前的位置与移动中的位置。
那么上面①与②的代码就应该变成这样
var mouseDownX,mouseDownY // 因在移动中需计算鼠标的偏移需要用到鼠标按下时的坐标,固声明称全局变量
obj.onmousedown = function(e) {
mouseDownX = e.pageX;
mouseDownY = e.pageY;
}
obj.onmousemove = function(e)
{var mouseMoveX = e.pageX,mouseMoveY = e.pageY;
}
很明显移动后元素的X坐标为鼠标移动后的X坐标 - 鼠标按下的X坐标 + 元素的初始X坐标
Y坐标为鼠标移动后的Y坐标 - 鼠标按下的Y坐标 + 元素的初始Y坐标
把新的 X,Y 替换元素的 X,Y 就搞定了。
一般是对象onmousedown,onmousemove 和onmouseup是document来出发事件采用因为如果拖动速度过快,拖动元素会不动,因此采用document.onmousemove事件代替拖动的元素拖动事件。
js拖拽原理及简单实现(渣渣⾃学)第⼀步 ⾸先简单分析下需求吧,我们就是想实现⿏标拖拽带颜⾊的⽅块时,让⽅块停留在⿏标松开的位置,需要计算的就是拖拽前的坐标和拖拽后的坐标,⿏标移动后相对于原位置的偏移量=⽬标元素的偏移量,根据这个等式和⼏个属性实现拖拽(下⾯会介绍到这⼏个属性,莫急哈,后⾯还会遇到⼀个⼩问题,⼀会详细描述),⿏标的状态事件有三种,⿏标按下时的事件(mousedown),⿏标移动时的事件(mousemove),⿏标松开时的事件(mouseup)第⼆部这⾥就是撸代码了,⾸先新建⼀个html页⾯<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><style>#div{position: absolute;width:100px;height: 100px;background-color: deeppink;}</style><body style="border:1px solid #000;height: 600px;margin:0"><div id="div"></div><script>let tar=document.getElementById('div')let isDrag=false;tar.onmousedown=function(el){var el= el || event;isDrag=trueif(isDrag){document.onmousemove=function(e){var e = e || event;tar.style.left= e.clientX - el.offsetX + 'px';tar.style.top= e.clientY - el.offsetY + 'px';}} else {return}document.onmouseup=function(){isDrag=false;// tar.onmousedown=null;document.onmousemove=null document.onmouseup=null}}</script></body></html>这⾥时完整的代码,我⼀步⼀步的来解释(1)<body style="border:1px solid #000;height: 600px;margin:0">这段代码起了个初始化的作⽤,(因为⾕歌浏览器会默认给body加8px的margin)border加不加⽆所谓,我加上是⽅便看⼀下边界;(2)position: absolute; 样式中的这个东西不能缺,因为只⽤元素定位了之后才能使⽤top和left属性;(3)页⾯中的isDrag就相当于⼀个开关,只有当为true的时候才允许拖拽(也就是⿏标按下的时候才能拖拽,松开时isDrag⾃动变为false,mousemove事件就不能触发了)(4)var e = e || event(window.event); 这⾏代码其实也很好理解,就是为了兼容,它就相当于⼀个函数中运⽤三⽬给e重新赋值,(function(event){var e = event ? event : window.event})()这样理解就简单了,上⾯说了个⼩问题是啥,现在可以揭晓了,把页⾯中的var 换成let试⼀下,浏览器控制台报了⼀⾏错,如下Uncaught SyntaxError: Identifier 'el' has already been declared(星星个b的),咋报错了,为啥啊,咋回事啊?赶紧百度⼀下压压惊,不废话了,简单解释下,这⾥涉及到两个知识点:函数的形参以及let和var声明变量的区别,函数的形参也是函数作⽤域的参数,也就是函数调⽤后形成的局部作⽤域内的参数,它不属于全局哦,也就相当于使⽤let声明了这个参数,有⼈会问了,⽤var咋就可以呢,咋就不报错呢,因为var声明的变量会覆盖掉同名变量(也就是覆盖了形参),不要杠为啥覆盖,js就是这么定义的,百度下答案都是⼤同⼩异,王⼋的屁股--龟腚(规定),tar.style.left= e.clientX - el.offsetX + 'px';tar.style.top= e.clientY - el.offsetY + 'px';这两⾏代码就是计算元素的left和top的代码,position天天⽤,top和left不难理解,e.clientX就是⿏标终点距离div左边的垂直距离,e.clientY就是距离div上边的垂直距离,el.offsetX 是div距离浏览器左边的距离,el.offsetY 是距离浏览器页⾯显⽰区的距离,页⾯显⽰区的上边,标签栏下⾯,有⼀条灰边,⿏标动的时候这四个值也是在不断变化的,只要设置⼀下div的top和left就能实现简单的拖拽功能了(5)mouseup事件是释放空间,节约内存。
深入理解javascript原生拖放前面的话拖放(drag-and-drop,DnD)其实是两个动作——拖和放。
所以,它涉及到两个元素。
一个是被拖的元素,称为拖放源;另一个是要放的目标,称为拖放目标。
本文将通过拆分这两个概念来详细介绍原生拖放拖放源什么样的元素才是拖放源呢?HTML5为所有HTML元素规定了一个draggable属性,表示元素是否可以拖动图像和链接的draggable属性自动被设置成了true,而其他元素这个属性的默认值都是false [注意]必须设置draggable='true'才能生效,只设置draggable不起作用默认情况下,文本只有在被选中的情况下才能拖动,而图像和链接在任何时候都可以拖动。
而其他元素则无法被拖放<input value="文字可拖动"><img alt="图像可拖动" src="/files/xiaohuochai/zan.gif"><a href="#">链接可拖动</a><div id="test" style="height:30px;width:300px;background:pink;">元素不可拖动</div> 当为元素设置draggable属性后,普通元素也可以拖动<div draggable="true" style="height:30px;width:100px;background:pink;"></div>兼容IE9-浏览器不支持draggable属性,但可通过mousedown事件处理程序调用dragDrop()方法来实现拖动效果复制代码<div id="test" style="height:30px;width:300px;background:pink;"></div><script>test.onmousedown = function(){this.dragDrop();}</script>复制代码[注意]如果让firefox支持draggable属性,必须添加一个ondragstart事件处理程序,并在dataTransfer对象使用setData()方法来启动效果拖放事件拖放源涉及到3个拖放事件。
拖动拖放源时,依次触发dragstart、drag和dragend这3个事件dragstart按下鼠标键并开始移动鼠标时,会在被拖放的元素上触发dragstart事件。
此时光标变成“不能放”符号(圆环中有一条反斜线),表示不能把元素放到自己上面drag触发dragstart事件后,随即会触发drag事件,而且在元素被拖动期间会持续触发该事件dragend当拖动停止时(无论是把元素放到了有效的放置目标,还是放到了无效的放置目标上),会触发dragend事件复制代码<div id="test" draggable="true" style="height:30px;width:100px;background:pink;">0</div><script>var timer,i=0;test.ondragstart = function(){this.style.backgroundColor = 'lightgreen';}test.ondrag = function(){if(timer) return;timer = setInterval(function(){test.innerHTML = i++;},100)}test.ondragend = function(){clearInterval(timer);timer = 0;this.style.backgroundColor = 'pink';}</script>复制代码拖放目标拖放目标是指被拖动的元素松开鼠标时被放置的目标拖放源被拖动到拖放目标上时,将依次触发dragenter、dragover和dragleave或drop这四个事件dragenter只要有元素被拖动到放置目标上,触发dragenter事件dragover被拖动的元素在放置目标的范围内移动时,持续触发dragover事件dragleave如果元素被拖出了放置目标,触发dragleave事件drop如果元素被放到了放置目标中,触发drop事件[注意]firefox浏览器的drop事件的默认行为是打开被放到放置目标上的URL。
为了让firefox 支持正常的拖放,还要取消drop事件的默认行为默认情况下,目标元素是不允许被放置的,所以不会发生drop事件。
只要在dragover和dragenter事件中阻止默认行为,才能成为被允许的放置目标,才能允许发生drop事件。
此时,光标变成了允许放置的符号复制代码<div id="test" draggable="true" style="height:30px;width:130px;background:pink;float:left;">拖放源</div><div id="target" style="float:right;height: 200px;width:200px;background:lightblue;">拖放目标</div><script>var timer,i=0;var timer1,i1=0;//兼容IE8-浏览器test.onmousedown = function(){if(this.dragDrop){this.dragDrop();}}test.ondragstart = function(){this.style.backgroundColor = 'lightgreen';this.innerHTML = '开始拖动';}test.ondrag = function(){if(timer) return;timer = setInterval(function(){test.innerHTML = '元素已被拖动' + ++i + '秒';},1000);}test.ondragend = function(){clearInterval(timer);timer = 0;i =0;this.innerHTML = '结束拖动';this.style.backgroundColor = 'pink';}target.ondragenter = function(e){e = e || event;if(e.preventDefault){e.preventDefault();}else{e.returnValue = false;}this.innerHTML = '有元素进入目标区域';this.style.background = 'red';}target.ondragover = function(e){e = e || event;if(e.preventDefault){e.preventDefault();}else{e.returnValue = false;}if(timer1) return;timer1 = setInterval(function(){target.innerHTML = '元素已进入' + (++i1) + '秒';},1000);}target.ondragleave = function(){clearInterval(timer1);timer1 = 0;i1=0;this.innerHTML = '元素已离开目标区域';this.style.backgroundColor = 'lightblue';}target.ondrop = function(){clearInterval(timer1);timer1 = 0;i1=0;this.innerHTML = '元素已落在目标区域';this.style.backgroundColor = 'orange';}</script>复制代码dataTransfer对象为了在拖放操作时实现数据交换,引入了dataTransfer对象,它是事件对象的一个属性,用于从被拖动元素向放置目标传递字符串格式的数据dataTransfer对象有两个主要方法:getData()和setData()getData()可以取得由setData()保存的值。
setData()方法的第一个参数,也是getData()方法唯一的一个参数,是一个字符串,表示保存的数据类型,取值为"text"或"URL"IE只定义了"text"和"URL"两种有效的数据类型,而HTML5则对此加以扩展,允许指定各种MIME类型。
考虑到向后兼容,HTML5也支持"text"和"URL",但这两种类型会被映射为"text/plain"和"text/uri-list"实际上,dataTransfer对象可以为每种MIME类型都保存一个值。
换句话说,同时在这个对象中保存一段文本和一个URL不会有任何问题[注意]保存在dataTransfer对象中的数据只能在drop事件处理程序中读取在拖动文本框中的文本时,浏览器会调用setData()方法,将拖动的文本以"text"格式保存在dataTransfer对象中。
类似地,在拖放链接或图像时,会调用setData()方法并保存URL。
然后,在这些元素被拖放到放置目标时,就可以通过getData()读到这些数据复制代码<div>请将从这堆内容不同乱七八糟的文字中挑选一些移动到拖放目标中</div><div id="target" style="margin-top:20px;height: 100px;width:200px;background:lightblue;">拖放目标</div><div id="result"></div><script>target.ondragenter = function(e){e = e || event;if(e.preventDefault){e.preventDefault();}else{e.returnValue = false;}this.innerHTML = '有元素进入目标区域';this.style.background = 'red';}target.ondragover = function(e){e = e || event;if(e.preventDefault){e.preventDefault();}else{e.returnValue = false;}}target.ondragleave = function(e){e = e || event;this.innerHTML = '元素已离开目标区域';this.style.backgroundColor = 'lightblue';}target.ondrop = function(e){e = e || event;if(e.preventDefault){e.preventDefault();}else{e.returnValue = false;}result.innerHTML = '落入目标区域的文字为:' + e.dataTransfer.getData('text');this.innerHTML = '元素已落在目标区域';this.style.backgroundColor = 'orange';}</script>复制代码当然,也可以在dragstart事件处理程序中调用setData(),手动保存自己要传输的数据,以便将来使用复制代码<div id="test" draggable="true" data-value="这是一个秘密"style="height:30px;width:100px;background:pink;">拖动源</div><div id="target" style="margin-top:20px;height: 100px;width:200px;background:lightblue;">拖放目标</div><div id="result"></div><script>//兼容IE8-浏览器test.onmousedown = function(){if(this.dragDrop){this.dragDrop();}}test.ondragstart = function(e){e = e || event;e.dataTransfer.setData('text',test.getAttribute('data-value'));}target.ondragenter = function(e){e = e || event;if(e.preventDefault){e.preventDefault();}else{e.returnValue = false;}this.innerHTML = '有元素进入目标区域';this.style.background = 'red';}target.ondragover = function(e){e = e || event;if(e.preventDefault){e.preventDefault();}else{e.returnValue = false;}}target.ondragleave = function(e){e = e || event;this.innerHTML = '元素已离开目标区域';this.style.backgroundColor = 'lightblue';}target.ondrop = function(e){e = e || event;if(e.preventDefault){e.preventDefault();}else{e.returnValue = false;}result.innerHTML = '落入目标区域的文字为:' + e.dataTransfer.getData('text');this.innerHTML = '元素已落在目标区域';this.style.backgroundColor = 'orange';}</script>复制代码改变光标利用dataTransfer对象,不仅可以传输数据,还能通过它来确定被拖动的元素以及作为放罝目标的元素能够接收什么操作。