平时在我们页面上,经常会悬浮着一些功能按钮,如帮助,联系客服等,按钮的显示比较简单,用定位悬浮在自己需要的位置上就行,比如下面的页面上我们展示一个帮助的按钮,用户点击后可以展示一些帮助的信息:
代码:
- <!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"
- />
- <style type="text/css">
- .help-btn {
- width: 50px;
- height: 50px;
- border-radius: 50%;
- background: blue;
- color: #fff;
- position: fixed;
- display: flex;
- align-items: center;
- justify-content: center;
- cursor: pointer;
- right: 0;
- bottom: 150px;
- }
- </style>
- <title>js实现拖拽</title>
- </head>
- <body>
- <div class="help-btn" id="help-btn">帮助</div>
- </body>
- </html>
但是这样定位一般是固定在这个位置,如果这时候刚好【帮助按钮】挡住了这个位置原本有的其他操作按钮,那么我们就需要能够把【帮助按钮】拖开,不影响原本页面上的操作。
一般实现拖动的思路是:在【帮助按钮】上鼠标按下时,记录下当前的坐标,以及原本相对于父元素的位置,然后监听【帮助按钮】的鼠标移动事件【onmousemove】,然后取现在的坐标,计算出两轴上移动的距离,用原本的位置减去移动的位置得到现在的位置,再重新设置给【帮助按钮】即可。最后在【帮助按钮】鼠标抬起事件【onmouseup】上注销对鼠标移动的监听即可。
代码:
- <script type="text/javascript">
- let btnEle = document.getElementById("help-btn");
-
- //帮助按钮鼠标按下时
- btnEle.onmousedown = (e) => {
- let defaultX = e.clientX; //默认位置的x轴坐标
- let defaultY = e.clientY; //默认位置的y轴坐标
-
- let defaultLeft = btnEle.offsetLeft; //默认左侧偏移位置
- let defaultTop = btnEle.offsetTop; //默认顶部偏移位置
-
- //帮助按钮鼠标移动时
- btnEle.onmousemove = (me) => {
- btnEle.style.cursor = "move"; //修改鼠标样式
-
- let nowX = me.clientX; //当前位置的x轴坐标
- let nowY = me.clientY; //当前位置的y轴坐标
-
- let moveX = defaultX - nowX; //在x轴上的移动距离
- let moveY = defaultY - nowY; //在y轴上移动的距离
-
- let nowLeft = defaultLeft - moveX; //当前位置左侧偏移量
- let nowTop = defaultTop - moveY; //当前位置顶部偏移量
-
- btnEle.style.left = `${nowLeft}px`; //将当前位置赋值给帮助按钮
- btnEle.style.top = `${nowTop}px`;
- };
-
- //鼠标抬起时
- btnEle.onmouseup = () => {
- btnEle.style.cursor = "default"; //重置鼠标样式
- //清除事件
- btnEle.onmousemove = null;
- btnEle.onmouseup = null;
- };
- };
- </script>
至此可以实现拖动【帮助按钮】移动位置。但是会出现以下几个问题:
问题1:移动过快,鼠标超过【帮助按钮】的范围,会导致鼠标抬起事件失效,造成移动卡顿;
解决方法,将鼠标移动事件和鼠标抬起事件挂载在document上。
代码:
- <script type="text/javascript">
- //帮助按钮鼠标按下时
- btnEle.onmousedown = (e) => {
- ...
- //鼠标移动时
- document.onmousemove = (me) => {
- ...
- };
- //鼠标抬起时
- document.onmouseup = () => {
- ...
- //清除事件
- document.onmousemove = null;
- document.onmouseup = null;
- };
- };
- </script>
问题2:鼠标移动过快,可能会选中“帮助”文字,这时候会造成鼠标抬起事件失效。出现移动bug。
解决方法:在移动时禁用文字选中。鼠标抬起时还原
代码:
- <script type="text/javascript">
- //帮助按钮鼠标按下时
- btnEle.onmousedown = (e) => {
- ...
- //禁用文字选中
- document.onselectstart = () => {
- return false;
- };
- //鼠标移动时
- ...
- //鼠标抬起时
- document.onmouseup = () => {
- ...
- document.onselectstart = null;
- };
- };
- </script>
问题3:会移动到屏幕外面去。
解决方法:限制范围不超过页面区域(或者你自定义父容器的内容范围)。
代码:
- <script type="text/javascript">
- ...
- //帮助按钮鼠标按下时
- btnEle.onmousedown = (e) => {
- ...
- let pageWidth = window.innerWidth; //页面宽度
- let pageHeight = window.innerHeight; //页面高度
- ...
- //鼠标移动时
- document.onmousemove = (me) => {
- ...
- //对位置进行限定,只显示在设定的区域内
- nowLeft = nowLeft <= 0 ? 0 : nowLeft;
- nowLeft =
- nowLeft >= pageWidth - btnEle.offsetWidth
- ? pageWidth - btnEle.offsetWidth
- : nowLeft;
-
- nowTop = nowTop <= 0 ? 0 : nowTop;
- nowTop =
- nowTop >= pageHeight - btnEle.offsetHeight
- ? pageHeight - btnEle.offsetHeight
- : nowTop;
-
- ...
- };
- ...
- };
- </script>
问题4:拖动的时候也会触发【帮助按钮】的点击事件。
解决方法:在鼠标按下时,记录当前时间,在鼠标抬起时再记录一次时间。在点击事件方法中对两个时间差值进行判断,此处我设定是100,可根据自己需求调整。大于这个时间间隔则为拖拽,不继续执行点击的方法。
代码:
- <script type="text/javascript">
- ...
- let startTime = 0; //鼠标点击时间
- let endTime = 0; //鼠标抬起时间
-
- //帮助按钮鼠标按下时
- btnEle.onmousedown = (e) => {
- startTime = new Date().getTime();
- ...
- //鼠标抬起时
- document.onmouseup = () => {
- endTime = new Date().getTime();
- ...
- };
- };
-
- //按钮的点击事件
- btnEle.onclick = () => {
- if (endTime - startTime >= 100) return false; //说明是拖拽
- console.log("我被点击了!");
- };
- </script>
到这里为止在单页面上可以实现元素的拖动。但是如果页面上还存在iframe窗口,当鼠标经过iframe窗口的时候,会出现拖拽卡顿,鼠标抬起事件失效的问题。
解决思路,在鼠标点击的时候在页面上加上一个蒙层,把蒙层作为父元素进行监听。鼠标移除的时候,去除蒙层。
完整代码:
- <!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"
- />
- <style type="text/css">
- .help-btn {
- width: 50px;
- height: 50px;
- border-radius: 50%;
- background: blue;
- color: #fff;
- position: fixed;
- display: flex;
- align-items: center;
- justify-content: center;
- cursor: pointer;
- right: 0;
- bottom: 150px;
- z-index: 9999;
- }
-
- .move-modal {
- position: fixed;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- z-index: 9999;
- }
- </style>
- <title>js实现拖拽</title>
- </head>
- <body>
- <!-- 蒙层 -->
- <div id="move-modal"></div>
-
- <div class="help-btn" id="help-btn">帮助</div>
- </body>
- <script type="text/javascript">
- let btnEle = document.getElementById("help-btn");
-
- let startTime = 0; //鼠标点击时间
- let endTime = 0; //鼠标抬起时间
-
- //帮助按钮鼠标按下时
- btnEle.onmousedown = (e) => {
- startTime = new Date().getTime();
- let fatherModal =
- document.getElementById("move-modal"); //拿到蒙层元素
- fatherModal.classList.add("move-modal"); //给页面加上蒙层
-
- let defaultX = e.clientX; //默认位置的x轴坐标
- let defaultY = e.clientY; //默认位置的y轴坐标
-
- let defaultLeft = btnEle.offsetLeft; //默认左侧偏移位置
- let defaultTop = btnEle.offsetTop; //默认顶部偏移位置
-
- let pageWidth = fatherModal.offsetWidth; //页面宽度
- let pageHeight = fatherModal.offsetHeight; //页面高度
-
- //禁用文字选中
- window.onselectstart = () => {
- return false;
- };
-
- //鼠标移动时
- fatherModal.onmousemove = (me) => {
- btnEle.style.cursor = "move"; //修改鼠标样式
-
- let nowX = me.clientX; //当前位置的x轴坐标
- let nowY = me.clientY; //当前位置的y轴坐标
-
- let moveX = defaultX - nowX; //在x轴上的移动距离
- let moveY = defaultY - nowY; //在y轴上移动的距离
-
- let nowLeft = defaultLeft - moveX; //当前位置左侧偏移量
- let nowTop = defaultTop - moveY; //当前位置顶部偏移量
-
- //对位置进行限定,只显示在设定的区域内
- nowLeft = nowLeft <= 0 ? 0 : nowLeft; //不能超过左侧
- nowLeft =
- nowLeft >= pageWidth - btnEle.offsetWidth
- ? pageWidth - btnEle.offsetWidth
- : nowLeft; //不能超过右侧
-
- nowTop = nowTop <= 0 ? 0 : nowTop; //不能超过顶部
- nowTop =
- nowTop >= pageHeight - btnEle.offsetHeight
- ? pageHeight - btnEle.offsetHeight
- : nowTop; //不能超过底部
-
- //将当前位置赋值给帮助按钮
- btnEle.style.left = `${nowLeft}px`;
- btnEle.style.top = `${nowTop}px`;
- };
-
- //鼠标抬起时,此处必须是document,不然当多屏幕的时候,鼠标移出当前屏幕还是不会触发鼠标抬起
- document.onmouseup = () => {
- endTime = new Date().getTime();
- btnEle.style.cursor = "default"; //重置鼠标样式
- fatherModal.classList.remove("move-modal"); //给页面去除蒙层
- //清除事件
- fatherModal.onmousemove = null;
- document.onmouseup = null;
- window.onselectstart = null;
- };
- };
-
- //按钮的点击事件
- btnEle.onclick = () => {
- if (endTime - startTime >= 100) return false; //说明是拖拽
- console.log("我被点击了!");
- };
- </script>
- </html>