前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >彻底搞懂拖拽——基于鼠标事件的拖拽以及基于HTML5 API的拖拽完整实现

彻底搞懂拖拽——基于鼠标事件的拖拽以及基于HTML5 API的拖拽完整实现

作者头像
从入门到进错门
发布2018-12-05 11:35:26
3.1K0
发布2018-12-05 11:35:26
举报

版权声明:本文为博主原创文章,未经博主允许不得转载。

一、基于鼠标事件的拖拽

原理——onmousedown、onmousemove、onmouseup

  1. onmousedown
  • 该事件会在鼠标按键被按下时触发
  • 支持该事件的HTML标签:
代码语言:javascript
复制
   <a>, <address>, <area>, <b>, <bdo>, <big>, <blockquote>, <body>, <button>,
   <caption>, <cite>, <code>, <dd>, <dfn>, <div>, <dl>, <dt>, <em>, <fieldset>,  
   <form>, <h1> to <h6>, <hr>, <i>, <img>, <input>, <kbd>, <label>, <legend>,  
   <li>, <map>, <ol>, <p>, <pre>, <samp>, <select>, <small>, <span>, <strong>,  
   <sub>, <sup>, <table>, <tbody>, <td>, <textarea>, <tfoot>, <th>, <thead>,  
   <tr>, <tt>, <ul>, <var>
  • 支持该事件的JavaScript对象: button, document, link
  1. onmousemove
  • 该事件会在鼠标指针移动时触发
  • 支持该事件的HTML标签:
代码语言:javascript
复制
   <a>, <address>, <area>, <b>, <bdo>, <big>, <blockquote>, <body>, <button>,  
   <caption>, <cite>, <code>, <dd>, <dfn>, <div>, <dl>, <dt>, <em>, <fieldset>,  
   <form>, <h1> to <h6>, <hr>, <i>, <img>, <input>, <kbd>, <label>, <legend>,  
   <li>, <map>, <ol>, <p>, <pre>, <samp>, <select>, <small>, <span>, <strong>,  
   <sub>, <sup>, <table>, <tbody>, <td>, <textarea>, <tfoot>, <th>, <thead>,  
   <tr>, <tt>, <ul>, <var>
  • 支持该事件的JavaScript对象: 默认情况下,onmousemove不是任何对象的事件,因为鼠标移动非常频繁
  1. onmouseup
  • 该事件会在鼠标按键被松开时触发
  • 支持该事件的HTML标签:
代码语言:javascript
复制
   <a>, <address>, <area>, <b>, <bdo>, <big>, <blockquote>, <body>, <button>,  
   <caption>, <cite>, <code>, <dd>, <dfn>, <div>, <dl>, <dt>, <em>, <fieldset>,  
   <form>, <h1> to <h6>, <hr>, <i>, <img>, <input>, <kbd>, <label>, <legend>,  
   <li>, <map>, <ol>, <p>, <pre>, <samp>, <select>, <small>, <span>, <strong>,  
   <sub>, <sup>, <table>, <tbody>, <td>, <textarea>, <tfoot>, <th>, <thead>,  
   <tr>, <tt>, <ul>, <var>
  • 支持该事件的JavaScript对象: button, document, link

具体实现

code:

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        #test {
            width: 100px;
            height: 100px;
            background: #000;
            position: absolute;
            color: #fff;
        }
    </style>
</head>

<body>
    <div id="test">4616125</div>
    <script>
        (function() {
            function Code() {}
            Code.prototype = {
                addEvent: function() {
                    var that = this;
                    var oDiv = document.getElementById('test');
                    oDiv.onmousedown = function(ev) {
                        var ev = ev || event;
                        var distanceX = ev.clientX - this.offsetLeft;
                        var distanceY = ev.clientY - this.offsetTop;
                        if (oDiv.setCapture) {
                            oDiv.setCapture();
                        }
                        document.onmousemove = function(ev) {
                            var ev = ev || event;
                            oDiv.style.left = ev.clientX - distanceX + 'px';
                            oDiv.style.top = ev.clientY - distanceY + 'px';
                        };
                        document.onmouseup = function(ev) {
                            document.onmousemove = document.onmouseup = null;
                            if (oDiv.releaseCapture) {
                                oDiv.releaseCapture();
                            }
                        };
                    };
                },

                init: function() {
                    var that = this;
                    window.onload = that.addEvent;
                },
            }
            new Code().init();
        })();
    </script>
