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


Posted in Javascript onJanuary 16, 2020

前言

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

vans的效果

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

我自己实现的效果

思路

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

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

let cls ="normal"
div未被选中时
<div className={cls}/> 
div被选中时
cls+=" current"
<div className ={cls}>

实现步骤

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

let tabs = {};
tabs[TABKAY.AREA] = {
key: TABKAY.AREA,
text: "全部区域",
obj: {}
};
tabs[TABKAY.SORT] = {
key: TABKAY.SORT,
text: "综合排序",
obj: {}
};
tabs[TABKAY.FILTER] = {
key: TABKAY.FILTER,
text: "筛选",
obj: SX
};
const initialState = {
areas: [{ id: "", name: "全部区域" }],
tabs: tabs,
actionKey: TABKAY.AREA,// 标识了当前选中tab
closePanel: true // 标识panel div 是否显示
};

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

renderTabs() {
const { tabs, actionKey, closePanel } = this.props;
//---------
if (!closePanel) {
 fixedBody();
} else {
 looseBody();
}
//---------

let aray = [];
for (let key in tabs) {
 let item = tabs[key];
 let cls = item.key + " item";
 if (item.key === actionKey && !closePanel) {
  cls += " current";
 }

 aray.push(
  <div className={cls} key={item.key} onClick={() => this.onChangeTab(item.key)}>
   {item.text}
  </div>);
}

return aray;
}

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

