user1791567
user1791567

Reputation: 1428

Allow select text on a HTML 5 draggable child element

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>

Upvotes: 13

Views: 7479

Answers (2)

Neven.Leung
Neven.Leung

Reputation: 860

Updates for the draggable issue in Firefox. (Mar. 8, 2024)

// Allow the text in content can be selected.
contentEl.onmousedown = function(event) {
  itemEl.setAttribute("draggable", "false");
};

// Let the item be draggable again after the text selection is done.
contentEl.onmouseleave = function(event) {
  itemEl.setAttribute("draggable", "true");
};

Inspired by https://stackoverflow.com/a/31733408/6359074


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 contentEl = document.querySelector('.content');

let mouseDownEl;

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

itemEl.ondragstart = function(event) {
  // Only the handle can be picked up to trigger the drag event.
  if (mouseDownEl.matches('.handle')) {
    // ...code
  } else {
    event.preventDefault();
  }
}

/* 
 * The code below is just for the draggable issue in Firefox. 
 */  

// Allow the text in content can be selected.
contentEl.onmousedown = function(event) {
  itemEl.setAttribute("draggable", "false");
};

// Let the item be draggable again after the text selection is done.
contentEl.onmouseleave = function(event) {
  itemEl.setAttribute("draggable", "true");
};
.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>

Upvotes: 10

Asons
Asons

Reputation: 87191

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>

Upvotes: 2

Related Questions