10

Consider the following example as displayed on Windows in the Chrome browser.

<input type="range">

Select the range slider using one of the following methods:

  • Method #1: Click inside the code snippet to activate the iframe. Press CTRLA to select the entire iframe content. Try moving the slider with the mouse.
  • Method #2: Above the slider, hold down the left mouse button and select the page content down to the bottom of the slider. Release the mouse button. Try moving the slider with the mouse.
  • Method #3: Click inside the code snippet to activate the iframe. Triple-click on the empty area next to the range slider. Try moving the slider with the mouse.

So, with this methods I can make the range slider stop functioning as expected. It displays a "circle with a line through it" cursor and refuses to allow me to slide the handle to the right or left.

My theory here is that the first actions I take "select" or "highlight" the range selector, as one would select a section of text in the browser, and then my subsequent attempts to operate the range slider are interpreted as me wanting to drag the selection.

Is there any work-around or way to avoid this bug?

So far attempts such as setting CSS user-select: none; on the input[type=range] element do not work:

input[type="range"] {
  user-select: none;
}
<input type="range">

neither does calling e.preventDefault() on the drag event:

document.querySelector('input[type="range"]').addEventListener('drag', (e) => {
  e.preventDefault();
});
<input type="range">

See effect in GIF:

Recording of effect

2
  • I slightly modified the question and added three methods - which I consider equivalent. I've managed to achieve some very interesting reproductions. For example, for method #3, it's enough to set user-select: none on the pseudo-elements as well, but interestingly, the bug can still be reproduced with method #1 and method #2. It's a pity the question hasn't received much attention over the years; it's not a common case, but it's certainly interesting. I believe we should primarily look for CSS-based solutions to avoid unnecessary JS execution. Commented Sep 14 at 20:48
  • The most promising JS-free solution so far is wrapping the element in a parent. I like it, but it requires unnecessary extra DOM manipulation, which isn't always feasible for an embedded element. Commented Sep 14 at 20:49

5 Answers 5

7

The accepted solution doesn't work for me (chrome). This version does:

const stop = function(e) {
  e.preventDefault();
  e.stopImmediatePropagation();
};

document.querySelectorAll('input[type="range"]').forEach((input) => { 
  input.draggable = true;
  input.addEventListener('dragstart', stop);
});
<input type="range">

Sign up to request clarification or add additional context in comments.

1 Comment

Although it seems effective, I think a CSS-only alternative would be better.
3

After a little tinkering I was able to stop this behavior by emtpying the user selection on mousedown. Emptying it on dragstart is too late, in that case the slider would work only from the second attempt onwards. mousedown is better.

document.querySelectorAll('input[type="range"]').forEach((input) => { 
  input.addEventListener('mousedown', () => window.getSelection().removeAllRanges());
});
Please select text from here
<input type="range">
until here and then use the slider.

3 Comments

