• 【CSS】自定义下拉框


    自定义下拉框

    效果

    在这里插入图片描述

    index.tsx

    import {useState, useEffect} from 'react'
    import {observer} from 'mobx-react-lite'
    import styles from './index.module.scss'
    import recordStore from 'src/store/recordStore'
    
    const DropDownBox: React.FC = props => {
      // 查询月份下拉框
      const [dropDown, setDropDown] = useState(false)	// 默认下拉框为关闭状态
      const handleOpen = (e: any) => {
        e.stopPropagation()		// 禁止事件冒泡
        e.nativeEvent.stopImmediatePropagation()	// 阻止监听同一事件的其他事件监听器被调用
        setDropDown(!dropDown)	// 将下拉框设为打开状态
      }
      useEffect(() => {
      	// 点击空白处关闭下拉框
        document.body.addEventListener('click', () => {
          setDropDown(false)
        })
        return () => {
          document.body.removeEventListener('click', () => {
            setDropDown(false)
          })
        }
      }, [])
      const selectMonth = (e: any) => {
        // console.log(e.currentTarget.getAttribute('data-name'))
        setDropDown(!dropDown)	// 选择月份后自动关闭下拉框
        const month = e.currentTarget.getAttribute('data-name')		// 获取下拉选项中的属性值
        recordStore.setSelectedMonth(month)		// 更新mobx中的month状态值
      }
      // 获取最近月份记录
      useEffect(() => {
        if (recordStore.selectedMonth) {
          recordStore.getDataList()
        }
      }, [recordStore.selectedMonth])	// 监听月份值变化, 月份更新则触发获取该月份数据记录的方法
      return (
    	<div className={styles.select}>
    	  <ul>
    	    <li>
    	      <div className={styles.head} onClick={handleOpen}>
    	        <span>{recordStore.selectedMonth}</span>
    	        <span className={styles.icon_down}></span>
    	      </div>
    	      <ul className={dropDown ? styles.option : styles.hide}>
    	        {recordStore.monthList &&
    	          recordStore.monthList.map((item: string, index: number) => (
    	            <li key={`month_${index}`} data-name={item} className={styles.item} onClick={selectMonth}>
    	              {item}
    	            </li>
    	          ))}
    	      </ul>
    	    </li>
    	  </ul>
    	</div>
    )
    
    export default observer(DropDownBox)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    recordStore.ts

    import {makeAutoObservable, runInAction} from 'mobx'
    import {formatMonth} from 'src/utils'
    import {listMonth} from 'src/services/api'
    
    class RecordStore {
      selectedMonth = '' // 选中月份
      monthList: Array<string> = [] // 月份列表
      constructor() {
        makeAutoObservable(this)
      }
      // 更改选中月份
      async setSelectedMonth(month = '') {
        runInAction(() => {
          this.selectedMonth = month
        })
      }
      // 获取月份列表
      async getMonthList() {
        const res = await listMonth()
        if (res.status === 0) {
          runInAction(() => {
            this.monthList = res.data
            this.selectedMonth = res.data.length > 0 && res.data[0]
          })
        }
      }
      // 获取该月份数据记录
      async getDataList() {
      	const res = await helpLog({month: formatMonth(this.selectedMonth)})
        if (res.status === 0) {
           runInAction(() => {
             this.dataList = res.data
           })
        }
      }
    }
    
    export default new RecordStore ()
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    index.module.scss

    .search {
      display: flex;
      line-height: 24px;
      ul {
        margin-left: 8px;
        width: 96px;
        height: 24px;
        border: 1px solid #dcdcdc;
        border-radius: 5px;
        background: #eeeeee;
        font-size: 12px;
        cursor: pointer;
        li {
          position: relative;
          .head {
            overflow: hidden;
            width: 100%;
            height: 24px;
            box-sizing: border-box;
            line-height: 20px;
            padding-left: 15px;
            .icon_down {
              position: absolute;
              top: 8px;
              right: 16px;
              width: 11px;
              height: 7px;
              background-image: url(../imgs/icon_down.png);
              background-size: 100%;
            }
          }
          .option {
            width: 96px;
            position: absolute;
            top: 23px;
            left: -9px;
            border: none;
            border-radius: 0;
            box-sizing: border-box;
            z-index: 1;
            .item {
              line-height: 20px;
              padding-left: 18px;
              background: #eeeeee;
              height: 24px;
              &:hover {
                background: #cccccc;
              }
            }
          }
          .hide {
            display: none;
          }
        }
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    补充

    在 React 合成事件中,需要阻止冒泡时,可以使用 e.stopPropagation()e.preventDefault() 方法来解决,另外还可以使用 e.nativeEvent.stopImmediatePropagation() 方法解决。

    1. e.stopPropagation() 只能阻止合成事件间冒泡,即下层的合成事件,不会冒泡到上层的合成事件。事件本身还都是在 document 上执行。所以最多只能阻止 document 事件不能再冒泡到 window 上。
    2. Event 接口的 stopImmediatePropagation() 方法阻止监听同一事件的其他事件监听器被调用。

    在 React 中,一个组件只能绑定一个同类型的事件监听器,当重复定义时,后面的监听器会覆盖之前的。

    事实上 nativeEventstopImmediatePropagation只能阻止绑定在 document 上的事件监听器。而合成事件上的 e.nativeEvent.stopImmediatePropagation() 能阻止合成事件不会冒泡到 document 上。

    利用 e.nativeEvent.stopImmediatePropagation 解决的问题:

    • 点击 div 内部,由于不冒泡,会正常执行菜单点击;
    • 点击 div 外部,执行 document 上事件,关闭 div;
  • 相关阅读:
    第7章 博客文章的前端渲染显示
    SpringMVC识 乱码过滤+ajax请求+SpringMVC四种跳转方式和默认的参数类型以及日期处理
    ChatGPT笔记
    离线部署NFS文件系统
    Log4j2再发新版本2.16.0,完全删除Message Lookups的支持,加固漏洞防御
    常见Lidar点云数据处理及可视化软件汇总
    gpnmb+ gpnmb-AT2 cell空转映射 上皮细胞的空转映射
    FFmpeg源代码:AVFormatContext相关接口
    Excel VSTO开发9 -使用Form窗口
    【论文翻译】关于在并行快照隔离中读取更新鲜的快照
  • 原文地址:https://blog.csdn.net/Jessieeeeeee/article/details/125330592