10

Having a table with draggable rows where each row is draggable=true, how can the user still be able to select text from a column?

<table>
 <thead>..</thead>
 <tbody>
  ..
  <tr draggable="true">
   <td>..</td>
   <td>Cool text but you can't select me</td>
   <td>..</td>
  </tr>
  ..
</tbody>
</table>

Another simple example (https://codepen.io/anon/pen/qjoBXV)

div {
  padding: 20px;
  margin: 20px;
  background: #eee;
}

.all-copy p {  
  -webkit-user-select: all;  /* Chrome all / Safari all */
  -moz-user-select: all;     /* Firefox all */
  -ms-user-select: all;      /* IE 10+ */
  user-select: all;          /* Likely future */   
}    
<div class="all-copy" draggable="true">
      <p>Select me as text</p>
    </div>
user1791567
  • 1,388
  • 1
  • 16
  • 29
  • I don't know if it is just Mac browsers, but you can still select the text by right-clicking the element. – psimekjr Jun 30 '17 at 23:24
  • 1
    Also, I subjectively think it is better practice to have a clear and visually distinguished element for handling drag and drop rather than allowing to drag the whole row when you also need to select the text. The draggable area deserves a different cursor than selectable text at least. – psimekjr Jun 30 '17 at 23:29
  • 1
    Yeah, not sure about browsers running in Windows. Mozilla doc says that pressing Alt browser should allow to select the text but that never worked. Agree about the handler. I'm just following specs. – user1791567 Jul 05 '17 at 07:50

2 Answers2

9

There are two things we need to do.

  • One thing is limitting the drag event only trigger on specified area, for example, the drag handle.

  • The other thing is that we only set the text on the div with content class can be selected. The reason why we do so is that the element that has been set to draggable, on which browser will add a default rule user-select: none.

const itemEl = document.querySelector('.item');
const handleEl = document.querySelector('.handle');

let mouseDownEl;

itemEl.onmousedown = function(evt) {
  mouseDownEl = evt.target;
}

itemEl.ondragstart = function(evt) {
  // only the handle div can be picked up to trigger the drag event
  if (mouseDownEl.matches('.handle')) {
    // ...code
  } else {
    evt.preventDefault();
  }
}
.item {
  width: 70px;
  border: 1px solid black;
  text-align: center;
}

.content {
  border-top: 1px solid gray;
  user-select: text;
}
<div class="item" draggable="true">
  <div class='handle'>handle</div>
  <div class='content'>content</div>
</div>
Neven.Leung
  • 820
  • 6
  • 12
  • Note: This does not work in Firefox - selecting "content" won't drag the parent, but the text also won't be selected. – kevlarr May 11 '23 at 15:14
2

One way to make that work, is to actually check which element fired the event, e.target, against the element that has the listener attach to itself, #draggable (in this case using this).

if (e.target === this) {...}

This will allow default behavior on element positioned inside the draggable element, such as selecting a text and so on.

Note, since Firefox has issue with draggable="true", I used a different drag method.

Stack snippet

(function (elem2drag) {
  var x_pos = 0, y_pos = 0, x_elem = 0, y_elem = 0;  
  
  document.querySelector('#draggable').addEventListener('mousemove', function(e) {
    x_pos = e.pageX;
    y_pos = e.pageY;
    if (elem2drag !== null) {
        elem2drag.style.left = (x_pos - x_elem) + 'px';
        elem2drag.style.top = (y_pos - y_elem) + 'px';
    }  
  })

  document.querySelector('#draggable').addEventListener('mousedown', function(e) {
    if (e.target === this) {
      elem2drag = this;
      x_elem = x_pos - elem2drag.offsetLeft;
      y_elem = y_pos - elem2drag.offsetTop;
      return false;
    }  
  })
  
  document.querySelector('#draggable').addEventListener('mouseup', function(e) {
    elem2drag = null;
  })
})(null);
#draggable {
  display: inline-block;
  background: lightgray;
  padding:15px;
  cursor:move;
  position:relative;
}
span {
  background: white;
  line-height: 25px;
  cursor:auto;  
}
<div id="draggable">
  <span>Select me as text will work<br>when the mouse is over the text</span>
</div>
Asons
  • 84,923
  • 12
  • 110
  • 165