You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

react-bootstrap模态框内react-popper中select点击后弹窗秒消失问题

问题:React-Bootstrap Modal中Popper内的Select弹窗秒消失

在React-Bootstrap Modal中使用react-popper展示带年月选择的DatePicker时,点击select的下拉选项弹窗仅显示一秒就消失。

复现代码

TSX代码:

import React, {useState} from 'react';
import ReactDOM from 'react-dom';
import FocusTrap from 'focus-trap-react';
import { usePopper } from 'react-popper';

export default function Tooltip() {
  const [referenceElement, setReferenceElement] = useState(null);
  const [popperElement, setPopperElement] = useState(null);
  const [showPicker, setShowPicker] = useState(false);
  const popper = usePopper(referenceElement, popperElement, {
    placement: 'bottom-start'
  });
  const openPopper = () => {
    setShowPicker(true);
  };
  const closePopper = () => {
    setShowPicker(false);
  };

  return <div className='datepicker-container'>
    <a ref={setReferenceElement} className="btn btn-dark" onClick={openPopper}>Click to open select in Popper</a>
  {showPicker && ReactDOM.createPortal(
    <FocusTrap
      active
      focusTrapOptions={{
        initialFocus: false,
        allowOutsideClick: true,
        clickOutsideDeactivates: true,
        onDeactivate: closePopper
      }}>
      <div
        tabIndex={-1}
        style={popper.styles.popper}
        className="dialog-sheet"
        {...popper.attributes.popper}
        ref={setPopperElement}
        role="dialog">
          <div>
        <select>
          <option>1</option>
          <option>2</option>
          <option>3</option>
          <option>4</option>
        </select>
        </div>
      </div>
    </FocusTrap>,
    document.querySelector('#popup-container')
  )}
</div>
}

CSS代码:

.datepicker-container {
  position: relative;
  z-index: 100;
}

.dialog-sheet {
  z-index: 2003;
  background-color: #EEE;
}

原因分析

问题核心在于FocusTrap的配置冲突:

  1. 你设置了clickOutsideDeactivates: true,而原生select的下拉弹窗是浏览器渲染的独立元素,不属于FocusTrap包裹的DOM范围,点击下拉选项时会被判定为“点击外部”,触发onDeactivate关闭Popper。
  2. React-Bootstrap Modal本身自带焦点陷阱(Focus Trap),额外嵌套FocusTrap会导致双重焦点管理,加剧冲突。

解决方案

方案一:调整FocusTrap的点击判定逻辑

修改clickOutsideDeactivates为自定义函数,仅当点击Popper外部区域时才触发关闭:

<FocusTrap
  active
  focusTrapOptions={{
    initialFocus: false,
    allowOutsideClick: true,
    clickOutsideDeactivates: (e) => {
      // 判断点击目标是否属于Popper容器内的元素
      return !e.target.closest('.dialog-sheet');
    },
    onDeactivate: closePopper
  }}>
  {/* 原有Popper内容 */}
</FocusTrap>

方案二:移除额外的FocusTrap,改用外部点击监听

利用Modal自带的焦点管理,去掉FocusTrap组件,通过监听document点击事件来关闭Popper:

import React, { useState, useEffect } from 'react';
import ReactDOM from 'react-dom';
import { usePopper } from 'react-popper';

export default function Tooltip() {
  const [referenceElement, setReferenceElement] = useState(null);
  const [popperElement, setPopperElement] = useState(null);
  const [showPicker, setShowPicker] = useState(false);
  const popper = usePopper(referenceElement, popperElement, {
    placement: 'bottom-start'
  });

  const openPopper = () => {
    setShowPicker(true);
  };

  const closePopper = () => {
    setShowPicker(false);
  };

  // 监听外部点击关闭Popper
  useEffect(() => {
    if (!showPicker) return;
    
    const handleClickOutside = (e) => {
      const isClickInsidePopper = popperElement?.contains(e.target);
      const isClickOnTrigger = referenceElement?.contains(e.target);
      if (!isClickInsidePopper && !isClickOnTrigger) {
        closePopper();
      }
    };

    document.addEventListener('click', handleClickOutside);
    return () => document.removeEventListener('click', handleClickOutside);
  }, [showPicker, popperElement, referenceElement]);

  // 阻止Popper内部点击冒泡到document
  const handlePopperClick = (e) => {
    e.stopPropagation();
  };

  return (
    <div className='datepicker-container'>
      <a ref={setReferenceElement} className="btn btn-dark" onClick={openPopper}>Click to open select in Popper</a>
      {showPicker && ReactDOM.createPortal(
        <div
          tabIndex={-1}
          style={popper.styles.popper}
          className="dialog-sheet"
          {...popper.attributes.popper}
          ref={setPopperElement}
          role="dialog"
          onClick={handlePopperClick}
        >
          <div>
            <select>
              <option>1</option>
              <option>2</option>
              <option>3</option>
              <option>4</option>
            </select>
          </div>
        </div>,
        document.querySelector('#popup-container')
      )}
    </div>
  );
}

补充说明

你的CSS中z-index设置没有问题,dialog-sheet的2003已经高于React-Bootstrap Modal默认的1050,无需调整。

内容的提问来源于stack exchange,提问作者Martin Cup

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 13:35:16