最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Toggle dropdown menu in reactjs - Stack Overflow

programmeradmin0浏览0评论

I have the following code for a simple dropdown menu on my navbar: /

index.html

<div id="menu-button"></div>

NavMenu.js

var NavMenu = React.createClass({
    getDefaultProps: function()
    {
        return {
            isOpen: false
        };
    },

    render: function()
    {
        if (this.props.isOpen)
        {
            return (
                <div className="dropdown">
                    <ul>
                        <li><a href="#">News</a></li>
                        <li><a href="#">About</a></li>
                        <li><a href="#">Guidelines</a></li>
                        <li><a href="#">Exchange</a></li>
                        <li><a href="#">Forum</a></li>
                    </ul>
                </div>
            );
        }
        return null;
    }
});

NavMenuButton.js

var NavMenuButton = React.createClass({

    getInitialState: function()
    {
        return {
            isOpen: false
        };
    },

    toggleMenu: function(e)
    {
        e.stopPropagation();
        this.setState({isOpen: !this.state.isOpen});
    },

    onClose: function()
    {
        this.setState({isOpen: false});
    },

    ponentDidMount: function ()
    {
        document.body.addEventListener('click', this.onClose);
    },

    ponentWillUnmount: function ()
    {
        document.body.removeEventListener('click', this.onClose);
    },

    render: function()
    {
        return (
            <div>
                <a onClick={this.toggleMenu} href="#">Menu</a>
                <NavMenu isOpen={this.state.isOpen} />
            </div>
        );
    }

});

React.render(<NavMenuButton />, document.getElementById('menu-button'));

I understand with my current code that both the toggleMenu method and onClose method are called when the user clicks the menu button to close the menu (since they are also effectively clicking the body); and that the onClose method is called first, meaning that the state is set to false, but then the toggleMenu method is called and it's set back to true. Why is this, and how can I fix it so that clicking the menu button toggles the menu and clicking the body hides the menu?

If this approach seems wrong what approach should I be using? I'm fairly new to react so I'm still learning what goes where and why.

Also, I cannot use a full body div as the solution to this, it needs to be a typical dropdown; so if users want to interact with another part of the page (maybe clicking a link), then they can do that.

I have the following code for a simple dropdown menu on my navbar: https://jsfiddle/jL3yyk98/10/

index.html

<div id="menu-button"></div>

NavMenu.js

var NavMenu = React.createClass({
    getDefaultProps: function()
    {
        return {
            isOpen: false
        };
    },

    render: function()
    {
        if (this.props.isOpen)
        {
            return (
                <div className="dropdown">
                    <ul>
                        <li><a href="#">News</a></li>
                        <li><a href="#">About</a></li>
                        <li><a href="#">Guidelines</a></li>
                        <li><a href="#">Exchange</a></li>
                        <li><a href="#">Forum</a></li>
                    </ul>
                </div>
            );
        }
        return null;
    }
});

NavMenuButton.js

var NavMenuButton = React.createClass({

    getInitialState: function()
    {
        return {
            isOpen: false
        };
    },

    toggleMenu: function(e)
    {
        e.stopPropagation();
        this.setState({isOpen: !this.state.isOpen});
    },

    onClose: function()
    {
        this.setState({isOpen: false});
    },

    ponentDidMount: function ()
    {
        document.body.addEventListener('click', this.onClose);
    },

    ponentWillUnmount: function ()
    {
        document.body.removeEventListener('click', this.onClose);
    },

    render: function()
    {
        return (
            <div>
                <a onClick={this.toggleMenu} href="#">Menu</a>
                <NavMenu isOpen={this.state.isOpen} />
            </div>
        );
    }

});

React.render(<NavMenuButton />, document.getElementById('menu-button'));

I understand with my current code that both the toggleMenu method and onClose method are called when the user clicks the menu button to close the menu (since they are also effectively clicking the body); and that the onClose method is called first, meaning that the state is set to false, but then the toggleMenu method is called and it's set back to true. Why is this, and how can I fix it so that clicking the menu button toggles the menu and clicking the body hides the menu?

If this approach seems wrong what approach should I be using? I'm fairly new to react so I'm still learning what goes where and why.

Also, I cannot use a full body div as the solution to this, it needs to be a typical dropdown; so if users want to interact with another part of the page (maybe clicking a link), then they can do that.

Share Improve this question edited May 23, 2015 at 14:55 user2442241 asked May 9, 2015 at 2:37 user2442241user2442241 9753 gold badges11 silver badges13 bronze badges 4
  • Please specify all the code needed for a jsfiddle. It makes it easier for us. I think your issue is that you change the state in handleClick only if it is not opened. So it goes only one way: non-open to open, but you also need to check the other way. I would remove the if statement. – Jeremy D Commented May 9, 2015 at 17:17
  • Extra code added. That if statement actually doesn't change anything (I thought it would), so with or without it the dropdown still doesn't disappear when the menu button is clicked. – user2442241 Commented May 9, 2015 at 17:44
  • The code is still not working out of the box. Still has issues with hasSignedInm accountpage and so on. Please give a minimal code to test – Jeremy D Commented May 9, 2015 at 17:46
  • Forgot about those, that should be working now. – user2442241 Commented May 9, 2015 at 17:57
Add a ment  | 

2 Answers 2

Reset to default 3

Your issue is that clicking on the link will also call the body click listener. It means that your state will go from:

  1. Click on link
  2. Click listener on body called
  3. This.state.open set to false
  4. Render called with this.state.open false
  5. Click listener on the link called
  6. This.state.open set to true
  7. Render called with this.state.open true

e.stopPropagation() doesn't work in React. One workaround would be to:

handleBodyClick: function(e)
{
    if (e.target.nodeName !== 'A') {
       this.setState({isOpen: false});
    }
},

Another way (and better way) would be to have the click listener not on body, but on a div, and make it as big as possible (to be as the same size a body basically).

Here is an example with binding a click on a div instead of body: https://jsfiddle/jL3yyk98/

handleClose: function() {
  this.setState({isOpen: false});
},

handleGlobalClick: function(event) {
  var _con = this.refs.mC.getDOMNode() // <ponent ref="mC">
  if (!(_con == event.target) && !_con.contains(event.target)) {
    this.handleClose();
  }
},

ponentDidMount: function() {
  document.body.addEventListener('click', this.handleGlobalClick);
},

ponentWillUnmount: function() {
  document.body.removeEventListener('click', this.handleGlobalClick);
},

this work on the latest chrome, you can also use the jQuery rewrite the node.contains()

发布评论

评论列表(0)

  1. 暂无评论