Skip to content

Commit e6b496d

Browse files
authored
#1448: Position ghost absolutely on IOS
1 parent 452436a commit e6b496d

File tree

1 file changed

+87
-16
lines changed

1 file changed

+87
-16
lines changed

Sortable.js

Lines changed: 87 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@
7070

7171
targetMoveDistance,
7272

73+
// For positioning ghost absolutely
74+
ghostRelativeParent,
75+
ghostRelativeParentInitialScroll = [], // (left, top)
76+
7377

7478
forRepaintDummy,
7579
realDragElRect, // dragEl rect after current animation
@@ -96,6 +100,9 @@
96100
Edge = !!navigator.userAgent.match(/Edge/i),
97101
FireFox = !!navigator.userAgent.match(/firefox/i),
98102
Safari = !!(navigator.userAgent.match(/safari/i) && !navigator.userAgent.match(/chrome/i) && !navigator.userAgent.match(/android/i)),
103+
IOS = !!(navigator.userAgent.match(/iP(ad|od|hone)/i)),
104+
105+
PositionGhostAbsolutely = IOS,
99106

100107
CSSFloatProperty = Edge || IE11OrLess ? 'cssFloat' : 'float',
101108

@@ -353,6 +360,7 @@
353360
// emulate drag over during autoscroll (fallback), emulating native DnD behaviour
354361
if (isFallback && this.layer === 0) {
355362
Sortable.active._emulateDragOver(true);
363+
Sortable.active._onTouchMove(touchEvt, true);
356364
}
357365
var scrollOffsetY = autoScrolls[this.layer].vy ? autoScrolls[this.layer].vy * speed : 0;
358366
var scrollOffsetX = autoScrolls[this.layer].vx ? autoScrolls[this.layer].vx * speed : 0;
@@ -948,9 +956,9 @@
948956
}
949957
},
950958