</body>

</html>

注意事项以及存在的问题:

  1. 注意事项
  • 被拖动的div的position属性值一定是absolute
  • onmousedown事件需要在window.onload时加载
  • 如果被拖动的div上有文字会有自带的文字拖动效果,需要将改div上的所有拖动事件绑定在该div上,可以使用setCapture
  • onmousemove和onmouseup需要在onmousedown里面绑定
  1. 存在的问题
  • 会被拖出边界
超出边界
超出边界

解决方案

  • 只需要实时计算拖拽的元素边框距离上下左右屏幕之间的距离就行了,具体代码如下:

code:

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
        #oDiv {
            width: 100px;
            height: 100px;
            background-color: #000;
            position: absolute;
        }
    </style>
</head>

<body>
    55555555555
    <div id="oDiv"></div>
    <script>
        oDiv.onmousedown = function(e) {
            var ev = e || event;
            var left = ev.clientX - oDiv.offsetLeft,
                top = ev.clientY - oDiv.offsetTop;
            document.onmousemove = function(e) {
                var ev = e || event;
                var leftW = ev.clientX - left;
                var topH = ev.clientY - top;
                //左边不能超出
                if (leftW < 0) {
                    leftW = 0;
                }
                //上边不能超出
                if (topH < 0) {
                    topH = 0;
                }
                //右边不能超出
                if (leftW > document.documentElement.clientWidth - oDiv.offsetWidth) {
                    leftW = document.documentElement.clientWidth - oDiv.offsetWidth;
                }
                //下边不能超出
                if (topH > document.documentElement.clientHeight - oDiv.offsetHeight) {
                    topH = document.documentElement.clientHeight - oDiv.offsetHeight;
                }
                oDiv.style.left = leftW + 'px';
                oDiv.style.top = topH + 'px';
            }
            document.onmouseup = function(e) {
                document.onmousemove = null;
                document.onmouseup = null;
            }
            return false;
        }
    </script>
</body>

</html>

result:

不能拖出边界
不能拖出边界

至此使用鼠标事件的拖拽大功告成!


二、基于HTML5拖拽API的拖拽

前序知识介绍

  一个典型的拖拽操作是这样的:用户用鼠标选中一个可拖动的(draggable)元素,移动鼠标到一个可放置的(droppable)元素,然后释放鼠标。 在操作期间,会触发一些事件类型,有一些事件类型可能会被多次触发(比如drag 和 dragover 事件类型)。   这里涉及几个知识点:

  1. 可拖动元素: 又称为源对象,是指我们鼠标点击之后准备拖动的对象(图片、div、文字等)
  2. 可放置元素: 又称为目标对象,是指可以放置源对象的区域
  3. 事件:

Event

On Event Handler

Description

drag

ondrag

当拖动元素或选中的文本时触发

dragend

ondragend

当拖拽操作结束时触发 (比如松开鼠标按键或敲“Esc”键)

dragenter

ondragenter

当拖动元素或选中的文本到一个可释放目标时触发

dragexit

ondragexit

当元素变得不再是拖动操作的选中目标时触发

dragleave

ondragleave

当拖动元素或选中的文本离开一个可释放目标时触发

dragover

ondragover

当元素或选中的文本被拖到一个可释放目标上时触发

dragstart

ondragstart

当用户开始拖动一个元素或选中的文本时触发

drop

ondrop

当元素或选中的文本在可释放目标上被释放时触发

ps:当从操作系统向浏览器中拖动文件时,不会触发dragstart 和dragend 事件

  1. 接口:

HTML5为所有的拖动相关事件提供了一个新的属性:

  • 源对象和目标对象的事件间传递数据 ev.dataTransfer {}//数据传递对象
  • 源对象上的事件处理中保存数据: ev.dataTransfer.setData(key,value);//key,value必须都是字符串类型
  • 目标对象上的事件处理中读取数据: var value2 = ev.dataTransfer.getData(key);
  1. 兼容性
