JS实现拖拽功能
本文代码地址(第一节):https://github.com/dirstart/js-exam/blob/master/拖拽div1.html
第二节:https://github.com/dirstart/js-exam/blob/master/拖拽div2.html
第三节:https://github.com/dirstart/js-exam/blob/master/拖拽div3.html
本来只是想要实现鼠标放置并且一个悬浮窗的功能,结果慢慢回顾基础突然想起以前看到过的拖拽,就都需要定位来说,悬浮窗和拖拽有相似的地方。
那么,怎么实现拖拽呢,我们需要什么呢?
- 首先我们看功能,拖拽就是 鼠标按住 => 鼠标移动 => 鼠标松开
- 接着我们需要什么? 最后的结果是拖住的块要动,而且是跟着鼠标移动 = > 改变块的 style.top ,要注意, offsetHeight是只读的,style.top是可写的,这个是我以前踩过的坑,不过已经忘了是在哪里踩的了。因为要改变,所以我们首先要获取,因此 => 获取style的函数
http://www.cnblogs.com/jshen/archive/2012/11/29/2794292.html (关于style.top和offsetTop)
- 最后我们所记录下的数据: 1.物块的起始位置 2.鼠标移动的位置 3.物块最后的位置
我们能够想到的过程: a.鼠标按下,物块不动(但是告诉浏览器我们准备好了,我点着的是box) b.鼠标移动,物块动,用数组保存鼠标移动的位置 c.鼠标放下,物块不动,一波操作结束
实际上块的移动距离是鼠标mousedown
点到mousemove
的变动距离
大致就先这样,现在开始我们的表演。
First Blood : 获取css属性
首先证明一下,为什么不直接用DOM的参数来直接获取属性?
css部分
*{margin: 0;padding: 0;}
#box{
margin-top: 240px;
margin-left: 500px;
width: 100px;
height: 100px;
background: #bfbfbf;
cursor: move;
}
body部分
<div id="box"></div>
js部分
let box=document.getElementById("box");
// box.style.marginTop=600+"px";
box.onmouseover=function(event){
console.log(parseInt(box.style.marginTop));
}
如上,最后显示的是NaN
,可是我们明明在CSS中写了margin-top
呀,于是我们将注释去掉,发现去掉之后显示的是600
.
=> 原因在于,style.marginTop获取不了外部的样式。
链接如下 : http://www.cnblogs.com/cythia/p/6721145.html
所以引出能够获取外部样式的 方法 currentStyle和getComputedStyle.之后,因为IE支持前者,FF和Chrome支持后者。
let oStyle=this.currentStyle?this.currentStyle:window.getComputedStyle(this,null);
box.onmouseover=function(event){
let oStyle=this.currentStyle?this.currentStyle:window.getComputedStyle(this,null);
console.log(parseInt(box.style.marginTop)); // NaN
console.log(oStyle.height); // 100px
}
发现可用,之后进入我们的第二步。
Second , 获取鼠标的动态并根据此改变我们的块已达到拖拽的目的
let event=event||window.event;
通过此句获得当前鼠标的地址,这样的赋值依旧是为了处理兼容性问题。
!!!!!!!!!!!!很幸运,我们见到了一个新坑,这里报错了
报错信息:Uncaught SyntaxError: Identifier 'event' has already been declared
这个错误应该是跟let有关,在这里用以前的var就不会出错。
在这里我先扔出大概的原理链接,因为我也不是很懂==,let x=x
这样是会报错的:
那么我们先用回 var event=event||window.event
,继续我们的旅程。
let box=document.getElementById("box");
let disX=disY=0;
let startX=startY=0;
let flag=false;
box.onmousedown=function(event){
var event=event||window.event;
// 阻止事件冒泡
if(event && event.stopPropagation){
event.stopPropagation();
}else{
window.event.cancelBubble=true;
}
// 记录下mousedown点的距离
flag=true;
startX=event.clientX;
startY=event.clientY;
console.log("down");
}
box.onmousemove=function(event){
if(flag===false) return ;
let oStyle=this.currentStyle?this.currentStyle:window.getComputedStyle(this,null);
disX=event.clientX-startX;
disY=event.clientY-startY;
box.style.marginLeft=parseInt(oStyle.marginLeft)+disX+"px";
box.style.marginTop=parseInt(oStyle.marginTop)+disY+"px";
}
document.onmouseup=function(){
flag=false;
}
我们发现能够移动,但是非常卡顿,很不平滑,完全不像每次计算 鼠标变动距离的样子。
问题已发现:少了一句话。 我们需要在 onmousemove 再加一句话,因为我们的startX始终没有改变,而实际上每一次的移动都应该改变我们上一次的起始点。
box.onmousemove=function(event){
............................................
startX=event.clientX;
startY=event.clientY;
}
加上我们的基本功能就算完成了。
但是还有个问题,当我们的鼠标移出了box的范围后,因为我们使用的是 box.onmousemove ,我们鼠标到了document就不行了。
这又是我们的思维漏洞,不过改倒是非常容易
将box.onmousemove
改成'document.onmousemove,将里面的三个
this改成
box` 即可。
第一版代码地址:https://github.com/dirstart/js-exam/blob/master/拖拽div1.html
那么这样我们的第二阶段就OK了,我们先看看网上其他的例子。
现在能想到的优化:1.样式可以更好看 2.可以给提示和信息 3.不让div移出浏览器框外。
看完fgm.cc里面的拖拽之后的
首先记录下过程中可能要看的知识点:http://blog.csdn.net/u012309349/article/details/50663841
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
*{margin: 0;padding: 0;}
ul,li{
list-style: none;
}
#box{
cursor: move;
position: absolute;
top: 50%;
left : 50%;
width: 300px;
height: 150px;
margin: -75px 0 0 -150px;
background: #C0E4DF;
border: 1px solid #3E2D2D;
}
html{
font-size: 12px;
background: #F0ADAD;
}
.header{
border-bottom: 1px solid #3E2D2D;
height: 40px;
line-height: 40px;
font-size: 16px;
text-align: center;
color: #8C1F1F;
}
</style>
</head>
<body>
<div id="box">
<p class="header">我是被拖拽的小窗口</p>
<p>拖拽是否开启</p><span></span>
<ul>
<li>位置X : <span id="displayX"></span></li>
<li>位置Y : <span id="displayY"></span></li>
</ul>
</div>
</body>
<script type="text/javascript">
let oBox=document.getElementById("box"),
oX=document.getElementById('displayX'),
oY=document.getElementById('displayY'),
isDrag=false,
temX=0,
temY=0,
disX=0,
disY=0;
oBox.onmousedown=(event)=>{
var event=event||window.event;
isDrag=true;
temX=event.clientX;
temY=event.clientY;
disX=temX-oBox.offsetLeft;
disY=temY-oBox.offsetTop;
this.setCapture && this.setCapture();
return false;
}
document.onmousemove=(event)=>{
if(!isDrag) return ;
var event=event||window.event;
temX=event.clientX-disX;
temY=event.clientY-disY;
oBox.style.left=temX+"px";
oBox.style.top=temY+"px";
// oBox.style.marginTop = oBox.style.marginLeft = 0;
}
document.onmouseup=(event)=>{
isDrag=false;
}
</script>
</html>
这是根据fgm.cc网站修改的部分,我们重点关注一下注释中的// oBox.style.marginTop = oBox.style.marginLeft = 0;
,这里有个问题,如果不加这句话的话鼠标不会保持在我们第一次放下的oBox内的位置。
其实原因是这个样子的,因为我们初始的时候为了让元素居中,所以我们用了
top:50%;left:50%; margin-left: #(负一半的元素宽度) ; margin-top: #(负一半的元素高度)
上面的目的是为了居中,而此后,因为我们直接通过 offsetLeft和offsetTop来设定了位置,这个时候的margin-left和margin-top如果不清楚,就会造成 ---- box盒子往 左上偏 。
可以这样理解 。我们这个时候已经不用 top 和 left 的绝对定位了, 而我们 派出去生物天敌 margin却还在起着它的作用 ,这就像当年的罗斯福与破坏生物的故事了。
So,我们得加上那句注释掉的话。
接下来的任务是怎么让盒子不移出我们的视线之外。
其实照网上的这种写法,要直接限定盒子简直是太容易了,不得不承认别人的逻辑比我的好上太多了。代码如下:
document.onmousemove=(event)=>{
if(!isDrag) return ;
var event=event||window.event;
temX=event.clientX-disX;
temY=event.clientY-disY;
oBox.style.marginTop = oBox.style.marginLeft = 0;
console.log(temX);
// 做判断
temX=temX<0?0:temX;
temX=temX>maxX?maxX:temX;
temY=temY<0?0:temY;
temY=temY>maxY?maxY:temY;
oBox.style.left=temX+"px";
oBox.style.top=temY+"px";
oBox.style.marginTop = oBox.style.marginLeft = 0;
}
至于如何来美化它,这里就不再阐述了,不过之后再来介绍一种用其他思路来实现的代码。
Third 第三种方法的实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
*{margin: 0;padding: 0;}
body{
background: #6A3545;
}
#box{
position: absolute;
left: 500px;
top: 300px;
width: 100px;
height: 100px;
border: 1px solid #fde331;
background: #1A5F5C;
cursor: move;
}
#temp{
position: absolute;
width: 100px;
height: 100px;
background: #bfb171;
opacity: 0.6;
}
</style>
</head>
<body>
<div id="box">
</div>
</body>
<script type="text/javascript">
var zIndex=1;
var oBox=document.getElementById('box');
function getStyle(ele,attr){
return ele.currentStyle?ele.currentStyle[attr]:getComputedStyle(ele,null)[attr];
}
oBox.onmousedown=function(event){
var event=event||window.event;
var disX=event.clientX-oBox.offsetLeft;
var disY=event.clientY-oBox.offsetTop;
var maxLeft=document.documentElement.clientWidth - oBox.offsetWidth;
var maxTop=document.documentElement.clientHeight - oBox.offsetHeight;
var oTemp=document.createElement("div");
oTemp["id"]="temp";
oTemp.style.left=getStyle(oBox,"left");
oTemp.style.top=getStyle(oBox,"top");
oTemp.style.zIndex=zIndex++;
document.body.appendChild(oTemp);
document.onmousemove=function(event){
var event=event||window.event;
var temX=event.clientX-disX;
var temY=event.clientY-disY;
temX=temX>maxLeft?maxLeft:temX;
temX=temX<0?0:temX;
temY=temY>maxTop?maxTop:temY;
temY=temY<0?0:temY;
oTemp.style.left=temX+"px";
oTemp.style.top=temY+"px";
return false;
}
document.onmouseup=function(){
document.onmousemove=null;
document.onmouseup=null;
oBox.style.left=oTemp.style.left;
oBox.style.top=oTemp.style.top;
document.body.removeChild(oTemp);
}
return false;
}
</script>
</html>
考虑到用户交互的移动,这往往是我们真正会做到的东西.
JS实现拖拽功能的更多相关文章
- Js元素拖拽功能实现
Js元素拖拽功能实现 需要解决的问题 最近项目遇到了一个问题,就是用户某个操作需要弹出一个自定义的内容输入框,但是有个缺点,当浏览太大的时候没办法点击确认和取消按钮,应为这个弹出框是采用绝对定位的,取 ...
- 原生js实现拖拽功能
1. 给个div,给定一些样式 <div class="drag" style="left:0;top:0;width:100px;height:100px&quo ...
- 通过 JS 实现简单的拖拽功能并且可以在特定元素上禁止拖拽
前言 关于讲解 JS 的拖拽功能的文章数不胜数,我确实没有必要大费周章再写一篇重复的文章来吸引眼球.本文的重点是讲解如何在某些特定的元素上禁止拖拽.这是我在编写插件时遇到的问题,其实很多插件的拖拽功能 ...
- 原生js拖拽功能制作滑动条实例教程
拖拽属于前端常见的功能,很多效果都会用到js的拖拽功能.滑动条的核心功能也就是使用js拖拽滑块来修改位置.一个完整的滑动条包括 滑动条.滑动痕迹.滑块.文本 等元素,先把html代码写出来,如下所示: ...
- 关于 JS 拖拽功能的冲突问题及解决方法
前言 我在之前写过关于 JS 拖拽的文章,实现方式和网上能搜到的方法大致相同,别无二致,但是在一次偶然的测试中发现,这种绑定事件的方式可能会和其它的拖拽事件产生冲突,由此产生了对于事件绑定的思考.本文 ...
- vuejs2.0使用Sortable.js实现的拖拽功能
简介 在使用vue1.x之前的版本的时候,页面中的拖拽功能,我在项目中是直接用的jquery ui中的sortable.js,只是在拖拽完成后,在update的回调函数中又重新排序了存放数据的数组.但 ...
- vue2.0使用Sortable.js实现的拖拽功能
简介 在使用vue1.x之前的版本的时候,页面中的拖拽功能,我在项目中是直接用的jQuery ui中的sortable.js,只是在拖拽完成后,在update的回调函数中又重新排序了存放数据的数组.但 ...
- js进阶 12-17 jquery实现鼠标左键按下拖拽功能
js进阶 12-17 jquery实现鼠标左键按下拖拽功能 一.总结 一句话总结:监听的对象必须是文档,鼠标按下运行mousemove事件,鼠标松开取消mousemove事件的绑定,div的偏移的话是 ...
- (Demo分享)利用JavaScript(JS)实现一个九宫格拖拽功能
利用JavaScript(JS)实现一个九宫格拖拽功能 Demo实现了对任意方格进行拖拽,可以交换位置,其中Demo-1利用了勾股定理判断距离! Demo-1整体思路: 1.首先div实现自由移动 ...
随机推荐
- Fiddler + 海马模拟器转包教程
Fiddler + 海马模拟器转包教程 转包用来做什么不说了, 整理一下步骤 1.安装Fiddler 下载地址 http://pan.baidu.com/s/18me0A 2.设置Fiddler: ...
- phpunit手动配置
phpunit: #!D:\xampp\php\.\php.exe<?php/* PHPUnit * * Copyright (c) 2001-2013, Sebastian Bergmann ...
- uoj problem 11 ydc的大树
题目大意: 给定一颗黑白树.允许删除一个白点.最大化删点后无法与删点前距自己最远的黑点连通的黑点个数.并求出方案数. 题解: 这道题很棒棒啊. 一开始想了一个做法,要用LCT去搞,特别麻烦而且还是\( ...
- Jetson TX2火力全开
Jetson Tegra系统的应用涵盖越来越广,相应用户对性能和功耗的要求也呈现多样化.为此NVIDIA提供一种新的命令行工具,可以方便地让用户配置CPU状态,以最大限度地提高不同场景下的性能和能耗. ...
- 报错之-Cannot set property 'onclick' of null
当js文件放在head里面时,如果绑定了onclick或者onmouseover事件,就会出现如下图类似的错误,是因为浏览器的加载你写的html文档的顺序是从上往下,加载完按钮节点才执行的js,所以当 ...
- Python:内置函数zip()
zip函数接受任意多个可迭代对象作为参数,将对象中对应的元素打包成一个tuple,然后返回一个可迭代的zip对象. 这个可迭代对象可以使用循环的方式列出其元素 若多个可迭代对象的长度不一致,则所返回的 ...
- jQuery对象和DOM对象的互换
Dom 对象:指的是普通的 JavaScript 对象 jQuery对象:是包装 Dom 对象后产生的对象. 一:JQuery 对象和 Dom 对象 在使用 JQuery 过程中,我们一般(也是绝大多 ...
- 【转】Ruby on Rails中select使用方法
在Ruby on Rails中真的有一堆Select helper可以用,我们经常容易混淆.常见的有三个..select, select_tag, collection_select(其余的什么sel ...
- 超牛 猴子补丁,修改python内置的print
猴子补丁一般是用于修改三方包或官方包,也可以用来修改自己或者他人的代码. 但也可以用来修改python 语言内置的关键字. 本篇博客修改python最常用的内置print,使你使用print时候,自动 ...
- etcd命令
etcdctl支持下面列出来的命令,基本上可以分为数据库操作和非数据库操作,可以查看etcdctl README.md来了解更多 ➜ ~ etcdctl -hNAME: etcdctl - A sim ...