.item {
flex: 1;
font-size: 15px;
border-right: 0.5px solid #eaeaea;
text-align: center;

&:last-child {
 border: none;
}

&.AREA:after, &.SORT:after, &.FILTER:after {
 content: "";
 display: inline-block;
 width: 5px;
 height: 5px;
 margin-bottom: 2px;
 margin-left: 6px;
 border: 2px solid #666;
 border-width: 0 2px 2px 0;
 transform: rotate(45deg);
 -webkit-transform: rotate(45deg);
 -webkit-transition: .3s;
 transition: .3s;
}

&.current {
 color: #0084ff;
}

&.current:after {
 border-color: #0084ff;
 transform: rotate(225deg);
 -webkit-transform: rotate(225deg);
}

chrome 查看元素

全部区域tab被选中:

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

综合tab被选中

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

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

完整代码

/**
 * Class:
 * Author: miyaliunian
 * Date: 2019/5/26
 * Description: tabs 选择器
 * 医院列表
 */
import React, { Component } from "react";
import { ZHPX, TABKAY } from "@api/Constant";
//Util
import { fixedBody, looseBody } from "@utils/fixRollingPenetration";
//Redux
import { connect } from "react-redux";
import { bindActionCreators } from "redux";
import { actions as tabActions, getTabs, getAreasList, getActionKey, getClosePanel } from "@reduxs/modules/tabs";
import { actions as hospitalActions,} from "@reduxs/modules/hospital";

//样式
import "./tabs.less";

class Tabs extends Component {
 
 /**
  * 变化当前点击的item状态 同时filter 请求
  * @param filterItem 当前选中的元素
  * @param key  哪个tab是选中状态
  */
 changeDoFilter(filterItem, key, event) {
  const { tabActions: { changeFilter }, hospitalActions:{filterHosiContentList} } = this.props;
  event.stopPropagation();
  changeFilter(filterItem, key, (filter) => {
   filterHosiContentList(filter);
  });
 }

 /**
  * 筛选tab确定按钮
  * @param event
  */
 filterPanel(event) {
  const {tabActions:{closePanelAction}, tabs,hospitalActions:{filterHosiContentList}} = this.props;
  event.stopPropagation();
  closePanelAction(()=>{
   filterHosiContentList(tabs)
  })
 }

 /**
  * 点击切换Tab
  * @param key
  */
 onChangeTab(key) {
  const { actionKey,tabActions: { changeTab } } = this.props;
  let closePanel = false;
  //如果前后点击的是同一个tab 就关闭panel
  if (actionKey === key && !this.props.closePanel) {
   closePanel = true;
  }
  closePanel ? looseBody() : fixedBody();
  changeTab(key, closePanel);
 }

 /**
  * 渲染顶部tab
  */
 renderTabs() {
  const { tabs, actionKey, closePanel } = this.props;
  //---------
  if (!closePanel) {
   fixedBody();
  } else {
   looseBody();
  }
  //---------

  let aray = [];
  for (let key in tabs) {
   let item = tabs[key];
   let cls = item.key + " item";
   if (item.key === actionKey && !closePanel) {
    cls += " current";
   }

   aray.push(
    <div className={cls} key={item.key} onClick={() => this.onChangeTab(item.key)}>
     {item.text}
    </div>);
  }

  return aray;
 }

 /**
  * 全部区域
  * @returns {*}
  */
 renderAreaContent() {
  const { areasList } = this.props;
  return areasList.map((item, index) => {
   let cls = item.active + " area-item";
   return (
    <li key={index} className={"area-item"} onClick={(e) => this.changeDoFilter(item, TABKAY.AREA, e)}>
     {item.name}
    </li>
   );
  });
 }


 /**
  * 全部排序
  * @returns {any[]}
  */
 renderSORTContent() {
  let sortList = ZHPX;
  return sortList.map((item, index) => {
   let cls = item.action ? "type-item active" : "type-item";

   return (
    <li key={index} className={cls} onClick={(e) => this.changeDoFilter(item, TABKAY.SORT, e)}>
     {item.name}
    </li>
   );
  });
 }


 /**
  * 筛选
  * @returns {*}
  */

 renderFilterInnerContent(items/*filterList*/) {
  return items.map((item, index) => {
   let cls = "cate-box";
   if (item.active) {
    cls += " active";
   }

   return (
    <div key={index} className={cls} onClick={(e) => this.changeDoFilter(item, TABKAY.FILTER, e)}>
     {item.name}
    </div>
   );
  });

 }

 renderFILTERContent() {
  let filterList = [];
  const { tabs } = this.props;
  filterList = tabs[TABKAY.FILTER].obj;
  return filterList.map((item, index) => {
   return (
    <li key={index} className={"filter-item"}>
     <p className={"filter-title"}>{index == 0 ? `医院类型: ${item.groupTitle}` : `医院等级: ${item.groupTitle}`}</p>
     <div className={"item-content"}>
      {this.renderFilterInnerContent(item.items, filterList)}
     </div>
    </li>
   );
  });
 }

 /**
  * 渲染过滤面板
  */
 renderContent() {
  const { tabs, actionKey } = this.props;
  let array = [];
  for (let key in tabs) {
   let item = tabs[key];
   let cls = item.key + "-panel";
   if (item.key === actionKey) {
    cls += " current";
   }

   // 全部区域
   if (item.key === TABKAY.AREA) {
    array.push(
     <ul key={item.key} className={cls}>
      {this.renderAreaContent()}
     </ul>
    );
   } else if (item.key === TABKAY.SORT) {
    // 综合排序
    array.push(
     <ul key={item.key} className={cls}>
      {this.renderSORTContent()}
     </ul>
    );
   } else if (item.key === TABKAY.FILTER) {
    // 筛选
    array.push(
     <ul key={item.key} className={cls}>
      {this.renderFILTERContent()}
      <div className={"filterBtn"} onClick={(e) => this.filterPanel(e)}>
       确定
      </div>
     </ul>
    );
   }

  }
  return array;
 }


 render() {
  const { closePanel, tabActions: { closePanelAction } } = this.props;
  let cls = "panel";
  if (!closePanel) {
   cls += " show";
  } else {
   cls = "panel";
  }
  return (
   <div className={"tab-header"}>
    <div className={"tab-header-top border-bottom"}>
     {this.renderTabs()}
    </div>
    <div className={cls} onClick={() => closePanelAction(()=>{})}>
     <div className={"panel-inner"}>
      {this.renderContent()}
     </div>
    </div>
   </div>
  );
 }


 componentDidMount() {
  const { areasList, tabActions: { loadAreas } } = this.props;
  if (areasList && areasList.length !== 1) {
   return;
  }
  loadAreas();
 }
}

const mapStateToProps = state => {
 return {
  areasList: getAreasList(state),
  tabs: getTabs(state),
  actionKey: getActionKey(state),
  closePanel: getClosePanel(state)
 };
};

const mapDispatchToProps = dispatch => {
 return {
  tabActions: bindActionCreators(tabActions, dispatch),
  hospitalActions: bindActionCreators(hospitalActions, dispatch)
 };
};

export default connect(mapStateToProps, mapDispatchToProps)(Tabs);

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

Javascript 相关文章推荐
用jquery设置按钮的disabled属性的实现代码
Nov 28 Javascript
javascript的数据类型、字面量、变量介绍
May 23 Javascript
基于jquery打造的百分比动态色彩条插件
Sep 19 Javascript
jquery弹出框的用法示例(一)
Aug 26 Javascript
jQuery插件制作之全局函数用法实例
Jun 01 Javascript
JS实现自动变换的菜单效果代码
Sep 09 Javascript
JavaScript事件用法浅析
Oct 31 Javascript
js中的eval()函数把含有转义字符的字符串转换成Object对象的方法
Dec 02 Javascript
详解如何在微信小程序开发中正确的使用vant ui组件
Sep 13 Javascript
Vue.js 实现地址管理页面思路详解(地址添加、编辑、删除和设置默认地址)
Dec 11 Javascript
微信小程序分享小程序码的生成(带参数)以及参数的获取
Mar 25 Javascript
解决vue请求接口第一次成功,第二次失败问题
Sep 08 Javascript
vue项目中使用eslint+prettier规范与检查代码的方法
Jan 16 #Javascript
JS实现简单的表格增删
Jan 16 #Javascript
JS实现基本的网页计算器功能示例
Jan 16 #Javascript
JS数组进阶示例【数组的几种函数用法】
Jan 16 #Javascript
js实现简单的秒表
Jan 16 #Javascript
JS 数组基本用法入门示例解析
Jan 16 #Javascript
js实现上下左右键盘控制div移动
Jan 16 #Javascript
You might like
PHP多线程类及用法实例
2014/12/03 PHP
PHP设计模式之装饰器模式实例详解
2018/02/07 PHP
Javascript !!的作用
2008/12/04 Javascript
jQuery live( type, fn ) 委派事件实现
2009/10/11 Javascript
jquery实现智能感知连接外网搜索
2013/05/21 Javascript
javascript获取ckeditor编辑器的值(实现代码)
2013/11/18 Javascript
document.write的几点使用心得
2014/05/14 Javascript
layui导航栏实现代码
2017/05/19 Javascript
解决IE7中使用jQuery动态操作name问题
2017/08/28 jQuery
JS中使用react-tooltip插件实现鼠标悬浮显示框
2019/05/15 Javascript
vue实现固定位置显示功能
2019/05/30 Javascript
微信小程序里引入SVG矢量图标的方法
2019/09/20 Javascript
vue-cli3访问public文件夹静态资源报错的解决方式
2020/09/02 Javascript
[35:26]DOTA2上海特级锦标赛B组小组赛#2 VG VS Fnatic第三局
2016/02/26 DOTA
[47:20]DAC2018 4.4 淘汰赛 Optic vs Mineski 第一场
2018/04/05 DOTA
Python的Flask框架中web表单的教程
2015/04/20 Python
Python3如何解决字符编码问题详解
2017/04/23 Python
python编程之requests在网络请求中添加cookies参数方法详解
2017/10/25 Python
pyqt5利用pyqtDesigner实现登录界面
2019/03/28 Python
Python对ElasticSearch获取数据及操作
2019/04/24 Python
python制作一个简单的gui 数据库查询界面
2020/11/19 Python
详解BeautifulSoup获取特定标签下内容的方法
2020/12/07 Python
Python命令行参数argv和argparse该如何使用
2021/02/08 Python
C语言编程题
2015/03/09 面试题
以太网Ethernet IEEE802.3
2013/08/05 面试题
售后求职信范文
2014/03/15 职场文书
爬山的活动方案
2014/08/16 职场文书
贷款承诺书
2015/01/20 职场文书
毕业论文答辩开场白和结束语
2015/05/27 职场文书
演讲比赛主持词
2015/06/29 职场文书
百年校庆感言
2015/08/01 职场文书
2016年国庆节假期旅游工作总结
2016/04/01 职场文书
全面盘点MySQL中的那些重要日志文件
2021/11/27 MySQL
Django基础CBV装饰器和中间件
2022/03/22 Python
Python实现灰色关联分析与结果可视化的详细代码
2022/03/25 Python
公历12个月名称的由来
2022/04/12 杂记