Open ericflo opened 10 years ago
This seems to discuss it a bit more: http://stackoverflow.com/a/11710295
atm i am using this https://github.com/tbosch/autofill-event
cc me
(@visionscaper tip: press "Subscribe" on the right column.)
Any updates or suggested best practices on this one? The autofill event polyfill seems like a sledgehammer solution.
Safari (8) does dispatch change events on autofill, but they don't bubble so they don't reach the react handler.
A related discussion on https://github.com/angular/angular.js/issues/1460 was closed, with some suggestions, one of them being making use of https://github.com/tbosch/autofill-event to fire change event on autofill, manually.
I think, we can close here as well.
It would be nice to have the autofill-event as part of React, possible as an addon. This functionality is going to be necessary for virtually everyone using React for form validation. The autofill-event script also adds a dependency for jQuery, which may be undesirable in many cases.
This is a browser bug that effects all browsers a little differently. I think just because angular punted on trying to work around or fix it doesn't mean react should. It's a common thing but I understand if this is a "wontfix".
I'm not super well versed on the various browser's bug trackers, but it would be nice if someone who is could find or open issues for this. I think the react team has more weight then most users would when opening an issue about it. And we could tract the tickets here for the react developers who are interested.
I caught up with the angular thread. Firefox has issues with password fields (https://bugzilla.mozilla.org/show_bug.cgi?id=950510 is still open), no word on safari's bug #, chrome is fixed.
Issues are already being tracked at some places - https://github.com/angular/angular.js/issues/1460#issuecomment-53947546
The Chrome issue is now closed, but it pretty clearly does not work in Chrome 50 with React 0.14.8.
no fix yet?
I feel like this was working for a while and broke again recently?
any update on this issue?
Here an extract of my solution for this issue:
export default class Input extends Component {
static propTypes = {
value: PropTypes.string,
onFieldChange: PropTypes.func,
};
static defaultProps = {
value: '',
}
componentDidMount() {
this._listener = setInterval(() => {
if (!this.input || this._previousValue === this.input.value) {
return;
}
this._previousValue = this.input.value;
const evt = document.createEvent('HTMLEvents');
evt.initEvent('input', true, true);
this.input.dispatchEvent(evt);
}, 20);
}
componentWillUnmount() {
clearInterval(this._listener);
}
refInput = (input) => this.input = input;
render() {
const { label,
value,
onFieldChange,
} = this.props;
this.input = this.input || { value };
return (
<input
value={this.input.value}
onChange={onFieldChange}
ref={this.refInput}
/>
);
}
}
NOTE: value
comes from the state and onFieldChange
updates the state with the new value
The setInterval code is from the https://github.com/Pephers/react-autofill
Can anyone confirm that this issue was fixed with iOS 10.2? I can't reproduce it now...
I'm still having problems on iOS 10.2 with Chrome version 55.0.2883.79... Contrary to what is described above though, for me the autofilled content flashes up briefly in the form and is then removed again. So it's now consistent with what is stored in the state, however, autofill still doesn't work...
I'm reproducing the same issue encountered by @irisSchaffer. I have an isomorphic application, where I render a static page with react and express, then on the client I export the props and use the same page component.
Even when not handling the password input value through react, the client react component deletes the saved password from the input, even when chrome shows it briefly. When I disable javascript from the browser, the saved password stays in its place.
This issue is visible on Chrome only, using its last version, 58.
Not related to react, the saved password is not accessible through element.value until there's an event fired on the dom, like focus, any key event, etc. Tried simulate an event fire through javascript, with no luck on the fill of the value property.
+1. any fix or hacks?
I'm still facing this problem on iOS 10.3.3 Chrome 60.0.3112.89.
The auto-fill works, fills the field but doesn't change the state.
Decorator work-around: https://github.com/Pephers/react-autofill
Generic component work-around:
/**
Trigger onChange event after browser auto-fill.
@see https://github.com/facebook/react/issues/1159
@example <AutoFillWatch component={ref =>
<input required type="password" ref={ref} />
}/>
*/
class AutoFillWatch extends Component {
static propTypes = {
// Auto-fill component like: <input type="password"
component: PropTypes.func.isRequired,
// component pass-through reference
ref: PropTypes.func,
}
componentDidMount() {
this._listener = setInterval(() => {
if (!this.input || this._previousValue === this.input.value) {
return;
}
this._previousValue = this.input.value;
const evt = document.createEvent('HTMLEvents');
evt.initEvent('input', true, true);
this.input.dispatchEvent(evt);
}, 100);
}
componentWillUnmount() {
clearInterval(this._listener);
}
componentDidMount() {
const {ref} = this.props
if(ref) {
console.log('ref', this.input);
ref(this.input)
}
}
refInput = (input) => this.input = input;
render() {
const {component} = this.props
return component(this.refInput)
}
}
Chrome for iOS (make sure you test with autofill from Chrome, and not the suggestions from the iOS keyboard) only emits change
when autofilling values. Mobile Safari on the other hand emits focus
, keydown
, input
, keyup
, change
and blur
, as does Chrome and Safari on Mac.
React doesn't use change
event for regular <input />
which seems to be the root cause of this issue: https://github.com/facebook/react/blob/e932ad68bed656eed5295b61ba74e5d0857902ed/src/renderers/dom/shared/eventPlugins/ChangeEventPlugin.js#L66-L71
Wouldn't it be possible to have React onChange
also trigger on DOM change
events? Would there be any harm in that?
Related issue for Chrome iOS: https://bugs.chromium.org/p/chromium/issues/detail?id=705275
I've tested the autofilling and events with the following little page:
<html>
<head>
<script type="text/javascript">
window.onload = function () {
let elements = document.querySelectorAll('input');
let actions = document.getElementById('actions')
let events = 'focus blur keydown keyup change input'.split(' ');
elements.forEach(element => {
events.forEach(event => {
element.addEventListener(event, e => {
console.log(e);
let eTypeNode = document.createTextNode(element.name + ' > ' + e.type + ":" + e.code + ":" + e.keyIdentifier);
actions.appendChild(eTypeNode);
actions.appendChild(document.createElement('br'));
})
})
})
};
</script>
</head>
<body>
<form>
<input type="text" name="name" id="a" autocomplete="name" />
<input type="email" name="email" id="b" autocomplete="email" />
<input type="tel" name="tel" id="c" autocomplete="tel" />
</form>
<div id="actions"></div>
</body>
</html>
Change event is emitted in Chrome v62 when you wrap inputs by a <form />
element.
theres an onInput event handler that will grab the value of an input field if it changes.
Still seeing some unusual behavior here with autofill on iOS. The above react-autofill decorator seems to work well with simple autofill but not the autofill shortcuts provided by the keyboard.
This should be fixed in Chrome for iOS now: https://chromium.googlesource.com/chromium/src/+/55518e17850cac0bdc6fca7b24092bea479e34db. I'm not sure when this patch will be included in a released version though.
为什么一定要用 onChange? Why do we HAVE to use 'onChange' but not the 'change event'
class Input extends Component {
componentDidMount(){
this.input.addEventListener('change', (e)=>{
this.props.onChange(this.props.field, e.target.value)
})
}
render(){
const { className, name, label, autofill, field, onBlur, onChange, value, error, visited, required, type, maxLength } = this.props
return (
<div className={ [styles.inputWrap, className || ''].join(' ') } onBlur={e => onBlur(field)}>
<input ref={n => this.input = n} id={field} name={name || field} type={ type || 'text' } defaultValue={ value }
autoComplete={ autofill } maxLength={maxLength} />
<div className={ [styles.placeholder, value? styles.active:''].join(' ') }>
<FormattedMessage id={label} />
<i className={styles.required}>{required? '*':''}</i>
<span className={ styles.error }>{ visited? error:'' }</span>
</div>
</div>
)
}
}
Change event is emitted in Chrome v62 when you wrap inputs by a
<form />
element.
I confirm that this solution works for Chrome for autofilling phone numbers via autoComplete="tel"
https://github.com/catamphetamine/react-phone-number-input/issues/101
The solutions above using this._previousValue !== this.input.value
unfortunately only work for iOS Safari. iOS Chrome's .value
is still empty after an autofill.
I have no idea how people have been handling autofill in forms when using controlled components. Consider this login scenario:
.value
as emptyUnless there's a way to get Chrome to report the value I can't think of a solution other than using uncontrolled components, html5 input validation, and getting form values on submit like the old days.
You can track (and star) the issue over here: https://bugs.chromium.org/p/chromium/issues/detail?id=813175
I couldn't get the solution above to work either @DominicTobias. There's multiple layers of problems. The first was that this.input.value
always came back as blank. However, I discovered, that if you use document.getElementById
in each loop, then document.getElementById(id).value
-- you'll get a value back.
This doesn't solve the dispatchEvent
issue, but at least you can get the autofilled value and can do stuff with it (for example just calling a controlled input's onChange
handler).
@wlingke good to know (and how strange)! I'll have to go down that route too if they don't fix the bug in time for my release
@wlingke When setting up a demo for the Chrome guys I realised something - this isn't an issue with Chrome I should have read the comments above more carefully (in particular @oscar-b comment). onChange
is a synthetic event in React which doesn't actually use the change
event, which seems pretty weird to me.
You can even see in the demo below that it behaves differently, more like it's listening for an input
event. When you use the real change
event then it works properly in iOS Chrome.
So I would suggest getting a ref to the element and just listening to the real change event, as well as the onChange
event which seems to be the input
event if you need to update state as the user is typing.
@DominicTobias I previously tried both change
and input
events separately as well as both at once in that decorator but wasn't able to get it to work in my app.
Also I'm not sure if this changes things.. but there's a difference when autofilling ONE field versus autofilling MULTIPLE fields. The input
event worked fine when autofilling one field. However, in mobile chrome you can actually autofill a set of fields at once from the keyboard (like firstname, lastname, email all with one tap).
In that second scenario, the decorator allows the first field autofills properly (first field meaning the field that is currently in focus). However, the decorator (which is attached to all 3 fields), doesn't seem to work on the other fields.
@wlingke Interesting, I added some more fields but when autofilling all three at once in iOS Chrome I am correctly receiving the change events natively (but 0 for the React version below):
(Screenshot is from desktop browser but I'm getting the same result on my phone)
@DominicTobias I think if you use the input
event - which is what I previously used, it'll work fully in Desktop. And work for the first input in Mobile.
input
event is closest to react's onChange
. The change
event only files after an element loses focus from what I understand (https://stackoverflow.com/questions/17047497/what-is-the-difference-between-change-and-input-event-for-an-input-element)
Yep react has for some reason bound onChange to input
event: https://stackoverflow.com/questions/38256332/in-react-whats-the-difference-between-onchange-and-oninput
So to keep things simple I just also call it in my Input
component for actual change events (as well as the normal input ones). Now it's working in iOS Chrome. Try it out and let me know!
componentDidMount() {
this.inputRef.addEventListener('change', this.onChange);
}
onChange = (event) => {
// React actually uses the input even for onChange, this causes autofill to
// break in iOS Chrome as it only fires a change event.
if (this.props.onChange) {
this.props.onChange(event);
}
}
componentWillUnount() {
this.inputRef.removeEventListener('change', this.onChange);
}
Edit: Chrome is supposed to have fixed in v65 but people are reporting it still doesn't work :/
Thank you for the explanation Dominic. We did fix that behavior back in December. We now fire keydown, change, input, and keyup events. https://chromium-review.googlesource.com/c/chromium/src/+/771674
We also had a fix for React. https://chromium-review.googlesource.com/c/chromium/src/+/844324
while this does fix the autofill behavior, it still does not invoke the onChange handler.
I also raised an issue on this and the dev was sure it was going to be fixed in 65... https://bugs.chromium.org/p/chromium/issues/detail?id=813175
I'm also listening to the native change
event like @DominicTobias does and it works for me in iOS Chrome. I'm triggering redux-form's onChange
in the native event listener attached on mount.
I'm also using the input:-webkit-autofill
selector for styling.
See the result for yourself at the bottom of https://labrewlangerie.com (contact form).
Seems like this fix is finally on its way into Chrome iOS! Not sure in what version though, since this seems to have been merged on December 1.
https://bugs.chromium.org/p/chromium/issues/detail?id=705275#c11 https://chromium-review.googlesource.com/c/chromium/src/+/771674
https://chromium.googlesource.com/chromium/src/+/55518e17850cac0bdc6fca7b24092bea479e34db
it should be already in the newest chrome. I am still facing this issue (Angular 5).
Same here in Chrome 65, React 15.6.
That's a shame, Mahmadi said it was going in on March 13th on v65 :(
Comment 15 by mahmadi@chromium.org, Mar 9 The fix isn't in Chrome 64. My bad. Chrome 65 will have (to be rolled out starting March 13th). I just tested your native events demo website and it works on Chrome 65. I appreciate you following up on this.
https://bugs.chromium.org/p/chromium/issues/detail?id=813175
Try going here on your phone and checking: https://kind-stallman-f3c045.netlify.com/native.html
What's really strange is that in v65 it's working natively, but even though it's getting an input even it's not being received in React! Need further investigation I'm not exactly sure how react is attaching the handler (or if it's doing it on the document element like a click handler for example).
Natively I can see the input event happening now (https://kind-stallman-f3c045.netlify.com/native.html):
But somehow React isn't receiving any of those events (https://kind-stallman-f3c045.netlify.com):
add onBlur={this.handleChange} on input
KlarnaUI team has an interesting hack to deal with this issue exploiting the onAnimationStart
event and :-webkit-autofill
pseudo class as described in this article - https://medium.com/@brunn/detecting-autofilled-fields-in-javascript-aed598d25da7
We've used this hack in our applications it works fine (could test only on Chrome). I'll try to come up with a shim using this technique so that it is generic.
Any update on the issue?
up, what's the best trick for the moment ?
@ericflo @sophiebits
This issue still seems to persist, any updates?
When there's a controlled component for form names that the user has saved in their browser (common with username/password fields), the browser will sometimes render the page with values in those fields without firing onChange events. If the user submits the form, the component state does not reflect what is showing to the user.
In experimenting with this, it appears that the data is there on load (tested by logging this.refs.myinput.getDOMNode().value)