EN
JavaScript - how to make drag and drop div with handle inside another element?
15 points
Using JavaScript it is possible to make drag&drop div element with handle, inside another draggable element in the following way.

Note: big advantage of presented approach in this section is resistance for changed margin, border and padding sizes (with nested elements too).
xxxxxxxxxx
1
2
<html>
3
<head>
4
<style>
5
6
div.draggable {
7
position: absolute;
8
left: 20px; right: 20px;
9
border: 5px solid gray;
10
overflow: hidden;
11
}
12
13
div.handle {
14
padding: 3px;
15
position: absolute;
16
left: 0; top: 0; right: 0;
17
background: gold;
18
height: 25px;
19
}
20
21
div.body {
22
padding: 3px;
23
position: absolute;
24
left: 0; top: 25px; right: 0; bottom: 0;
25
background: cyan;
26
/* this style disables elements selection when user is dragging */
27
touch-callout: none; /* iOS Safari */
28
user-select: none; /* Safari */
29
user-select: none; /* Konqueror HTML */
30
user-select: none; /* Firefox in the past (old versions) */
31
user-select: none; /* Internet Explorer (>=10) / Edge */
32
user-select: none; /* Currently supported: */
33
/* Chrome, Opera and Firefox */
34
}
35
36
</style>
37
<script type="text/javascript">
38
39
function computeMargins(element) {
40
var style = element.currentStyle || window.getComputedStyle(element);
41
var result = {
42
marginLeft: parseInt(style.marginLeft),
43
marginRight: parseInt(style.marginRight),
44
marginTop: parseInt(style.marginTop),
45
marginBottom: parseInt(style.marginBottom)
46
};
47
return result;
48
}
49
50
function prepareDragging(element, handle) {
51
var dragging = false;
52
var clickX, clickY;
53
var positionX, positionY;
54
var style = element.style;
55
function onMouseDown(e) {
56
clickX = e.clientX;
57
clickY = e.clientY;
58
var margins = computeMargins(element);
59
// this approach prevents against dragging issues when margins have set sizes
60
positionX = element.offsetLeft - margins.marginLeft - margins.marginRight;
61
positionY = element.offsetTop - margins.marginTop - margins.marginBottom;
62
dragging = true;
63
}
64
function onMouseUp(e) {
65
dragging = false;
66
}
67
function onMouseMove(e) {
68
if (dragging) {
69
var x = positionX + e.clientX - clickX;
70
var y = positionY + e.clientY - clickY;
71
style.left = x + 'px';
72
style.top = y + 'px';
73
}
74
}
75
var onTouchStart = function(event) {
76
var touches = event.touches;
77
if (touches.length === 1) {
78
event.preventDefault();
79
var touch = touches[0];
80
onMouseDown({
81
clientX: touch.clientX,
82
clientY: touch.clientY
83
});
84
}
85
};
86
var onTouchEnd = function(event) {
87
var touches = event.touches;
88
if (touches.length === 0) {
89
onMouseUp({});
90
}
91
};
92
var onTouchMove = function(event) {
93
if (dragging) {
94
event.preventDefault();
95
var touches = event.touches;
96
if (touches.length === 1) {
97
var touch = touches[0];
98
onMouseMove({
99
clientX: touch.clientX,
100
clientY: touch.clientY
101
});
102
}
103
}
104
};
105
handle.addEventListener('mousedown', onMouseDown);
106
handle.addEventListener('touchstart', onTouchStart);
107
window.addEventListener('mouseup', onMouseUp);
108
window.addEventListener('touchend', onTouchEnd);
109
window.addEventListener('mousemove', onMouseMove);
110
window.addEventListener('touchmove', onTouchMove);
111
return function() {
112
if (removed) {
113
return;
114
}
115
handle.removeEventListener('mousedown', onMouseDown);
116
handle.removeEventListener('touchstart', onTouchStart);
117
window.removeEventListener('mouseup', onMouseUp);
118
window.removeEventListener('touchend', onTouchEnd);
119
window.removeEventListener('mousemove', onMouseMove);
120
window.removeEventListener('touchmove', onTouchMove);
121
removed = true;
122
};
123
}
124
125
</script>
126
</head>
127
<body style="height: 300px;">
128
<div id="my-element-1" class="draggable" style="width: 200px; height: 200px;">
129
<div id="my-handle-1" class="handle">Drag me...</div>
130
<div class="body">
131
<p>Something inside...</p>
132
<div id="my-element-2" class="draggable" style="width: 100px; height: 100px;">
133
<div id="my-handle-2" class="handle">Drag me...</div>
134
<div class="body">Something inside...</div>
135
</div>
136
</div>
137
</div>
138
<script>
139
140
var element1 = document.querySelector('#my-element-1');
141
var handle1 = document.querySelector('#my-handle-1');
142
143
prepareDragging(element1, handle1);
144
145
var element2 = document.querySelector('#my-element-2');
146
var handle2 = document.querySelector('#my-handle-2');
147
148
prepareDragging(element2, handle2);
149
150
</script>
151
</body>
152
</html>