React 例子 - 右键菜单 context menu

这篇博客介绍了如何在React应用中实现右键菜单功能。通过使用HTML的contextmenu事件,结合React的状态管理,创建了一个可显示和隐藏的右键菜单。详细代码示例展示了事件监听、状态控制以及菜单内容的展示过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

代码并非原创,忘了从哪里找到的,如有人知道,可博客留言。
效果点击链接:
https://codepen.io/Anyicheng2015/pen/jaayMw

首先,你要对React有点了解。其次,你要明白HTML的事件处理机制。

我先贴代码的框架,然后逐步完善。

class ContextMenu extends React.Component {
    constructor(props) {
        super(props);
    }  
    componentDidMount() {
    }

    componentWillUnmount() {
    }
    render() {
    }
}

ReactDOM.render(<ContextMenu />, document.getElementById('root'));

鼠标右击事件,通常的‘click’事件是针对鼠标左键,mousedown是鼠标左键和右键及中间键都可以。那么有没有鼠标右键的事件呢?
有的,在HTML里面支持contextmenu事件。
https://developer.mozilla.org/en-US/docs/Web/Events/contextmenu
添加contextmenu事件处理:

document.addEventListener('contextmenu', this._handleContextMenu);

当然也需要移除:

document.removeEventListener('contextmenu', this._handleContextMenu);

当鼠标点到其它地方,或者滚动的时候,不要显示右键菜单,需要添加其他事件。反应到代码上面:

    componentDidMount() {
        document.addEventListener('contextmenu', this._handleContextMenu);
        document.addEventListener('click', this._handleClick);
        document.addEventListener('scroll', this._handleScroll);
    };

    componentWillUnmount() {
      document.removeEventListener('contextmenu', this._handleContextMenu);
      document.removeEventListener('click', this._handleClick);
      document.removeEventListener('scroll', this._handleScroll);
    }

接下来 ,就是事件处理方法了:
右键菜单事件:

    _handleContextMenu = (event) => {
        event.preventDefault();

        this.setState({ visible: true });

        const clickX = event.clientX;
        const clickY = event.clientY;
        const screenW = window.innerWidth;
        const screenH = window.innerHeight;
        const rootW = this.root.offsetWidth;
        const rootH = this.root.offsetHeight;

        // right为true,说明鼠标点击的位置到浏览器的右边界的宽度可以放contextmenu。
        // 否则,菜单放到左边。
        // top和bottom,同理。
        const right = (screenW - clickX) > rootW;
        const left = !right;
        const top = (screenH - clickY) > rootH;
        const bottom = !top;

        if (right) {
            this.root.style.left = `${clickX + 5}px`;
        }

        if (left) {
            this.root.style.left = `${clickX - rootW - 5}px`;
        }

        if (top) {
            this.root.style.top = `${clickY + 5}px`;
        }

        if (bottom) {
            this.root.style.top = `${clickY - rootH - 5}px`;
        }
    };

从上面的代码中,我们看到,需要增加一个state,名称为visible,用来控制菜单是否显示。在_handleContextMenu里面,它被设置为true,从而可以显示出来。那么,当鼠标点击其它位置或者滚动的时候,需要把它设置为false。

    _handleClick = (event) => {
        const { visible } = this.state;
        const wasOutside = !(event.target.contains === this.root);

        if (wasOutside && visible) this.setState({ visible: false, });
    };

    _handleScroll = () => {
        const { visible } = this.state;

        if (visible) this.setState({ visible: false, });
    };

最后,右键菜单 context menu 的内容:

    render() {
        const { visible } = this.state;

        return(visible || null) && 
            <div ref={ref => {this.root = ref}} className="contextMenu">
                <div className="contextMenu--option">Share this</div>
                <div className="contextMenu--option">New window</div>
                <div className="contextMenu--option">Visit official site</div>
                <div className="contextMenu--option contextMenu--option__disabled">View full version</div>
                <div className="contextMenu--option">Settings</div>
                <div className="contextMenu--separator" />
                <div className="contextMenu--option">About this app</div>
            </div>
    };

完整代码:

class ContextMenu extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            visible: false,
        };
    }  

    componentDidMount() {
        document.addEventListener('contextmenu', this._handleContextMenu);
        document.addEventListener('click', this._handleClick);
        document.addEventListener('scroll', this._handleScroll);
    };

    componentWillUnmount() {
      document.removeEventListener('contextmenu', this._handleContextMenu);
      document.removeEventListener('click', this._handleClick);
      document.removeEventListener('scroll', this._handleScroll);
    }

    _handleContextMenu = (event) => {
        event.preventDefault();

        this.setState({ visible: true });

        const clickX = event.clientX;
        const clickY = event.clientY;
        const screenW = window.innerWidth;
        const screenH = window.innerHeight;
        const rootW = this.root.offsetWidth;
        const rootH = this.root.offsetHeight;

        const right = (screenW - clickX) > rootW;
        const left = !right;
        const top = (screenH - clickY) > rootH;
        const bottom = !top;

        if (right) {
            this.root.style.left = `${clickX + 5}px`;
        }

        if (left) {
            this.root.style.left = `${clickX - rootW - 5}px`;
        }

        if (top) {
            this.root.style.top = `${clickY + 5}px`;
        }

        if (bottom) {
            this.root.style.top = `${clickY - rootH - 5}px`;
        }
    };

    _handleClick = (event) => {
        const { visible } = this.state;
        const wasOutside = !(event.target.contains === this.root);

        if (wasOutside && visible) this.setState({ visible: false, });
    };

    _handleScroll = () => {
        const { visible } = this.state;

        if (visible) this.setState({ visible: false, });
    };

    render() {
        const { visible } = this.state;

        return(visible || null) && 
            <div ref={ref => {this.root = ref}} className="contextMenu">
                <div className="contextMenu--option">Share this</div>
                <div className="contextMenu--option">New window</div>
                <div className="contextMenu--option">Visit official site</div>
                <div className="contextMenu--option contextMenu--option__disabled">View full version</div>
                <div className="contextMenu--option">Settings</div>
                <div className="contextMenu--separator" />
                <div className="contextMenu--option">About this app</div>
            </div>
    };
}

ReactDOM.render(<ContextMenu />, document.getElementById('root'));
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值