Reputation: 2063
I have a nice little React drag-drop library that works for mouse- and touch systems. For touch, it grabs the touch location via clientX
and clientY
(e.targetTouches[0].clientX, e.targetTouches[0].clientY
). It uses those coordinates to place the dragged element, which has position: fixed
.
HOWEVER it turns out that at least on IOS Safari (v.11.x), when you zoom the display, the coordinate system for position:fixed no longer matches the window coordinate system. So the dragged element displays in the wrong place on the page.
Picture the zoomed-in browser window as a small rectangular view onto a larger rectangle containing the un-zoomed content. The location:fixed coordinate system uses the larger rectangle. The window coordinate system uses the small one.
As you scroll, the window pans around the larger rectangle in a way that's difficult to describe, with the result that the offset between 0,0 in position-fixed-land and 0,0 in the browser window is always changing.
How can I get the offset between the browser window and the "position:fixed" coordinate systems? (I can then add that offset to the position of the dragged element to position it correctly.)
Upvotes: 4
Views: 1802
Reputation: 34032
I came across this in my search for a solution. I have a position:absolute
element that I update the position on when the window is resized, and iOS triggers this on zooming in/out.
I discovered window.visualViewport
which has the key information needed to offset this!
My solution for setting the position/size of an element with the zoom offset:
positionNavHighlight(link: HTMLElement) {
const rect = link.getBoundingClientRect();
const offsetTop = window.visualViewport.offsetTop;
const offsetLeft = window.visualViewport.offsetLeft;
navActiveBg.style.top = `${rect.top + offsetTop}px`;
navActiveBg.style.left = `${rect.left + offsetLeft}px`;
navActiveBg.style.height = `${rect.height}px`;
navActiveBg.style.width = `${rect.width}px`;
}
Upvotes: 0
Reputation: 2063
Stick an element at 0,0, position:fixed.
Get its x/y offset from the browser window using getBoundingClientRect()
.
Then delete the element.
function getFixedOffset() {
let fixedElem = document.createElement('div');
fixedElem.style.cssText = 'position:fixed; top: 0; left: 0';
document.body.appendChild(fixedElem);
const rect = fixedElem.getBoundingClientRect();
document.body.removeChild(fixedElem);
return [rect.left, rect.top]
}
This works (yay!) but feels pretty kludgey, creating and then destroying a DOM element every time the user drags-and-drops. Other suggestions are welcome.
Upvotes: 1