antd的Select,DatePicker,Cascader等组件选项框随页面滚动的问题解决
一、场景
在开发过程中,会使用很多的Select、DatePicker等组件,当这些组件在可滚动的区域内滚动时,你会发现该组件的选项框也会跟着滚动,产生分离。如下图所示。
二、解决方法
通过查询相应的官方API,发现官方给我们提供了getPopupContainer属性,该属性是菜单渲染的父节点,默认是body。只要添加该属性,设置好父节点,就可以解决这种分离。
添加getPopupContainer属性
getPopupContainer={(triggerNode: any) => triggerNode.parentNode}
在Select、DatePicker、Cascader添加getPopupContainer属性栗子:
<div style={{ margin: 10, overflow: 'scroll', height: 500 }}> <div style={{ padding: 100, height: 1000 }} id='getPopupContainerDiv'> <Select defaultValue="1" style={{ width: 120 }} getPopupContainer={(triggerNode: any) => triggerNode.parentNode} > <Option value="1">选项1</Option> <Option value="2">选项2</Option> </Select> <DatePicker getPopupContainer={(triggerNode: any) => triggerNode.parentNode} style={{ width: '160px' }} format="YYYY-MM-DD" /> <Cascader getPopupContainer={(triggerNode: any) => triggerNode.parentNode} options={options} /> </div> </div>
设置完成之后,发现Select是没有分离的,但是DatePicker和Cascader还是会分离。
这是为什么呢?首先看了一下版本,我的antd版本是4.x的,在3.x是正常的,这就让人掉头发了。
没办法,先硬着头皮打印一下这三个组件的triggerNode以及triggerNode.parentNode观察一下。
Select的triggerNode以及triggerNode.parentNode
DatePicker和Cascader的triggerNode以及triggerNode.parentNode
果真,三个组件的triggerNode.parentNode有差异,DatePicker和Cascader直接找到了最外层的div,他的节点还是在最外层,导致分离。
原因找到了,如何解决呢?
既然官方提供的是定位到父节点,就可以解决分离问题,那么我直接定位到本身节点,那岂不是也可以解决?将getPopupContainer代码修改一番,如下:
getPopupContainer={(triggerNode: any) => triggerNode}
结果:修改完之后运行,发现都不再分离。
三、全局设置getPopupContainer
一般来说,项目中会用到很多这种选择组件,我们如果一个一个的去添加getPopupContainer属性,那么久太麻烦了,这就需要我们全局配置。
<ConfigProvider
locale={zh_CN}
getPopupContainer={(node: any) => node} //ant 4.x
// getPopupContainer={(triggerNode: any) => triggerNode.parentNode} //ant 3.x
<Route />
</ConfigProvider >
这样配置完成之后,全局就配置完毕,不需要我们每个都去添加。
四、全局配置后,在Modal中使用报错解决
开发项目,肯定会使用Modal弹框组件,在Modal组件中也避免不了使用选择组件,在全局配置完getPopupContainer后,发现Modal会报错。
ant3.x版本报错,4.x版本直接空白
官方API给的解释是:全局设置getPopupContainer
触发节点时,Modal 的用法不存在 triggerNode导致报错
解决方法:增加一个判断条件
<ConfigProvider
locale={zh_CN}
//getPopupContainer={(node: any) => node}
getPopupContainer={(node: any) => {
if (node) {
return node //ant 4.x
// return node.parentNode; //ant 3.x
}
return document.body;
}}
>
<Route />
</ConfigProvider >
五、完整代码
import React from 'react'; import { Button, Select, DatePicker, Cascader, Modal, Form } from 'antd' const Option = Select.Option export default () => { const [form] = Form.useForm(); const options = [ { value: 'zhejiang', label: 'Zhejiang', children: [ { value: 'hangzhou', label: 'Hangzhou', children: [ { value: 'xihu', label: 'West Lake', }, ], }, ], }, { value: 'jiangsu', label: 'Jiangsu', children: [ { value: 'nanjing', label: 'Nanjing', children: [ { value: 'zhonghuamen', label: 'Zhong Hua Men', }, ], }, ], }, ]; const triggerNodeFunc = (triggerNode: any) => { console.log(triggerNode) console.log(triggerNode.parentNode) return triggerNode } return ( <div> <Modal title='组件选项框滚动分离' visible={true} bodyStyle={ { maxHeight: '300px', overflowY: 'auto' } } > <div style={{ padding: 100, height: 1000 }}> <Select defaultValue="1" style={{ width: 120 }} > <Option value="1">选项1</Option> <Option value="2">选项2</Option> </Select> <DatePicker style={{ width: '160px' }} format="YYYY-MM-DD" /> <Cascader options={options} /> </div> </Modal> {/* <div style={{ margin: 10, overflow: 'scroll', height: 500 }}> <div style={{ padding: 100, height: 1000 }} id='getPopupContainerDiv'> <Select defaultValue="1" style={{ width: 120 }} // getPopupContainer={(triggerNode: any) => triggerNodeFunc(triggerNode)} > <Option value="1">选项1</Option> <Option value="2">选项2</Option> </Select> <DatePicker // getPopupContainer={(triggerNode: any) => triggerNode} style={{ width: '160px' }} format="YYYY-MM-DD" /> <Cascader // getPopupContainer={(triggerNode: any) => triggerNodeFunc(triggerNode)} options={options} /> </div> </div> */} </div > ); }
ReactDOM.render( <Provider store={store}> <ConfigProvider locale={zh_CN} // getPopupContainer={(node: any) => node} // getPopupContainer={(triggerNode: any) => triggerNode.parentNode} getPopupContainer={(node: any) => { if (node) { return node // return node.parentNode; } return document.body; }} > <Route /> </ConfigProvider > </Provider>, document.getElementById('root') );