this did not work for me; pmad's solution did however
Although it seems effective, I think a CSS-only alternative would be better.
Variant: Empty the selection only if the slider is inside it (check that in a way that's robust to the slider or its ancestor having user-select: none), so that the user doesn't lose the selection otherwise.
2

Setting user-select: none; for the <input> doesn't help, but you can set that rule for its parent.

Wrap the <input> in a dedicated <span> if it doesn't yet have a parent that shouldn't be selectable.

This solution works only if there's selectable text before or after the <input>, not on both sides.

:has(> input[type="range"]) {
  user-select: none;
}
<div>
  Triple click here, then drag (!) the slider
  <span>
    <input type="range">
  </span>
</div>

1 Comment

Undoubtedly, a CSS-only solution is far better than the other JS-based approaches. Nice hack providing the parent span.
1

From @pmad's answer, it's not clear why e.stopImmediatePropagation() and resetting draggable are necessary. Simply calling e.preventDefault() as described in the question works, but it doesn't need to be applied to the entire drag event. You only need to ignore text selection once at the start of the drag, during the dragstart event:

document.querySelector('input[type="range"]').addEventListener('dragstart', (e) => {
  e.preventDefault();
});
<input type="range">

3 Comments

Although it seems effective, I think a CSS-only alternative would be better.
This is a clean solution, and it works in more cases than my CSS-only answer (which I'm waiting for mods to undelete). But I'm scared that a bug in some browser version might cause preventDefault of dragstart to literally prevent the usual dragging of the slider even when it's not selected. We should rather report the problem itself to the Chromium issue tracker.
...To reduce that risk, we can preventDefault only if the slider is inside the selection. Check that in a way that's robust to the slider or its ancestor having user-select: none.
1

It seems that Chrome handles the <input> DOM element differently from other elements. Regardless of the type, the issue can be reproduced, but it's especially noticeable with range inputs, since a drag event there is expected to trigger an action.

p, input {
  display: block;
  margin-block: 1em;
  user-select: none;
}
<div>
  Click here, then press Ctrl+A
  <input type="text" value="test">
  <p>Hello World</p>
</div>

Chrome result after CTRLA Firefox result after CTRLA
Chrome result after CTRL+A selection Firefox result after CTRL+A selection

Chrome also highlights the contents of the <input>. In the case of a range this isn't visible, but it definitely happens in the background, which proves that the <input> element does not respect user-select: none; in any way. This is unexpected behavior - a bug.

Although the @root's solution is nice, as it wraps the input[type=range] element in a separate element and disables selection on it, it doesn't prevent direct text selection on <input> element. The approach is somewhat problematic because it would require wrapping every input[type=range] in a separate element, and it fails if this wrapping is omitted or not feasible.

Instead of wrapping in a separate parent span, you can simply apply user-select: none; directly to the input element and all its pseudo-elements, like this:

input[type="range"] {
  user-select: none;
}
<div>
  Triple click here, then drag (!) the slider
  <input type="range">
</div>

Note: This provides a solution for mouse selection, but not for the CTRLA combination. However, it doesn't require extra JS code or wrapping in an additional parent element.

Note: In my original example, I favored extending user-select: none; to pseudo-elements (-webkit-slider-thumb and -webkit-slider-runnable-track), but later I realized that this was merely a side effect of incorrect testing; in fact, when triple-clicking to select the sibling text, the <input> is already protected by the user-select: none; mentioned in the question.

Another interesting point is that although @root's solution of wrapping the <input> in an element solves the input[type=range] problem, for other inputs it remains obvious that the content of an <input> cannot be prevented from being selected using user-select: none;.

:has(> input[type="text"]) {
  user-select: none;
}
<div>
  Click here, then press Ctrl+A
  <span>
    <input type="text" value="test">
  </span>
</div>

I think if the content selection issue could be resolved, it would also fix the inconsistent behavior of range inputs. I believe this is something that should rather be reported as a bug in Chromium.

5 Comments

The fact that this works when triple clicking but not when clicking and pressing Ctrl+A (nor mouse drag selection when I tried) reveals new unexplored nuances. What's the fundamental difference between the effects of triple clicking and Ctrl+A? Why does Ctrl+A ignore user-select: none, is this a bug in Chrome?
Yeah, you're right, it doesn't work for dragging either. I also discovered the CTRL+A issue and have been experimenting with it since, but it seems that no CSS trick has any effect on it, at least for input[type="range"] elements specifically. Interesting.
To better understand the difference between the effects of triple clicking and Ctrl+A, we can compare the output of getSelection() after each of those two actions. I haven't tried yet.
@root I've revisited my train of thought a bit. The pseudo-elements were a good idea, but just a side effect of faulty testing: it only works in your triple-click example, but then I realized that even without pseudo-elements, the triple-click case is resolved. Instead, I incorporated the general concept of the <input> DOM element into my answer, since I noticed that Chrome handles selection of input elements differently and even selects their content, making an input[type=text] example much more illustrative.
If we could figure out how to prevent the content from being selected in input[type=text], that would also solve the original input[type=range] problem. So far, your solution seems to be the only one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.