React Bootstrap OverlayTrigger with trigger="focus" bug work around

React Bootstrap OverlayTrigger with trigger="focus" bug work around

在 iOS Safari 中,带有 trigger="focus" 的 OverlayTrigger 在点击外部时无法关闭。这是我的代码:

<OverlayTrigger
    trigger="focus"
    placement="right"
    overlay={ <Popover id="popoverID" title="Popover Title">
                What a popover...
            </Popover> } >
    <a bsStyle="default" className="btn btn-default btn-circle" role="Button" tabIndex={18}>
        <div className="btn-circle-text">?</div>
    </a>
</OverlayTrigger>

我知道这是 Bootstrap 的一个已知错误,因为它甚至不适用于 iOS 中的 their own website,但是有人知道有什么方法可以绕过它吗?如果是不需要 jQuery 的东西最好,但是欢迎 jQuery 解决方案。谢谢

好的,因为没有其他人给我一个解决方法,我和我的同事一起解决了这个问题 3 天,我们想出了这个沉重的解决方案:

问题:

使用 trigger="focus",Bootstrap Popover/Tooltip 可以在点击 Popover/Tooltip 之外时被关闭,但不能被 TOUCHING 关闭。 Android 浏览器显然会自动将触摸更改为点击,因此在 Android 上一切正常。但是 iOS safari 和基于 iOS safari 的浏览器(iOS chrome、iOS firefox 等)不会那样做。

修复:

我们发现在React Bootstrap中,Overlay组件实际上可以让你自定义何时显示Popover/Tooltip,所以我们在Overlay的基础上构建了这个组件InfoOverlay。为了处理组件外的点击,我们需要为 Popover/Tooltip 和 window 添加事件监听器来处理 'mousedown' 和 'touchstart'。此外,由于组件的 padding-right 最初为 0px,因此此方法会使 Popover 始终具有其最小宽度,并且我们根据某些父组件的宽度进行制作,以便它基于父组件进行响应。代码如下所示:

import React, { Component, PropTypes as PT } from 'react';
import {Popover, Overlay} from 'react-bootstrap';

export default class InfoOverlay extends Component {

    static propTypes = {
        PopoverId: PT.string,
        PopoverTitle: PT.string,
        PopoverContent: PT.node,
        // You need to add this prop and pass it some numbers 
        // if you need to  customize the arrowOffsetTop, it's sketchy...
        arrowOffsetTop: PT.number,
        // This is to be able to select the parent component
        componentId: PT.string  
    }

    constructor(props) {
        super(props);
        this.state = {
            showPopover: false,
            popoverClicked: false
        };
    }

    componentDidMount() {
        // Here are the event listeners and an algorithm
        // so that clicking popover would not dismiss itself
        const popover = document.getElementById('popoverTrigger');
        if (popover) {
            popover.addEventListener('mousedown', () => {
                this.setState({
                    popoverClicked: true
                });
            });
            popover.addEventListener('touchstart', () => {
                this.setState({
                    popoverClicked: true
                });
            });
        }
        window.addEventListener('mousedown', () => {
            if (!this.state.popoverClicked) {
                this.setState({
                    showPopover: false
                });
            } else {
                this.setState({
                    popoverClicked: false
                });
            }
        });
        window.addEventListener('touchstart', () => {
            if (!this.state.popoverClicked) {
                this.setState({
                    showPopover: false
                });
            } else {
                this.setState({
                    popoverClicked: false
                });
            }
        });

        // this is to resize padding-right when window resizes
        window.onresize = ()=>{
            this.setState({});
        };
    }

    // This function sets the style and more importantly, padding-right
    getStyle() {
        if (document.getElementById(this.props.componentId) && document.getElementById('popoverTrigger')) {
            const offsetRight = document.getElementById(this.props.componentId).offsetWidth - document.getElementById('popoverTrigger').offsetLeft - 15;
            return (
                {display: 'inline-block', position: 'absolute', 'paddingRight': offsetRight + 'px'}
            );
        }
        return (
            {display: 'inline-block', position: 'absolute'}
        );
    }

    overlayOnClick() {
        this.setState({
            showPopover: !(this.state.showPopover)
        });
    }

    render() {
        const customPopover = (props) => {
            return (
                {/* The reason why Popover is wrapped by another
                    invisible Popover is so that we can customize
                    the arrowOffsetTop, it's sketchy... */}
                <div id="customPopover">
                    <Popover style={{'visibility': 'hidden', 'width': '100%'}}>
                        <Popover {...props} arrowOffsetTop={props.arrowOffsetTop + 30} id={this.props.PopoverId} title={this.props.PopoverTitle} style={{'marginLeft': '25px', 'marginTop': '-25px', 'visibility': 'visible'}}>
                            {this.props.PopoverContent}
                        </Popover>
                    </Popover>
                </div>
            );
        };

        return (
            <div id="popoverTrigger" style={this.getStyle()}>
                <a bsStyle="default" className="btn btn-default btn-circle" onClick={this.overlayOnClick.bind(this)} role="Button" tabIndex={13}>
                    <div id="info-button" className="btn-circle-text">?</div>
                </a>
                <Overlay
                    show={this.state.showPopover}
                    placement="right"
                    onHide={()=>{this.setState({showPopover: false});}}
                    container={this}>
                    {customPopover(this.props)}
                </Overlay>
            </div>
        );
    }
}

最后,这是一项繁重的工作,因为它需要大量代码来修复,而且您可能会感觉到您的站点由于 4 个事件侦听器而稍微变慢了。最好的解决办法就是告诉 Bootstrap 解决这个问题...