徹底搞懂拖拽——基於滑鼠事件的拖拽以及基於HTML5 API的拖拽完整實現
一、基於滑鼠事件的拖拽
原理——onmousedown、onmousemove、onmouseup
- 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標籤:
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>
注意事項以及存在的問題:
- 注意事項
- 被拖動的div的position屬性值一定是absolute
- onmousedown事件需要在window.onload時載入
- 如果被拖動的div上有文字會有自帶的文字拖動效果,需要將改div上的所有拖動事件綁定在該div上,可以使用setCapture
- onmousemove和onmouseup需要在onmousedown裡面綁定
- 會被拖出邊界
解決方案
只需要實時計算拖拽的元素邊框距離上下左右屏幕之間的距離就行了,具體代碼如下:
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 事件類型)。
這裡涉及幾個知識點:- 可拖動元素: > 又稱為源對象,是指我們滑鼠點擊之後準備拖動的對象(圖片、div、文字等)
- 可放置元素: >又稱為目標對象,是指可以放置源對象的區域
- 事件:
- 介面:
HTML5為所有的拖動相關事件提供了一個新的屬性:
源對象和目標對象的事件間傳遞數據ev.dataTransfer {}//數據傳遞對象
源對象上的事件處理中保存數據:ev.dataTransfer.setData(key,value);//key,value必須都是字元串類型
var value2 = ev.dataTransfer.getData(key);
- 兼容性
具體實現代碼
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:
os:這知乎的Markdown支持度讓人難受!
參考資料
- w3school在線教程
推薦閱讀: