基于鼠标事件和H5的拖拽实现

765 阅读6分钟

基于鼠标事件的拖拽

原理——onmousedownonmousemoveonmouseup

  • 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
      
  • 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不是任何对象的事件,因为鼠标移动非常频繁
      
  • 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
      

具体实现

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

注意事项及存在的问题

  • 注意事项
    • 被拖动的元素必须是绝对定位(position:absolute;)
    • onmousedown事件需要在window.onload时加载
    • 如果被拖动元素上有文字会有自带的文字拖动效果,需要将该元素上的所有拖动事件绑定在该元素上
    • onmousemoveonmouseup需要在onmousedown里面绑定,在拖拽完成之后需要重新定义事件值为null
  • 存在的问题
    • 会被拖拽出边界,需要进行边界判断

解决方案

  • 在拖拽时对元素边界进行实时判断

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

基于HTML5拖拽API实现拖拽

前序:一个典型的拖拽操作为用户用鼠标选中一个可拖动(draggable)元素,移动鼠标到一个可放置(droppable)元素上,然后松开鼠标。在操作期间,会触发事件,有些事件可能会被多次触发(比如dragdragover事件)

  • 可拖动元素

    又称为源对象,是指我们点击之后准备拖动的对象(图片,容器,文字等元素)

  • 可放置元素

    又称为目标对象,是指可以放置源对象的区域

  • 事件

    Event On Event Handler Description
    drag ondrag 当拖动元素或选中的文本时触发
    dragend ondragend 当拖拽操作结束时触发(比如松开鼠标或者按下“Ese”键)
    dragenter ondragenter 当拖动元素或选中的文本到一个可释放目标时触发
    dragexit ondragexit 当元素变得不再是拖动操作的选中目标时触发
    dragleave ondragleave 当拖动元素或选中的文本离开一个可释放目标上时触发
    dragover ondragover 当元素或选中的文本拖到一个可释放目标时触发
    dragstart ondragstart 当用户爱是拖动一个元素或选中的文本时触发
    drop ondrop 当元素或选中的文本在可释放目标上释放时触发

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

  • 源对象和目标对象的事件间传递数据

    ev.dataTransfer{} // 数据传递对象

  • 源对象上的事件处理中保存数据

    ev.dataTransfer.setData(key,value) // key,value必须都是字符串类型

  • 目标对象上的事件处理中读取数据

    var value = ev.dataTransfer.getData(key)

  • 兼容性

全部代码

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