实现Dropdown下拉菜单监听键盘上下键选中功能-React

用过ant design的小伙伴都知道,select组件是支持联想搜索跟上下键选中的效果的,但是在项目中我们可能会遇到用select组件无法实现我们的需求的情况,比如说一个div框,里面有input,又有tag标签,在input中输入内容触发联想,然后选中其中某一个,以tag标签的形式回填到div框中(类似这种需求)。这个时候,我们可以采用ant design的dropdown组件来帮助我们实现。

getBoundingClientRect()方法可以获取元素的大小及其相对视口的位置,这样可以帮助对元素是否在可视区域内进行判断

getBoundingClientRect() 是一个在 DOM(文档对象模型)中常用的方法,它返回一个 DOMRect 对象,
该对象提供了元素的大小及其相对于视口的位置。这个方法非常有用,
特别是在需要知道元素在页面上的确切位置或尺寸时。

DOMRect 对象包含以下属性:

x:元素左上角相对于视口(viewport)的 x 坐标(包括任何滚动偏移)。
y:元素左上角相对于视口(viewport)的 y 坐标(包括任何滚动偏移)。
width:元素的宽度(包括内边距 padding,但不包括边框 border、外边距 margin 和滚动条)。
height:元素的高度(包括内边距 padding,但不包括边框 border、外边距 margin 和滚动条)。
top:元素顶部边缘相对于视口(viewport)的 y 坐标(包括任何滚动偏移)。
right:元素右边缘相对于视口(viewport)的 x 坐标(包括任何滚动偏移)。
bottom:元素底部边缘相对于视口(viewport)的 y 坐标(包括任何滚动偏移)。
left:元素左边边缘相对于视口(viewport)的 x 坐标(包括任何滚动偏移)。

页面布局代码 ,需要根据实际情况来进行调整

componentDidMount(){
    // 添加键盘事件监听  
    document.addEventListener('keydown', this.handleKeyDown);
}

// 渲染下拉项
showGroupContcatMenu = ()=>{
    const {contactSearchList, currentFocusMenuIndex} = this.state;
    return <Menu>
      {
        contactSearchList.map((item, index)=>(
          <Menu.Item id={"dropdown-menu-item-" + index} key={item.id} onClick={()=>this.handleSelectMenuItem(item)} style={{background: index == currentFocusMenuIndex ? '#fff5e6' : ''}}>
            <div>{item.name}&nbsp;&nbsp;{item.enterpriseName}&nbsp;&nbsp;<span>{item.email && `<${item.email}>`}</span></div>
          </Menu.Item>
        ))
      }
      <Menu.Item id={"dropdown-menu-item-" + contactSearchList.length} key={contactSearchList.length} style={{background: currentFocusMenuIndex == contactSearchList.length ? '#fff5e6' : ''}} onClick={()=>this.openGroupContcatIcon(null)}>搜索联系人</Menu.Item>
    </Menu>
  }


// 布局代码
<div className="tag">
  {
    selectContact.length > 0 && selectContact.map(item=>(
      <Popover key={item.id} trigger="click" 
         content={
           <Spin spinning={showPhoneLoading}>
             <span style={{userSelect:'none'}}>{showPhoneLoading?'':trueEmail}</span>
           </Spin>
         } 
         overlayClassName='contact-email-tip'
         onVisibleChange={(visible) => this.onPopoverVisibleChange(visible, item)}>
         <Tag key={item.id} closable={true} onClose={(e)=>this.handleClose(item.id,item,e,'searchGroupContcat','contact')}>{item.name}{item.hiddenEmail || item.email || item.disPlayEmail?<span>&nbsp;&nbsp;</span>:''}{item.hiddenEmail || item.email || item.disPlayEmail}</Tag>
      </Popover>
    ))
  }
  <Dropdown 
     visible={onKeywordsContact ? true : false}
     overlay={()=>this.showGroupContcatMenu()} 
     overlayClassName='bropdown-overlay-class'
     destroyPopupOnHide={true} 
  >
     <div>
        {getFieldDecorator('onKeywordsContact', {
           initialValue: '',
        })(
           <AInput 
             className="input" 
             onBlur={(e)=>this.handleBlur('onKeywordsContact')}
             onPressEnter={this.handleInputPressEnter}
             onChange={this.handleGroupContcatChange}/>
        )}
     </div>
  </Dropdown>
