-
Notifications
You must be signed in to change notification settings - Fork 47.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
No blur event fired when button is disabled/removed #9142
Comments
Hmm, I don't want to mutate the DOM. Disabled is a field in my state that I want to use when rendering |
Just use |
Calling @OliverJAsh I tried your demo and I see both Using Chrome 56. Is there a specific browser you were having this issue with? Also, it sounds like you are using |
@aweary Please see the gif demo below. In the demo, you can see my vanilla event listener is logged, but the React |
@OliverJAsh can you verify what browser you're using? I can't reproduce in Chrome 56, but I can reproduce it in Chrome 57. It looks to be browser-specific. Button focus behavior is not well defined and inconsistent between browsers. For example, using this example with plain JavaScript neither Firefox nor Safari on OSX fire It's also worth noting that you are listening for the |
I'm using Chrome 59 (dev channel).
Interesting, I didn't realise this.
The blur event does not bubble up to the div, but can be listened to using event capturing (on the way down as opposed to on the way up). This is why I would expect React—when using Chrome—to follow the behaviour of Chrome's event model. |
@OliverJAsh It's a tough problem, the reason Chrome's Since Chrome's |
It seems to me that this is a job for If the focusNode operation fails, we should dispatch the blur event ourselves since we know that a blur must have happened as a result of reconciliation in this case. |
I think it is fine to special case this because this is already a special case for focus management and we don't have any other plan for it atm. Other than possibly introducing "pivot points" in the reconciler that avoids touching active trees so that we don't need to do manual focus/selection management. However, even that requires us to special case focus as a pivot point. So it seems pretty inherent that we need something like this special case regardless. |
@sebmarkbage I'm happy to work on adapting
The |
The issue with the native event object is that they don't always have a representative native event. It can basically be any event type. E.g. you can get a I think it's probably fine just to let the native event be |
I'm not entirely sure if this would help, but I too wasn't able to get the <button
ref='myButton'
onClick={() => {this.refs['myButton'].focus()}}
onBlur={() => {console.log('blured')}}>
Click
</button> but this won't <button
ref='myButton'
onBlur={() => {console.log('blured')}}>
Click
</button> |
I encouter the same bug when using a textfield that has focus and gets disabled on the next render. A native blur event fires, but the react attached event handler doesn't (using Chrome 64). Below is a minimal example that shows the bug. It renders a textinput and a button. If you focus the textfield, and then submit by pressing enter, only a native blur event is logged. The react blur event does not fire.
|
I encountered this issue again with an element where the tabIndex is removed in the next render. This native implementation works <div id="button" tabIndex="0">Focus me, then click me</div>
<script type="text/javascript">
let button = document.querySelector('#button')
button.onclick = function () {
button.removeAttribute('tabIndex')
}
button.onblur = function () {
alert('onBlur handler called')
}
</script> while this React component doesn't fire the blur handler class extends React.PureComponent {
state = {
tabIndex: 0,
};
onBlur = () => {
alert("onBlur handler called");
};
onClick = () => {
this.setState({ tabIndex: null });
};
render() {
return (
<div tabIndex={this.state.tabIndex} onClick={this.onClick} onBlur={this.onBlur}>
Focus me, then click me
</div>
);
}
} |
I can reproduce the same problem with an |
@oliviertassinari what's the version of React in your case? I checked your sandbox example and blur event has been fired |
I'm encountering this issue in Chrome 81 + React 16.8.6 -- a focusable Edit: looks like the native |
I'm encountering the same problem as @subhero24 but on React 16.3.1 and Chrome 83. (That is, when my text input is focused and then disabled, it doesn't receive a blur event). |
@Bosch-Eli-Black FYI the native event handlers work. It's an unfortunate workaround, but you can use a callback ref to add a handler that works:
No need to remove the event listener in modern browsers, it'll get removed when the node is removed. |
Some notes on browser inconsistencies: Firefox has an additional bug where the button stays focused even though it becomes disabled. If React want's to normalize blur behavior across browsers it should also actually blur disabled elements. Firefox not firing blur when an element is removed (and actually blurred) is tracked in https://bugzilla.mozilla.org/show_bug.cgi?id=559561 Generally React could normalize blur events for elements losing focus because that would actually follow some spec:
-- https://www.w3.org/TR/uievents/#event-type-blur It's a bit more problematic if user agents don't move focus. In the end it's up to user agents to decide what elements are focusable and which aren't. PS:
You still add new event listeners on every re-render. The ref handler is new on every re-render so React executes it on every render. |
Is that true? The function identities are stable (no inline ref handlers) and the browser de-dupes identical listener adds, so I think it should be fine. |
Firefox 106 has fixed their bug so now both Chrome and Firefox will raise a native blur event on disable and React will ignore it. My workaround for this issue is to raise the blur event on the function that's being passed in as that's bringing the React component closer to what the browsers are doing: import { useRef } from "react";
import { UseFocusConfig } from "src/hooks";
/**
* We need to manually raise a blur event as React ignores browser events while
* reconciling: https://github.com/facebook/react/issues/9142
*/
export function useBlurDisabledInput(
disabled: boolean | undefined,
input: HTMLInputElement | undefined,
isFocused: boolean,
focusHandlers: UseFocusConfig
) {
function handler(e: FocusEvent) {
let propagationStopped = false;
const currentTarget = e.currentTarget as EventTarget & Element;
const relatedTarget = e.relatedTarget as (EventTarget & Element) | null;
const target = e.target as EventTarget & Element;
const reactEvent: React.FocusEvent = {
...e,
currentTarget,
relatedTarget,
nativeEvent: e,
target,
isDefaultPrevented: () => e.defaultPrevented,
isPropagationStopped: () => propagationStopped,
stopPropagation() {
e.stopPropagation();
propagationStopped = true;
},
persist() {},
};
input?.removeEventListener("blur", handler);
focusHandlers?.onBlur?.(reactEvent);
}
const handlerRef = useRef(handler);
if (disabled && input && isFocused) {
input.removeEventListener("blur", handlerRef.current);
handlerRef.current = handler;
input.addEventListener("blur", handler);
}
} I can't use a Looking forward to deleting all this once React fixes this bug. |
Is there any update on this? React is still not firing onBlur when a Some observationsReact 18.0.0 After the button is disabled:
AlternativeIf React is sticking with the current implementation of its |
Do you want to request a feature or report a bug?
Bug
What is the current behavior?
When a focussed button becomes disabled, React does not dispatch a blur event.
If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://jsfiddle.net or similar (template: https://jsfiddle.net/reactjs/69z2wepo/).
What is the expected behavior?
A blur event will be dispatched.
Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?
15.1.0, not sure if it worked in previous versions.
Isolated test case: http://jsbin.com/fuvite/1/edit?html,css,js,output
The text was updated successfully, but these errors were encountered: