当前位置: 首页 > news >正文

实现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.statelet 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;   }  }}

相关文章:

  • vue3状态管理,pinia的使用
  • JavaScript、Kotlin、Flutter可以开发鸿蒙APP吗?
  • HTTP --tcp和keep-alive
  • oracle linux7安装oracle11g0204
  • 强化学习(一) 基本概念和赌博机问题
  • 【量算分析工具-获取高程】GeoServer改造Springboot番外系列八
  • 应用案例|精密制造中使用复合机器人得到显著提升
  • MySQL表的增删改查初阶(上篇)
  • Kubernetes集群Pod控制器
  • 解析Java中1000个常用类:ProcessHandle.Info类,你学会了吗?
  • 执行shell脚本时为什么要写成./test.sh,而不是test.sh?
  • 数据结构--关键路径
  • MYSQL之主从复制
  • JDBC 学习笔记(一)基础篇 - JDBC 搭建的六大步骤
  • 创建模拟器
  • 5、React组件事件详解
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • C++入门教程(10):for 语句
  • CSS3 聊天气泡框以及 inherit、currentColor 关键字
  • Fabric架构演变之路
  • GitUp, 你不可错过的秀外慧中的git工具
  • happypack两次报错的问题
  • JavaScript 奇技淫巧
  • JavaScript设计模式之工厂模式
  • Netty源码解析1-Buffer
  • python docx文档转html页面
  • python_bomb----数据类型总结
  • rabbitmq延迟消息示例
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 搞机器学习要哪些技能
  • 可能是历史上最全的CC0版权可以免费商用的图片网站
  • 如何正确配置 Ubuntu 14.04 服务器?
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 线上 python http server profile 实践
  • 《码出高效》学习笔记与书中错误记录
  • C# - 为值类型重定义相等性
  • 第二十章:异步和文件I/O.(二十三)
  • ​Java基础复习笔记 第16章:网络编程
  • ​ssh-keyscan命令--Linux命令应用大词典729个命令解读
  • ​卜东波研究员:高观点下的少儿计算思维
  • #define 用法
  • #define与typedef区别
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • #我与Java虚拟机的故事#连载16:打开Java世界大门的钥匙
  • (1)bark-ml
  • (2)STL算法之元素计数
  • (2)空速传感器
  • (2022版)一套教程搞定k8s安装到实战 | RBAC
  • (Matalb分类预测)GA-BP遗传算法优化BP神经网络的多维分类预测
  • (SpringBoot)第二章:Spring创建和使用
  • (第61天)多租户架构(CDB/PDB)
  • (附源码)springboot码头作业管理系统 毕业设计 341654
  • (更新)A股上市公司华证ESG评级得分稳健性校验ESG得分年均值中位数(2009-2023年.12)
  • (规划)24届春招和25届暑假实习路线准备规划
  • (十七)devops持续集成开发——使用jenkins流水线pipeline方式发布一个微服务项目