clauderic / react-sortable-hoc

A set of higher-order components to turn any list into an animated, accessible and touch-friendly sortable list✌️
https://clauderic.github.io/react-sortable-hoc/
MIT License
10.82k stars 982 forks source link

Attempting to drag in Safari and IE selects text on page #253

Open mccambridge opened 7 years ago

mccambridge commented 7 years ago

When I drag an item in Safari or IE 11, the text on other areas of the page that my mouse travels by gets selected. It's as if the browser doesn't know I'm dragging but thinks my mouse went down for the purpose of highlighting text on the page.

Great hoc! I definitely appreciate your work! :D

wahabshah commented 7 years ago

Firefox and IE both have the same problem of a drag causing selection. The solution I found is just two steps:-

  1. Define the following css class :-

    .noselect {
    -webkit-touch-callout: none; /* iOS Safari */
    -webkit-user-select: none; /* Safari */
     -khtml-user-select: none; /* Konqueror HTML */
       -moz-user-select: none; /* Firefox */
        -ms-user-select: none; /* Internet Explorer/Edge */
            user-select: none; /* Non-prefixed version, currently
                                  supported by Chrome and Opera */
    }
  2. Apply the css class on your SortableItem component (div or li which you will drag) like so :-
const SortableItem = SortableElement(({value,SortableItemClassName}) =>
  <li className="noselect">{value}</li>
);

Check both the reproduced problem and its solution by opening the sandbox in Firefox :- https://codesandbox.io/s/G6jPqD9oy

The source of solution is from Overflowstack

Finesse commented 6 years ago

@wahabshah It doesn't work in Safari for me.

Here is my solution. Add the special sort start event handler to your sortable container:

const SortableList = SortableContainer(/* your container */);

ReactDOM.render(
    <SortableList
        items={[]} // your list items
        onSortStart={(_, event) => event.preventDefault()} // it does the trick
        onSortEnd={() => {}} // your sort end handler
    />,
    document.getElementById('app')
);

Unfortunately it doesn't work when the pressDelay or the distance option is set.

I insist that this handler must be in react-sortable-hoc by default.

dagadbm commented 6 years ago

technically these aren't really solutions but more work-arounds. I think the real problem is because the framework decides to copy the element to the bottom of the body and hides the original one instead of actually changing the element. I used react-beautiful-dnd and they don't have these problems (though I have NO IDEA how in hell they get the grabbing cursor (i don't see it anywhere in the dom lol) but it works!

dagadbm commented 6 years ago

By the way this behavior is completely different on safari. (Just try to do a select all on safari). Safari for some reason selects the background as well as the text this is why there is some weird behavior when using this in safari (even when you use user-select: none)

juliankigwana commented 6 years ago

Adding the user-select:none style to the SortableItem stops the user from being able to interact with the item. For example, they may wish to copy the contents. So instead I use the SortableContainers onSortStart and onSortEnd to set some state which is then passed into the SortableItem to set when the class is added.

handleOnSortEnd () {
        this.setState({ isSorting: false });
}

handleOnSortStart () {
        this.setState({ isSorting: true });
}
const SortableItem = SortableElement(({value}) =>
  <li className={ classnames({ "user-select-none": this.state.isSorting }) }>{value}</li>
);
campmedia commented 5 years ago

I didn't have any luck with any of the above solutions so I came up with this. @wahabshah was where i started, but I then reversed the thinking. Apply user-select:none to everything but the items that can select. by using the asterisk * and :not() pseudo

*NOTE this will remove text selection form the entire page, for my application this is perfect.

*:not(.noselect){
 -webkit-touch-callout: none; /* iOS Safari */
   -webkit-user-select: none; /* Safari */
    -khtml-user-select: none; /* Konqueror HTML */
      -moz-user-select: none; /* Firefox */
       -ms-user-select: none; /* Internet Explorer/Edge */
           user-select: none; /* Non-prefixed version, currently
                                 supported by Chrome and Opera */
}
oziniak commented 5 years ago

Almost two years passed, and the issue is still there.

B3Kay commented 5 years ago

I also have this problem. Would be nice with a proper fix for this.

denis-lukin commented 4 years ago

If you want to use pressDelay or distance, you could try something like this:

updateBeforeSortStart={() => {
    function disableselect() {return false}
    document.onselectstart = disableselect;
    document.onmousedown = disableselect;
}}
onSortEnd={() => {
    document.onselectstart = null;
    document.onmousedown = null;
}}
abime commented 3 years ago

If you want to use pressDelay and distance and solution by @denis-lukin did not work for you. You can try something like this:

updateBeforeSortStart={() => {
    document.body.classList.add('not-selectable');
}}
onSortEnd={() => {
    document.body.classList.remove('not-selectable')
}}