I was dealing with some problem these days ago. The problem is related with event bubbling with react.
I have implemented a drop-down with some react components. This is made with a div containing a ul and some li elements. I need to make the drop down accessible by keyboard so I fire the onblur, onfocus, onkeydown and onclick elements to show and hide the drop down and to use it with keyboard.
I fire a function passed by props to real with the show/hide stuff and when the div is focused or clicked I show the drop down and when is onblur I hide it changing the state of the component. The problem is that I have some li elements with onclick functions to select the desired option. However, when I click on an option, onblur event of parent fires, it changes the state and onclick event of the li element doesn't fire so I cannot choose any option.
I'm trying to solve this using event bubbling or propagation but I couldn't find any solution. Could you please help me?
Thanks a lot!
EDIT: Code of the problem:
const Filter = (props: FilterProps) => {
...
<div onBlur={(e) =>
{props.handleDropdown(e, props.isOpen)}} onKeyDown={(e) => {props.handleKeyDown(e)}} onFocus={(e) => props.handleDropdown(e, props.isOpen)} className={props.isOpen ? "Dropdown Dropdown--multiselection is-open" : "Dropdown Dropdown--multiselection"}>
<Button className="FilterField Dropdown__trigger Button--secondary" onClick={(e) => props.handleDropdown(e, props.isOpen)}>
<span className="Dropdown__label">{setLabels(ASSETS, props.selectedAssets)}</span>
<span className="Dropdown__caret"></span>
</Button>
<ul className="Dropdown__menu">
<li className={checkSelectedAsset(-1, props.selectedAssets).class} onClick={(e) => props.selectAsset(e, -1)}>
<Translate id="all"/>
{checkSelectedAsset(-1, props.selectedAssets).isSelected &&
<span className="Dropdown__menu-item-icon">
<IconCheck/>
</span>
}
</li>
<li className="Dropdown__menu-divider"></li>
{
(props.assetClasses && props.assetClasses.length > 0) &&
props.assetClasses.map((asset) => {
return (
<li className={checkSelectedAsset(asset, props.selectedAssets).class} onClick={(e) => props.selectAsset(e, asset)}>
{
<span>
<Translate id={`products.${Helper.getType(asset)}`}/>
</span>
}{checkSelectedAsset(asset, props.selectedAssets).isSelected &&
<span className="Dropdown__menu-item-icon">
<IconCheck/>
</span>
}
</li>
);
})
}
</ul>
</div>
interface PositionsContainerState {
...
isOpen: boolean;
}
class Container extends
React.Component<ContainerProps, ContainerState> {
openCloseDropdown = (event, isOpen: boolean) => {
event.stopPropagation();
if (event.type === "focus") {
this.setState({
dropdownExpanded: true,
focusTriggered: true
});
}
else if (event.type === "blur") {
this.setState({
dropdownExpanded: false,
focusTriggered: false
});
}
else if (event.type === "click") {
if (this.state.focusTriggered) {
this.setState({
dropdownExpanded: isOpen,
focusTriggered: false
});
}
else {
this.setState({
dropdownExpanded: !isOpen,
});
}
}
};
selectAsset = (event, asset: number) => {
//event.detail.keyboardEvent.preventDefault();
if (asset < 0) {
this.props.dispatch(setFilterAssets([]));
}
else {
let auxSelectedAssets = assign([], this.props.selectedAssets);
if (auxSelectedAssets.indexOf(asset) === -1)
auxSelectedAssets.push(asset);
else
auxSelectedAssets.splice(auxSelectedAssets.indexOf(asset), 1);
this.props.dispatch(setFilterAssets(auxSelectedAssets));
}
}
render() {
return (
<Filter
handleDropdown={props.openCloseDropdown}
isOpen={props.isOpen}
selectAsset={props.selectAsset}
/>
)
};