我正在创建显示在屏幕顶部的菜单。当用户点击其中一个菜单项div时会出现很多链接。我不能通过点击另一个菜单项(没问题,已经实现它)来隐藏这个div,而且还可以点击其他任何地方然后点击这个div和menuitems。
我听说过两个解决方案:
第一个对我不好,因为div将覆盖页面上的链接,我希望它们可以点击,即使在点击menuitem后它出现了相应的div。所以我尝试了第二次灵魂。但问题是在我的组件的处理程序之前触发了body上的jquery click处理程序。我不知道如何让它首先调用我的组件处理程序,然后阻止事件传播。
这是代码和js小提琴:
/** @jsx React.DOM */
var Menu = React.createClass({
click: function(e) {
console.log('component handled click - should be called before jquery one and prevent jquery handler from running at all');
e.stopPropagation();
},
render: function(){
console.log("component renders");
return (
<div>
<div>| MenuItem1 | MenuItem2 | MenuItem 3 |</div>
<br />
<div className="drop">
MenuItem 1 (This div appears when you click menu item)
<ul>
<li><a href="#" onClick={this.click}>Item 1 - when I click this I don't want document.body.click to fire</a></li>
<li><a href="#" onClick={this.click}>Item 1 - when I click this I don't want document.body.click to fire</a></li>
</ul>
</div>
</div>
);
}
});
React.renderComponent(<Menu />, document.getElementById("menu"));
$(document).ready(function() {
console.log('document is ready');
$("body").click(function() {
console.log('jquery handled click - should not happen before component handled click');
});
});
启动控制台,然后运行它以解释问题
答案 0 :(得分:15)
所以我解决了。 使用jQuery向文档添加事件处理程序时,将首先触发此事件处理程序。所以你无法捕捉事件并阻止它的传播。但是当你使用document.addEventlistener附加处理程序时,这个处理程序最后被调用,你有机会反应:)点击React组件。
我修改了Mike的代码以使其有效(http://jsfiddle.net/s8hmstzz/):
/** @jsx React.DOM */
var Menu = React.createClass({
getInitialState: function() {
return {
show: true
}
},
componentDidMount: function() {
// when attaching with jquery this event handler is run before other handlers
//$('body').bind('click', this.bodyClickHandler);
document.addEventListener('click', this.bodyClickHandler);
},
componentWillUnmount: function() {
//$('body').unbind('click', this.bodyClickHandler);
document.removeEventListener('click', this.bodyClickHandler);
},
anchorClickHandler: function(e) {
console.log('click on anchor - doing some stuff and then stopping propation');
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
},
bodyClickHandler: function(e) {
console.log('bodyclickhandler');
this.setState({show: false})
},
clickHandler: function(e) {
// react to click on menu item
},
preventEventBubbling: function(e) {
console.log('click on div - stopping propagation');
e.stopPropagation();
e.nativeEvent.stopImmediatePropagation();
},
render: function() {
return (
<div>
<a href="#" onClick={this.anchorClickHandler}>Link - will stop propagation</a>
<div id="menudrop" onClick={this.preventEventBubbling} style={{display: this.state.show ? 'block' : 'none'}}>
<ul>
<li onClick={this.clickHandler}>Menu Item</li>
</ul>
</div>
</div>
)
}
});
React.renderComponent(<Menu />, document.getElementById('menu'));
启动控制台,然后单击div,anchor和body以查看其工作原理。 我不知道为什么使用addEventListener而不是jQuery更改事件处理程序的触发顺序。
答案 1 :(得分:8)
请参阅上面的评论。
基本上,当结合使用jquery(或普通的JS事件)时,你不能依赖能够在React中停止传播像onClick
这样的事件。
您可以做的是检查您的$('body').click()
功能,而不是单击菜单(因此单击其中的项目后不会关闭菜单)。另外值得注意的是,你应该在反应组件中绑定这个主体事件,这样你就可以通过安装组件来干净地添加/删除它,而不会让组件本身不知道的任何东西显着改变组件的功能。
所以你做的是这样的事情(nb:未经测试的代码,但是基于我对同一问题的组件)
var Menu = React.createClass({
getInitialState: function() {
return {
show: true
}
},
componentDidMount: function() {
$('body').bind('click', this.bodyClickHandler);
},
componentWillUnmount: function() {
$('body').unbind('click', this.bodyClickHandler);
},
bodyClickHandler: function(e) {
if ($(e.target).parents('div').get(0) !== this.getDOMNode()) {
this.setState({
show: false
})
}
},
clickHandler: function(e) {
// react to click on menu item
},
render: function() {
return (
<div style={{display: this.state.show ? 'block' : 'none'}}>
<ul>
<li onClick={this.clickHandler}>Menu Item</li>
</ul>
</div>
)
}
});
因此,在上面的代码中,我们假设您希望在组件安装到dom后立即显示菜单下拉列表。这可能不是这种情况,因为您可能希望安装组件,然后根据悬停显示/隐藏它。在这种情况下,您只需要将$('body').bind()
事件绑定从componentDidMount
移动到回调,只要您显示菜单。每当隐藏组件时,$('body').unbind()
都应该移动到。然后你会得到一个完美的情况,当菜单显示时,我们绑定一个等待点击的身体事件,并在它被隐藏和/或卸载时取消绑定。
当点击菜单时,bodyClickHandler
之前调用clickHandler
会发生什么,但bodyClickHandler
仅在您点击的父树不包含根div
时隐藏菜单this.getDOMNode()
1}}我们的组件(render()
给出<li>
返回的根节点的html元素。
因此,在组件外部单击将关闭它,单击组件内部将不执行任何操作,单击clickHandler
将触发{{1}}。一切都按预期工作。
希望这有帮助。