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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/caomage/article/details/83622628

一、基于鼠标事件的拖拽

原理——onmousedown、onmousemove、onmouseup

  1. onmousedown
  • 该事件会在鼠标按键被按下时触发
  • 支持该事件的HTML标签:
   <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标签:
   <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标签:
   <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:

<!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:

<!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:

<!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:

拖拽展示


参考资料

新博客地址:http://www.caomage.com

没有更多推荐了,返回首页