react实现移动端下拉菜单的示例代码

前端之家收集整理的这篇文章主要介绍了react实现移动端下拉菜单的示例代码前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

前言

项目中要实现类似与vant的DropdownMenu:下拉菜单。看了vans 的效果 其实也没什么难度,于是动手鲁了一个这样的组件。
项目的技术栈为react全家桶+material UI + ant Design mobile。

vans的效果

react实现移动端下拉菜单的示例代码

我自己实现的效果

思路

常规做法获取dom元素,动态修改选中dom的innerHtml。
当然这种方式不是react推荐的

我的做法既然react不推荐直接操作dom元素,那可以采用动态动态修改class的方式达到效果,例如:

  1. let cls ="normal"
  2. div未被选中时
  3. <div className={cls}/>
  4. div被选中时
  5. cls+=" current"
  6. <div className ={cls}>

实现步骤

顶部tab采用三个div的方式布局,由于需要动态修改tab上的标题,所以定义一个数组,reducer中的tab数据结构如下

  1. let tabs = {};
  2. tabs[TABKAY.AREA] = {
  3. key: TABKAY.AREA,text: "全部区域",obj: {}
  4. };
  5. tabs[TABKAY.SORT] = {
  6. key: TABKAY.SORT,text: "综合排序",obj: {}
  7. };
  8. tabs[TABKAY.FILTER] = {
  9. key: TABKAY.FILTER,text: "筛选",obj: SX
  10. };
  11. const initialState = {
  12. areas: [{ id: "",name: "全部区域" }],tabs: tabs,actionKey: TABKAY.AREA,// 标识了当前选中tab
  13. closePanel: true // 标识panel div 是否显示
  14. };

tabUI组件的页面容器渲染方法

  1. renderTabs() {
  2. const { tabs,actionKey,closePanel } = this.props;
  3. //---------
  4. if (!closePanel) {
  5. fixedBody();
  6. } else {
  7. looseBody();
  8. }
  9. //---------
  10.  
  11. let aray = [];
  12. for (let key in tabs) {
  13. let item = tabs[key];
  14. let cls = item.key + " item";
  15. if (item.key === actionKey && !closePanel) {
  16. cls += " current";
  17. }
  18.  
  19. aray.push(
  20. <div className={cls} key={item.key} onClick={() => this.onChangeTab(item.key)}>
  21. {item.text}
  22. </div>);
  23. }
  24.  
  25. return aray;
  26. }

