Elton Lin
Elton Lin

Reputation: 77

Mobile safari unpredictably resizes & re-positions elements being dragged

I just want to drag an image, get an feedback image of identical dimensions, and drag it to the exact visual location. It easily works on desktop.

But for mobile safari, not only does the initial drag activation (hold for about 0.5 seconds) shrinks the element, it also re-positions the contact point to the element's center, such that if I dragged the element from the bottom edge, it will move the feedback image as if I'm holding the center, causing a offset in the drop destination if I go by the "visual representation" which is now lying to the user.

I investigated position absolute, overflow, etc. It seems related to the width of the element - for some reason iOS seems to always want to scale dragged elements to 170px, such that 180px elements get scaled down a little, and 500px elements get scaled down a lot. And the re-positioning is really troubling. However for iPad Safari, it actually scales up 170px elements because it's "relatively" small I'm guessing.

Tiny elements are interesting, they actually expand a little, but surprisingly, they don't get re-positioned upon drag movement, so the dragging visuals stay consistent to reality.

I've scoured MDN docs, iOS archives (tried modifying the pseudoelement), adding a custom image (and setting explicit height and widths), to no avail. Is there any official explanation as to the underlying behavior of iOS Safari so I can predictably work with it? Or does it just have to be guessed through empirical experimentation. The problems can be re-produced by using mobile safari on the snippet below:

Any help is really appreciated it, I've spent 7 hours on this already...

const smallElem = document.getElementById('1')
smallElem.addEventListener('dragstart', onDragstart)

const mediumElem = document.getElementById('2')
mediumElem.addEventListener('dragstart', onDragstart)

const largeElem = document.getElementById('3')
largeElem.addEventListener('dragstart', onDragstart)

function onDragstart (e) {
  e.dataTransfer.setData('text/plain', "You must pass in data");
}
* {
      user-select: none;
  }

.unselectable {
  -moz-user-select: -moz-none;
  -khtml-user-select: none;
  -webkit-user-select: none;
  -o-user-select: none;
  user-select: none;
}

  .peg {
      width: 75px;
      height: 20px;

      text-align: center;
      margin: 10px;
      font-weight: bold;
      border: 1px black solid;
      background: purple;
      font-size: 12px;
      color: white;
  }

  .example {
    border: 1px solid #ccc;
    background: #f7f6f5;
    border-radius: 8px;
    margin: 1em;
    padding: 2em;
    color: #333;
  }

  .test-container {
    padding: 20px;
    display: flex;
    flex-direction: column;
    gap: 2rem;
  }

  .dragdemo {
    /* Even 175px will not work, it will slightly shrink */
    width: 170px;
    height: 100px;
    line-height: 100px;
    text-align: center;
    border-radius: 6px;
    background: green;
    color: #efe;

    -webkit-user-drag: element;
    user-select: none;
  }

  .baseline-div:-webkit-drag {
      background-color:rgb(255,255,154);
      width: 190px;
      height: 100px;
  }


  .baseline-div {
    width: 190px;
    height: 100px;
    line-height: 100px;

    background: #e0e0e0;

    -webkit-user-drag: element;
    user-select: none;
  }
<div class="test-container unselectable">
  <div id="1" class="peg" draggable="true">
  
  </div>
  <pre>w, h = 75, 20, small elements won't be re-positioned, but it expands slightly upon drag activation</pre>
  
  <div class="example">
    <div id="2" class="dragdemo" draggable="true">

    </div>
    <pre>w, h = 170, 100, 170px is the max width where there is no distortion in the drag feedback image's size</pre>
   
    <div id="3"
      class="baseline-div" 
      draggable="true" 
    >
   
    </div>
    w, h = 190, 100
  </div>

Upvotes: 0

Views: 29

Answers (0)

Related Questions