Touchable presses not firing in absolute positioned container with zIndex (for a dropdown)
You have to set position for Select
instead of Option
here is a snack: https://snack.expo.io/HkDHHo6YV
change wrap
and menu
style with given below:
wrap: {
position: 'absolute',
zIndex: 10,
width: "100%",
backgroundColor: 'rgb(73,75,77)',
},
menu: {
backgroundColor: 'rgb(73,75,77)',
maxHeight: 300,
},
Edit, Working solution
The missing piece was to account for the space under the dropdown since it was completely absolutely positioned. I had to change the Select
component to render a "padding" element to account for the space.
class Select extends React.PureComponent {
render() {
const { value, open, options, onOptionPress } = this.props;
const shadowStyle = shadowStyles[Platform.OS];
return (
<>
<View style={{height: 60}}></View>
<View style={[shadowStyle, styles.wrap]}>
<TouchableWithoutFeedback onPress={this.props.onPress}>
<View style={styles.selectContainer}>
<Text>{value}</Text>
</View>
</TouchableWithoutFeedback>
{open && (
<View style={styles.menu}>
{options.map((item, idx) => (
<Option value={item} key={idx} onPress={onOptionPress} />
))}
</View>
)}
</View>
</>
);
}
}
Then adjusted the height of the styles.selectContainer
to be the same height 60
selectContainer: {
padding: 16,
height: 60
},
Full working example
import * as React from 'react';
import {
Text,
Button,
View,
StyleSheet,
Platform,
TouchableWithoutFeedback,
TouchableOpacity,
} from 'react-native';
class Dropdown extends React.Component {
constructor() {
super();
this.state = {
open: false,
selected: undefined,
};
}
handleSelectPress = () => {
this.setState({ open: !this.state.open });
};
handleOptionPress = item => {
this.setState({ selected: item, open: !this.state.open });
this.props.onSelectOption(item);
};
render() {
const { selected = {}, open } = this.state;
const { placeholder = '', options = [] } = this.props;
return (
<Select
onPress={this.handleSelectPress}
value={selected.name || placeholder}
open={open}
options={options}
onOptionPress={this.handleOptionPress}
/>
);
}
}
const shadowStyles = {
ios: {
shadowOpacity: 0.3,
shadowRadius: 3,
shadowOffset: {
height: 1,
width: 1,
},
},
android: {
elevation: 5,
},
};
class Select extends React.PureComponent {
render() {
const { value, open, options, onOptionPress } = this.props;
const shadowStyle = shadowStyles[Platform.OS];
return (
<>
<View style={{height: 60}}></View>
<View style={[shadowStyle, styles.wrap]}>
<TouchableWithoutFeedback onPress={this.props.onPress}>
<View style={styles.selectContainer}>
<Text>{value}</Text>
</View>
</TouchableWithoutFeedback>
{open && (
<View style={styles.menu}>
{options.map((item, idx) => (
<Option value={item} key={idx} onPress={onOptionPress} />
))}
</View>
)}
</View>
</>
);
}
}
class Option extends React.PureComponent {
handlePress = () => {
this.props.onPress(this.props.value);
};
render() {
const { value } = this.props;
return (
<TouchableOpacity onPress={this.handlePress}>
<View style={styles.optionContainer}>
<Text>{value.name}</Text>
</View>
</TouchableOpacity>
);
}
}
export default class App extends React.Component {
render() {
return (
<View style={styles.root}>
<Dropdown
placeholder="--- SELECT ---"
options={[
{ name: 'Press me!', id: '1' },
{ name: 'No Me!', id: '2' },
{ name: 'Cmon guys, here!', id: '3' },
]}
onSelectOption={item => console.log(`item pressed! ${item}`)}
/>
<Text>hellof</Text>
<View style={{backgroundColor: "red", height: 250}} />
</View>
);
}
}
const styles = StyleSheet.create({
root: {
width: 360,
height: 640,
backgroundColor: '#292c2e',
marginTop: 100
},
wrap: {
position: 'absolute',
zIndex: 10,
width: "100%",
backgroundColor: 'rgb(73,75,77)',
},
selectContainer: {
padding: 16,
height: 60
},
menu: {
backgroundColor: 'rgb(73,75,77)',
maxHeight: 300,
},
optionContainer: {
padding: 16,
height: 60
},
});
There is a problem with your styles in styles.menu
not with zIndex.
You have given position:'absolute'
and top:'100%'
.
This means your child view is positioned as absolute to the parent view and your child view will start right after your parent view is completing because of top:"100%"
.
Conclusion: your options are rendering out of the parent. yes, they are visible because there are no other components below and more of that zIndex is higher. and you can not touch anything that is not withing the range of parent.
Solution:
See working snack here
in styles.menu, change these,
menu: {
backgroundColor: 'rgb(73,75,77)',
zIndex: 1005,
top: 0,
left: 0,
right: 0,
maxHeight: 300,
},
and you can remove zIndex: 1000 and 1005 also :)