样式:这里边有个技巧,就是利用了css元素选择器的伪类的方式巧妙实现了箭头以及箭头的旋转动画

  1. .item {
  2. flex: 1;
  3. font-size: 15px;
  4. border-right: 0.5px solid #eaeaea;
  5. text-align: center;
  6.  
  7. &:last-child {
  8. border: none;
  9. }
  10.  
  11. &.AREA:after,&.SORT:after,&.FILTER:after {
  12. content: "";
  13. display: inline-block;
  14. width: 5px;
  15. height: 5px;
  16. margin-bottom: 2px;
  17. margin-left: 6px;
  18. border: 2px solid #666;
  19. border-width: 0 2px 2px 0;
  20. transform: rotate(45deg);
  21. -webkit-transform: rotate(45deg);
  22. -webkit-transition: .3s;
  23. transition: .3s;
  24. }
  25.  
  26. &.current {
  27. color: #0084ff;
  28. }
  29.  
  30. &.current:after {
  31. border-color: #0084ff;
  32. transform: rotate(225deg);
  33. -webkit-transform: rotate(225deg);
  34. }

chrome 查看元素

全部区域tab被选中:

react实现移动端下拉菜单的示例代码

综合tab被选中

react实现移动端下拉菜单的示例代码

每次点击不同的tab时 都会自动的渲染current这个css样式,这样就实现了下拉菜单功能

完整代码

  1. /**
  2. * Class:
  3. * Author: miyaliunian
  4. * Date: 2019/5/26
  5. * Description: tabs 选择器
  6. * 医院列表
  7. */
  8. import React,{ Component } from "react";
  9. import { ZHPX,TABKAY } from "@api/Constant";
  10. //Util
  11. import { fixedBody,looseBody } from "@utils/fixRollingPenetration";
  12. //Redux
  13. import { connect } from "react-redux";
  14. import { bindActionCreators } from "redux";
  15. import { actions as tabActions,getTabs,getAreasList,getActionKey,getClosePanel } from "@reduxs/modules/tabs";
  16. import { actions as hospitalActions,} from "@reduxs/modules/hospital";
  17.  
  18. //样式
  19. import "./tabs.less";
  20.  
  21. class Tabs extends Component {
  22.  
  23. /**
  24. * 变化当前点击的item状态 同时filter 请求
  25. * @param filterItem 当前选中的元素
  26. * @param key 哪个tab是选中状态
  27. */
  28. changeDoFilter(filterItem,key,event) {
  29. const { tabActions: { changeFilter },hospitalActions:{filterHosiContentList} } = this.props;
  30. event.stopPropagation();
  31. changeFilter(filterItem,(filter) => {
  32. filterHosiContentList(filter);
  33. });
  34. }
  35.  
  36. /**
  37. * 筛选tab确定按钮
  38. * @param event
  39. */
  40. filterPanel(event) {
  41. const {tabActions:{closePanelAction},tabs,hospitalActions:{filterHosiContentList}} = this.props;
  42. event.stopPropagation();
  43. closePanelAction(()=>{
  44. filterHosiContentList(tabs)
  45. })
  46. }
  47.  
  48. /**
  49. * 点击切换Tab
  50. * @param key
  51. */
  52. onChangeTab(key) {
  53. const { actionKey,tabActions: { changeTab } } = this.props;
  54. let closePanel = false;
  55. //如果前后点击的是同一个tab 就关闭panel
  56. if (actionKey === key && !this.props.closePanel) {
  57. closePanel = true;
  58. }
  59. closePanel ? looseBody() : fixedBody();
  60. changeTab(key,closePanel);
  61. }
  62.  
  63. /**
  64. * 渲染顶部tab
  65. */
  66. renderTabs() {
  67. const { tabs,closePanel } = this.props;
  68. //---------
  69. if (!closePanel) {
  70. fixedBody();
  71. } else {
  72. looseBody();
  73. }
  74. //---------
  75.  
  76. let aray = [];
  77. for (let key in tabs) {
  78. let item = tabs[key];
  79. let cls = item.key + " item";
  80. if (item.key === actionKey && !closePanel) {
  81. cls += " current";
  82. }
  83.  
  84. aray.push(
  85. <div className={cls} key={item.key} onClick={() => this.onChangeTab(item.key)}>
  86. {item.text}
  87. </div>);
  88. }
  89.  
  90. return aray;
  91. }
  92.  
  93. /**
  94. * 全部区域
  95. * @returns {*}
  96. */
  97. renderAreaContent() {
  98. const { areasList } = this.props;
  99. return areasList.map((item,index) => {
  100. let cls = item.active + " area-item";
  101. return (
  102. <li key={index} className={"area-item"} onClick={(e) => this.changeDoFilter(item,TABKAY.AREA,e)}>
  103. {item.name}
  104. </li>
  105. );
  106. });
  107. }
  108.  
  109. /**
  110. * 全部排序
  111. * @returns {any[]}
  112. */
  113. renderSORTContent() {
  114. let sortList = ZHPX;
  115. return sortList.map((item,index) => {
  116. let cls = item.action ? "type-item active" : "type-item";
  117.  
  118. return (
  119. <li key={index} className={cls} onClick={(e) => this.changeDoFilter(item,TABKAY.SORT,e)}>
  120. {item.name}
  121. </li>
  122. );
  123. });
  124. }
  125.  
  126. /**
  127. * 筛选
  128. * @returns {*}
  129. */
  130.  
  131. renderFilterInnerContent(items/*filterList*/) {
  132. return items.map((item,index) => {
  133. let cls = "cate-Box";
  134. if (item.active) {
  135. cls += " active";
  136. }
  137.  
  138. return (
  139. <div key={index} className={cls} onClick={(e) => this.changeDoFilter(item,TABKAY.FILTER,e)}>
  140. {item.name}
  141. </div>
  142. );
  143. });
  144.  
  145. }
  146.  
  147. renderFILTERContent() {
  148. let filterList = [];
  149. const { tabs } = this.props;
  150. filterList = tabs[TABKAY.FILTER].obj;
  151. return filterList.map((item,index) => {
  152. return (
  153. <li key={index} className={"filter-item"}>
  154. <p className={"filter-title"}>{index == 0 ? `医院类型: ${item.groupTitle}` : `医院等级: ${item.groupTitle}`}</p>
  155. <div className={"item-content"}>
  156. {this.renderFilterInnerContent(item.items,filterList)}
  157. </div>
  158. </li>
  159. );
  160. });
  161. }
  162.  
  163. /**
  164. * 渲染过滤面板
  165. */
  166. renderContent() {
  167. const { tabs,actionKey } = this.props;
  168. let array = [];
  169. for (let key in tabs) {
  170. let item = tabs[key];
  171. let cls = item.key + "-panel";
  172. if (item.key === actionKey) {
  173. cls += " current";
  174. }
  175.  
  176. // 全部区域
  177. if (item.key === TABKAY.AREA) {
  178. array.push(
  179. <ul key={item.key} className={cls}>
  180. {this.renderAreaContent()}
  181. </ul>
  182. );
  183. } else if (item.key === TABKAY.SORT) {
  184. // 综合排序
  185. array.push(
  186. <ul key={item.key} className={cls}>
  187. {this.renderSORTContent()}
  188. </ul>
  189. );
  190. } else if (item.key === TABKAY.FILTER) {
  191. // 筛选
  192. array.push(
  193. <ul key={item.key} className={cls}>
  194. {this.renderFILTERContent()}
  195. <div className={"filterBtn"} onClick={(e) => this.filterPanel(e)}>
  196. 确定
  197. </div>
  198. </ul>
  199. );
  200. }
  201.  
  202. }
  203. return array;
  204. }
  205.  
  206. render() {
  207. const { closePanel,tabActions: { closePanelAction } } = this.props;
  208. let cls = "panel";
  209. if (!closePanel) {
  210. cls += " show";
  211. } else {
  212. cls = "panel";
  213. }
  214. return (
  215. <div className={"tab-header"}>
  216. <div className={"tab-header-top border-bottom"}>
  217. {this.renderTabs()}
  218. </div>
  219. <div className={cls} onClick={() => closePanelAction(()=>{})}>
  220. <div className={"panel-inner"}>
  221. {this.renderContent()}
  222. </div>
  223. </div>
  224. </div>
  225. );
  226. }
  227.  
  228. componentDidMount() {
  229. const { areasList,tabActions: { loadAreas } } = this.props;
  230. if (areasList && areasList.length !== 1) {
  231. return;
  232. }
  233. loadAreas();
  234. }
  235. }
  236.  
  237. const mapStateToProps = state => {
  238. return {
  239. areasList: getAreasList(state),tabs: getTabs(state),actionKey: getActionKey(state),closePanel: getClosePanel(state)
  240. };
  241. };
  242.  
  243. const mapDispatchToProps = dispatch => {
  244. return {
  245. tabActions: bindActionCreators(tabActions,dispatch),hospitalActions: bindActionCreators(hospitalActions,dispatch)
  246. };
  247. };
  248.  
  249. export default connect(mapStateToProps,mapDispatchToProps)(Tabs);

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

猜你在找的JavaScript相关文章