I am attempting to prevent clicks on a React Bootstrap popover from bubbling up and triggering the onClick handler on a parent element. Using the standard event.stopPropagation() does not, in fact, stop propagation, nor does its native counterpart:
Please see the working reduced test case on JSBin.
My current approach (within the parent's onClick event) is to check if the target element is inside the popover's DOM tree (using jQuery.has for convenience):
function onParentClicked(e) {
if (!$(this.refs.parentElement).has(e.target)) {
// click did NOT originate from popover
}
}
This approach mostly works, but seems like a hacky workaround for simply using stopPropagation in the child's onClick handler.
Is this possibly a React Bootstrap bug? Or am I missing something in how I should be handling this logic?
You could try putting Popover outside the parent div?
Related
I'm using react-window to optimize a custom dropdown menu component. The dropdown's menu uses the List component from react-window to render a ul, and the item component passed to the List renders an li element with an onClick handler assigned (to close the menu and pick an item). The dropdown component worked fine before I used react-window, but after switching, the onClick handler does not fire the first time each li element is clicked.
Investigating the li in Chrome's dev tools revealed that after the li node is first mounted, React attaches an noop event handler to the node's onclick property, apparently due to some iOS Safari bug (here). After clicking the node once, this goes away and my onClick handler fires properly.
If I change the event to onMouseUp, it works fine, but I shouldn't have to do that; I should be able to use onClick and have it fire reliably.
I'd like to file a bug report on this, but I'm not entirely sure if it's React's fault or react-window's fault. I looked through the source of react-window and it doesn't seem to do anything strange to render the item component; it just uses createElement. Obviously onClick events work for some elements, including my li elements before I moved to react-window...somehow the noop event handler wasn't affecting them beforehand.
Is anyone else familiar with this particular iOS workaround and why it's suddenly breaking my event handlers?
In my React component I have a click event handler on the component's root element. I need to prevent this event bubbling when the user clicks a certain child element but also allow the double click to highlight the inner text as it would do usually.
I have tried to attach a click handler to the child element and call event.stopPropagation() which does prevent the parent click handler from firing but doing a double-click does not highlight the text.
Is there a way to do this?
Footnote: After writing this question I wonder if the only way is to use createTextRange()
I am using a javascript library that will add/remove elements to the DOM when some data is updated.
To add a new element to the DOM it calls a template function returning an element. In my case, the template function is defined inside an angular directive and returns something like return $compile(html)(scope)[0]; because I need to use the UI Bootstrap Popover directive inside the added element.
For the Popover, I need to use popover-append-to-body="true".
My problem is, if the triggering element is removed from the DOM, the popover is never removed. So if we add a new triggering element, a second popover will be appended to body, etc.
Here is a simplified example : http://plnkr.co/edit/AFsXBcaLBAs0F2garbAu?p=preview
Clicking on the "Click" button opens the popover, clicking "Remove" removes the "Click" button, clicking "Add" re-adds the "Click" button and clicking "Click" again adds a second popover to the DOM.
How can I remove the Popover directive when the triggering element is removed from the DOM ?
I need to totally deletes it, not only hide it/remove it from the DOM (I can hide it with popover-is-open but when this is set back to true, I see the popover still exists).
Is there a way to call destroy on the Popover directive of the element that will be deleted ?
You shouldn't do DOM manipulation in you a controller, both in JS and HTML, that's why directives are for, and for your case there were a couple of built in directives you could have used.
you should have kept an array to represent your buttons and popover states
you should place all you JS code in your controller, and used ng-click to bind click events to functions in your controller
don't use onclick when you have ng-click
The angular API works completely different then vanilla JS and even jquery, so don't mix them, use what Angular provides you, refer to the docs for help.
Here is your "revamped" code
I know this is the proper way to do -
Attach handlers on react components using attributes such as onClick, onBlur etc.
If attaching custom events then attach them in componentDidMount so that it always gets attached on every rendering.
https://facebook.github.io/react/docs/interactivity-and-dynamic-uis.html#under-the-hood-autobinding-and-event-delegation
However, what happens when an event is attached to a DOM element using jquery ? Why am I not able to listen to those events ? I am attaching them out side componentDidMount. However the event handler should get trigerred the first time atleast ?
I was debugging some react code which was attaching an handler to it directly, but it was not trigerring the handler at all. Why was that ?
Without code example it's difficult to know for sure but more than likely you're trying to bind an event using jQuery to some react-generate DOM element. So depending where you do the bind, you're probably using a jQuery selector which returns no results because React hasn't rendered that element to the DOM at the time the selector is being run.
This is why, if you are going to use jQuery binding, you should make sure you do so as part of the component lifecycle that ensures the element is actually rendered to the DOM before you are trying to select it with jQuery. So that's why the recommendation is to put this kind of code inside componentDidMount and clean it up in componentWillUnmount.
I'm using react with a map library, I use react to render elements in a container which provided by the map library.
The problem is that the container prevent event bubbling, I checkout reactjs source code found the event emitter listen on html document and depend on event bubbling to dispatch event.
How can I make the onClick handler to work? I currently addEventListener manually but don't think it a elegant way.
<body>
<container preventBubbling>
<ReactElement onClick={this.handleClick}/>
</container>
</body>
As you said, React depends on event bubbling instead of attaching event handlers directly to any DOM node you want events for. This is an integral part of React, and not something you can avoid without manually attaching event handlers in componentDidMount. And also manually removing them in componentWillUnmount.
What's the reason for cancelling bubbling in the map library? I'd try to remove that cancellation and see if it works.