951-
_emulateDragOver: function (bypassLastTouchCheck) {
959+
_emulateDragOver: function (forAutoScroll) {
952960
if (touchEvt) {
953-
if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY && !bypassLastTouchCheck) {
961+
if (this._lastX === touchEvt.clientX && this._lastY === touchEvt.clientY && !forAutoScroll) {
954962
return;
955963
}
956964
this._lastX = touchEvt.clientX;
@@ -995,7 +1003,7 @@
9951003
},
9961004

9971005

998-
_onTouchMove: function (/**TouchEvent*/evt) {
1006+
_onTouchMove: function (/**TouchEvent*/evt, forAutoScroll) {
9991007
if (tapEvt) {
10001008
var options = this.options,
10011009
fallbackTolerance = options.fallbackTolerance,
@@ -1004,11 +1012,15 @@
10041012
matrix = ghostEl && _matrix(ghostEl),
10051013
scaleX = ghostEl && matrix && matrix.a,
10061014
scaleY = ghostEl && matrix && matrix.d,
1007-
dx = ((touch.clientX - tapEvt.clientX) + fallbackOffset.x) / (scaleX ? scaleX : 1),
1008-
dy = ((touch.clientY - tapEvt.clientY) + fallbackOffset.y) / (scaleY ? scaleY : 1),
1015+
relativeScrollOffset = PositionGhostAbsolutely && ghostRelativeParent && _getRelativeScrollOffset(ghostRelativeParent),
1016+
dx = ((touch.clientX - tapEvt.clientX)
1017+
+ fallbackOffset.x) / (scaleX || 1)
1018+
+ (relativeScrollOffset ? (relativeScrollOffset[0] - ghostRelativeParentInitialScroll[0]) : 0) / (scaleX || 1),
1019+
dy = ((touch.clientY - tapEvt.clientY)
1020+
+ fallbackOffset.y) / (scaleY || 1)
1021+
+ (relativeScrollOffset ? (relativeScrollOffset[1] - ghostRelativeParentInitialScroll[1]) : 0) / (scaleY || 1),
10091022
translate3d = evt.touches ? 'translate3d(' + dx + 'px,' + dy + 'px,0)' : 'translate(' + dx + 'px,' + dy + 'px)';
10101023

1011-
10121024
// only set the status to dragging, when we are actually dragging
10131025
if (!Sortable.active && !awaitingDragStarted) {
10141026
if (fallbackTolerance &&
@@ -1019,13 +1031,11 @@
10191031
this._onDragStart(evt, true);
10201032
}
10211033

1022-
this._handleAutoScroll(touch, true);
1023-
1034+
!forAutoScroll && this._handleAutoScroll(touch, true);
10241035

10251036
moved = true;
10261037
touchEvt = touch;
10271038

1028-
10291039
_css(ghostEl, 'webkitTransform', translate3d);
10301040
_css(ghostEl, 'mozTransform', translate3d);
10311041
_css(ghostEl, 'msTransform', translate3d);
@@ -1036,11 +1046,46 @@
10361046
},
10371047

10381048
_appendGhost: function () {
1049+
// Bug if using scale(): https://stackoverflow.com/questions/2637058
1050+
// Not being adjusted for
10391051
if (!ghostEl) {
1040-
var rect = _getRect(dragEl, this.options.fallbackOnBody ? document.body : rootEl, true),
1052+
var container = this.options.fallbackOnBody ? document.body : rootEl,
1053+
rect = _getRect(dragEl, true, container, !PositionGhostAbsolutely),
10411054
css = _css(dragEl),
10421055
options = this.options;
10431056

1057+
// Position absolutely
1058+
if (PositionGhostAbsolutely) {
1059+
// Get relatively positioned parent
1060+
ghostRelativeParent = container;
1061+
1062+
while (
1063+
_css(ghostRelativeParent, 'position') === 'static' &&
1064+
_css(ghostRelativeParent, 'transform') === 'none' &&
1065+
ghostRelativeParent !== document
1066+
) {
1067+
ghostRelativeParent = ghostRelativeParent.parentNode;
1068+
}
1069+
1070+
if (ghostRelativeParent !== document) {
1071+
var ghostRelativeParentRect = _getRect(ghostRelativeParent, true);
1072+
1073+
rect.top -= ghostRelativeParentRect.top;
1074+
rect.left -= ghostRelativeParentRect.left;
1075+
}
1076+
1077+
if (ghostRelativeParent !== document.body && ghostRelativeParent !== document.documentElement) {
1078+
if (ghostRelativeParent === document) ghostRelativeParent = _getWindowScrollingElement();
1079+
1080+
rect.top += ghostRelativeParent.scrollTop;
1081+
rect.left += ghostRelativeParent.scrollLeft;
1082+
} else {
1083+
ghostRelativeParent = _getWindowScrollingElement();
1084+
}
1085+
ghostRelativeParentInitialScroll = _getRelativeScrollOffset(ghostRelativeParent);
1086+
}
1087+
1088+
10441089
ghostEl = dragEl.cloneNode(true);
10451090

10461091
_toggleClass(ghostEl, options.ghostClass, false);
@@ -1054,11 +1099,11 @@
10541099
_css(ghostEl, 'width', rect.width);
10551100
_css(ghostEl, 'height', rect.height);
10561101
_css(ghostEl, 'opacity', '0.8');
1057-
_css(ghostEl, 'position', 'fixed');
1102+
_css(ghostEl, 'position', (PositionGhostAbsolutely ? 'absolute' : 'fixed'));
10581103
_css(ghostEl, 'zIndex', '100000');
10591104
_css(ghostEl, 'pointerEvents', 'none');
10601105

1061-
options.fallbackOnBody && document.body.appendChild(ghostEl) || rootEl.appendChild(ghostEl);
1106+
container.appendChild(ghostEl);
10621107
}
10631108
},
10641109

@@ -2245,7 +2290,7 @@
22452290
* @param {[Boolean]} adjustForTransform Whether the rect should compensate for parent's transform
22462291
* @return {Object} The boundingClientRect of el
22472292
*/
2248-
function _getRect(el, container, adjustForTransform) {
2293+
function _getRect(el, adjustForTransform, container, adjustForFixed) {
22492294
if (!el.getBoundingClientRect && el !== win) return;
22502295

22512296
var elRect,
@@ -2273,7 +2318,7 @@
22732318
width = window.innerWidth;
22742319
}
22752320

2276-
if (adjustForTransform && el !== win) {
2321+
if (adjustForFixed && el !== win) {
22772322
// Adjust for translate()
22782323
container = container || el.parentNode;
22792324

@@ -2295,9 +2340,11 @@
22952340
/* jshint boss:true */
22962341
} while (container = container.parentNode);
22972342
}
2343+
}
22982344

2345+
if (adjustForTransform && el !== win) {
22992346
// Adjust for scale()
2300-
var matrix = _matrix(el),
2347+
var matrix = _matrix(container || el),
23012348
scaleX = matrix && matrix.a,
23022349
scaleY = matrix && matrix.d;
23032350

@@ -2328,7 +2375,6 @@
23282375
* Checks if a side of an element is scrolled past a side of it's parents
23292376
* @param {HTMLElement} el The element who's side being scrolled out of view is in question
23302377
* @param {String} side Side of the element in question ('top', 'left', 'right', 'bottom')
2331-
* @return {int} Amount the element is overflowing over the specified side of it's parent scroll element
23322378
* @return {HTMLElement} The parent scroll element that the el's side is scrolled past, or null if there is no such element
23332379
*/
23342380
function _isScrolledPast(el, side) {
@@ -2356,6 +2402,31 @@
23562402
return false;
23572403
}
23582404

2405+
/**
2406+
* Returns the scroll offset of the given element, added with all the scroll offsets of parent elements.
2407+
* The value is returned in real pixels.
2408+
* @param {HTMLElement} el
2409+
* @return {Array} Offsets in the format of [left, top]
2410+
*/
2411+
function _getRelativeScrollOffset(el) {
2412+
var offsetLeft = 0,
2413+
offsetTop = 0,
2414+
winScroller = _getWindowScrollingElement();
2415+
2416+
if (el) {
2417+
do {
2418+
var matrix = _matrix(el),
2419+
scaleX = matrix.a,
2420+
scaleY = matrix.d;
2421+
2422+
offsetLeft += el.scrollLeft * scaleX;
2423+
offsetTop += el.scrollTop * scaleY;
2424+
} while (el !== winScroller && (el = el.parentNode));
2425+
}
2426+
2427+
return [offsetLeft, offsetTop];
2428+
}
2429+
23592430
// Fixed #973:
23602431
_on(document, 'touchmove', function(evt) {
23612432
if ((Sortable.active || awaitingDragStarted) && evt.cancelable) {

0 commit comments

Comments
 (0)