React: `onChange` event doesn't trigger when direclty clicking on the maximum `input[type=range]` value

Created on 29 May 2018  路  15Comments  路  Source: facebook/react

Do you want to request a feature or report a bug?
bug

What is the current behavior?
When you directly after the loading choose the maximum value on the input type range, the event isn't fire with an onChange. The event is fire when it's not the maximum value of the input.

Sandbox: https://codesandbox.io/s/7kv59yj360
Click on the maximum (right) of the input of type range.
There is no message on the console and the displayed value doesn't change as expected
Click on a other value on the input, a message is displayed in the console and the value change
When you click on the last value it's now displayed.

What is the expected behavior?
An event should trigger when you directly click on the maximum value of the input of type range.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
Testing on macOS High Sierra, Firefox 60.0.1, Safari 11.1, Chrome 66.0.3359.181, react 16.2.0

To get around this behaviour, I had to change onChange by onInput but add a blanck onChange to still get the drag behaviour on Safari on Iphone (see this CodeSandBox )

DOM Bug

Most helpful comment

Sorry, I should have explained what's happening here when I marked it as a bug.

The reason this is happening is because range inputs will have a default value.

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range

The default value is halfway between the specified minimum and maximum鈥攗nless the maximum is actually less than the minimum, in which case the default is set to the value of the min attribute.

When we track the input's value we read that value property, initializing the tracked value to to the default. In this case, it defaults to 10 because of the order in which the properties are assigned.

  1. Input element is created, value defaults to 50
  2. min attribute is set, value stays at 50
  3. max attribute is set, value is changed to 10 since 50 is greater than the new max

When we track the input's value we define a getter/setter for the value attribute, which _should_ correct the tracked value the next time it's updated. But since the current value _isn't_ empty, we skip setting value and only set defaultValue, which doesn't update the tracked value. So when you go to update the input by setting it to the max, it doesn't actually update because it thinks the tracked value is _already_ the max.

A couple solutions would be:

  1. Add a special case to postMountWrapper that checks if the element is a range input.
  2. Update inputValueTracking so it updates the tracked value when defaultValue is set as well

The first option is likely the easiest solution. I'm not sure what effects updating inputValueTracking might have, we could be relying on the tracked value and the defaultValue to be different in some cases.

All 15 comments

The workaround of using onInput instead of onChange works well but React thinks that the field is uncontrolled and shows a warning.

Without React, there is no issue with the onChange listener on a range input.
https://jsfiddle.net/L0L07119/

@brainlulz I found one interesting thing, the range work after I remove your max attribute, see

https://codesandbox.io/s/318jw3jjkq

It may be related to how react set the attributes when rendering the dom

It looks like this only happens when max is equal or less than 50.

@whs-dot-hk That's why by removing the max attribute, the range works because max defaults to 100

The value of range is not a integer as everyone expected, it is a string

https://codesandbox.io/s/ykkon3qp5j (Edited)

same here

https://jsfiddle.net/zf8x0s6k/

It doesn't change anything to the issue described here.

https://codesandbox.io/s/r883k20pn

When you use html native properties it is string anyway

<input type="range" max="10" />

Compare

https://codesandbox.io/s/81m5w3q4ql

with

https://codesandbox.io/s/llx9714o7m

both of them fire the event input when click on the max value, the max=60 one fire input then change event, but max=10

Is changing the code binding the default of onInput to onChange possible here? Is there any drawbacks?

This sounds like something that should be easy to fix. Anyone wants to look into why this is happening?

@gaearon I'm currently working on it

Sorry, I should have explained what's happening here when I marked it as a bug.

The reason this is happening is because range inputs will have a default value.

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range

The default value is halfway between the specified minimum and maximum鈥攗nless the maximum is actually less than the minimum, in which case the default is set to the value of the min attribute.

When we track the input's value we read that value property, initializing the tracked value to to the default. In this case, it defaults to 10 because of the order in which the properties are assigned.

  1. Input element is created, value defaults to 50
  2. min attribute is set, value stays at 50
  3. max attribute is set, value is changed to 10 since 50 is greater than the new max

When we track the input's value we define a getter/setter for the value attribute, which _should_ correct the tracked value the next time it's updated. But since the current value _isn't_ empty, we skip setting value and only set defaultValue, which doesn't update the tracked value. So when you go to update the input by setting it to the max, it doesn't actually update because it thinks the tracked value is _already_ the max.

A couple solutions would be:

  1. Add a special case to postMountWrapper that checks if the element is a range input.
  2. Update inputValueTracking so it updates the tracked value when defaultValue is set as well

The first option is likely the easiest solution. I'm not sure what effects updating inputValueTracking might have, we could be relying on the tracked value and the defaultValue to be different in some cases.

@Illu I have a fix locally I can submit, but if you want to give it a try feel free to and I'll defer to you 馃檪

@aweary thanks man ! I'll give it a try 馃槂

@aweary May I present you some test case

I have wrote mine, but this is already there

It said the order

https://github.com/facebook/react/blob/e0a03c1b4d84453122c4e9d1a5e0cec52bef9066/packages/react-dom/src/__tests__/ReactDOMInput-test.js#L1296

Some test case I wrote

https://github.com/whs-dot-hk/react/commit/5dc4073b4e9b4440754ee3234ea7dfc0a4762911

It's the value always correct? Or is it 50?

Fixed in React 16.4.1.

oninput working perfect instant of onchange

Was this page helpful?
0 / 5 - 0 ratings