兼容性
兼容性

ps:图片来源(CAN I USE?


具体实现代码

code:

代码语言:javascript
复制
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style type="text/css">
        #thatDiv {
            width: 500px;
            height: 100px;
            border: 1px solid red;
            position: relative;
        }
        #thisDiv {
            width: 500px;
            height: 100px;
            border: 1px solid black;
            margin-bottom: 20px;
        }
        #tarDiv,
        #tarDiv1,
        #tarDiv2,
        #tarDiv3,
        #tarDiv4 {
            float: left;
            width: 50px;
            height: 50px;
            background-color: #000;
            border: 1px #fff solid;
        }
        .tarDiv {
            color: #fff;
            text-align: center;
            line-height: 50px;
        }
    </style>
</head>

<body>
    <div id="thisDiv">
        <div id="tarDiv" class="tarDiv" draggable="true">1</div>
        <div id="tarDiv1" class="tarDiv" draggable="true">2</div>
        <div id="tarDiv2" class="tarDiv" draggable="true">3</div>
        <div id="tarDiv3" class="tarDiv" draggable="true">4</div>
        <div id="tarDiv4" class="tarDiv" draggable="true">5</div>
    </div>
    <div id="thatDiv"></div>

    <script type="text/javascript">
        var tarDiv = document.getElementsByClassName("tarDiv");
        var thisDiv = document.getElementById("thisDiv");
        var thatDiv = document.getElementById("thatDiv");
        thisDiv.ondragstart = function(ev) {
            var ev = ev || window.event;
            ev.dataTransfer.setData("text", ev.target.id); //将被拖拽的元素的id存入dataTransfer对象中
            window.thisId = ev.target.id;
            ev.dataTransfer.effectAllowed = "copy";
        }
        thatDiv.ondragover = function(ev) { //阻止dragover的默认事件
            var ev = ev || window.event;
            if (typeof ev.preventDefault == "function") {
                ev.preventDefault();
            } else {
                ev.returnValue = false;
            }
            var div = document.getElementById(window.thisId);
            thatDiv.appendChild(div);
            div.style.cssText = "border:1px #fff dashed;";

            ev.preventDefault();
            ev.dataTransfer.dropEffect = "copy";
        }
        thatDiv.ondragenter = function(ev) { //阻止dragenter的默认事件
            var ev = ev || window.event;
            if (typeof ev.preventDefault == "function") {
                ev.preventDefault();
            } else {
                ev.returnValue = false;
            }
        }
        thatDiv.ondragleave = function(ev) {
            var ev = ev || window.event;
            var removeDiv = document.getElementById(window.thisId);
            thatDiv.removeChild(removeDiv);
            thisDiv.appendChild(removeDiv);
            removeDiv.style.cssText = "border:1px #fff solid;";
            ev.preventDefault();
        }
        thatDiv.ondrop = function(ev) {
            var ev = ev || window.event;
            var divId = ev.dataTransfer.getData("Text"); //从dataTransfer对象中取出数据
            if (typeof ev.preventDefault == "function") { //阻止drop事件的默认行为
                ev.preventDefault();
            } else {
                ev.returnValue = false;
            }
            var moveDiv = document.getElementById(divId);
            thatDiv.appendChild(moveDiv);
            moveDiv.setAttribute('draggable', 'false');
            moveDiv.style.cssText = "border:1px #fff solid;";

        }
    </script>
</body>

</html>

result:

拖拽展示
拖拽展示

参考资料

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018年11月01日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、基于鼠标事件的拖拽
    • 原理——onmousedown、onmousemove、onmouseup
      • 具体实现
        • 注意事项以及存在的问题:
          • 解决方案
          • 二、基于HTML5拖拽API的拖拽
            • 前序知识介绍
              • 具体实现代码
                • 参考资料
                相关产品与服务
                流计算 Oceanus
                流计算 Oceanus 是大数据产品生态体系的实时化分析利器,是基于 Apache Flink 构建的企业级实时大数据分析平台,具备一站开发、无缝连接、亚秒延时、低廉成本、安全稳定等特点。流计算 Oceanus 以实现企业数据价值最大化为目标,加速企业实时化数字化的建设进程。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档