Reputation: 1428
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
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
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