</div>

监听键盘响应事件的核心代码

handleKeyDown = (event) =>{
    const {onKeywordsContact, currentFocusMenuIndex, contactSearchList} = this.state
    let newCurrentFocusMenuIndex = currentFocusMenuIndex;
    if (event.key === 'ArrowUp' && onKeywordsContact) {  
      event.preventDefault(); 
      newCurrentFocusMenuIndex = currentFocusMenuIndex <= 0 ? contactSearchList.length : currentFocusMenuIndex - 1;
      this.scrollIntoViewIfNeeded(newCurrentFocusMenuIndex, contactSearchList.length);
    } else if (event.key === 'ArrowDown' && onKeywordsContact) {  
      event.preventDefault();  
      newCurrentFocusMenuIndex = currentFocusMenuIndex >= contactSearchList.length ? 0 : currentFocusMenuIndex + 1;
      this.scrollIntoViewIfNeeded(newCurrentFocusMenuIndex, contactSearchList.length);
    }  
    this.setState({
      currentFocusMenuIndex: newCurrentFocusMenuIndex // 记录当前选中高亮的元素
    }) 
  }


scrollIntoViewIfNeeded = (newCurrentFocusMenuIndex, maxLength)=>{
      const ulEle = $('.bropdown-overlay-class .ant-dropdown-menu')[0];
      const liEle = $('#dropdown-menu-item-' + newCurrentFocusMenuIndex)[0];
      const ulRect = ulEle.getBoundingClientRect();  
      const liRect = liEle.getBoundingClientRect();
      
      if(newCurrentFocusMenuIndex == 0){
        ulEle.scrollTop = 0;
      } else if(newCurrentFocusMenuIndex == maxLength){
        ulEle.scrollTop = ulEle.scrollHeight;
      } else {
        // 检查li是否在ul的上方  
        if (liRect.top < ulRect.top) {  
          // 滚动ul到li的顶部位置  
          ulEle.scrollTop = liEle.offsetTop;  
        }  
        // 检查li是否在ul的下方(这里假设我们不想滚动超过li的底部)  
        else if (liRect.bottom > ulRect.bottom) {  
          // 滚动ul到li的底部位置减去ul的高度,以确保li的底部在可视区域内  
          ulEle.scrollTop = liEle.offsetTop + liEle.offsetHeight - ulEle.offsetHeight;   
        }  
      }
    }

  • 10
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
element-plus 中的 el-dropdown 实现多级下拉菜单的方法如下: 1. 首先需要嵌套多个 el-dropdown-menu 组件,每个 el-dropdown-menu 代表一个下拉菜单的级别,同时需要为每个 el-dropdown-menu 设置唯一的 ref 标识。 2. 接着需要在每个 el-dropdown-menu 中定义需要展示的菜单项,使用 el-dropdown-item 组件实现。 3. 在每个 el-dropdown-menu 上添加 @command 事件监听,当菜单项被点击时,触发该事件,并将所选中的菜单项作为参数传递给事件处理函数。 4. 在事件处理函数中,判断当前点击的菜单项是否有子菜单需要展示,如果有,则通过 ref 获取对应的子菜单 el-dropdown-menu 组件,并使用其 show 方法显示该子菜单。 以下是示例代码: ``` <template> <el-dropdown> <span class="el-dropdown-link">多级下拉菜单</span> <el-dropdown-menu ref="menu1"> <el-dropdown-item @command="handleCommand">选项1</el-dropdown-item> <el-dropdown-item @command="handleCommand">选项2</el-dropdown-item> <el-dropdown-item @command="handleCommand">选项3</el-dropdown-item> <el-dropdown-item @command="handleCommand">选项4</el-dropdown-item> <el-dropdown-item @command="handleCommand">选项5</el-dropdown-item> </el-dropdown-menu> </el-dropdown> </template> <script> export default { methods: { handleCommand(command) { if (command === '选项1') { const menu2 = this.$refs.menu2; menu2.show(); } else if (command === '选项2') { // do something } else if (command === '选项3') { // do something } else if (command === '选项4') { // do something } else if (command === '选项5') { // do something } } } } </script> ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小灰灰学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值