Toggle class except if a child is clicked in React - javascript

I want to have a header where you can click it to hide/show it. But on this header, there will be a group of buttons that would be part of a child element. If you click these buttons, I don't want the whole thing collapse.
How can I achieve this in React? What I have so far is going to collapse everything, because Child is in Parent and under row
class Parent extends Component {
constructor() {
super();
this.state = {
show: false
}
}
render() {
return (
<div className="row" onClick={() => this.setState({show: !this.state.show})}>
<div className="col-2">
<Child/>
</div>
...
</div>
)
}
}

You should be able to use stopPropagation() in the event handler for the buttons to prevent it from bubbling further. See http://facebook.github.io/react/docs/events.html for API details.
class Child extends Component {
handleClick(event) {
event.stopPropagation();
doSomething();
}
render() {
return (
<button onClick={e => this.handleClick(e)}>
Click Me!
</button>
);
}
}

In Child's onClick event handler,
add this line
event.stopPropagation()

Related

How to detect if child is clicked on parent click React

I'm using React and I have the following situation.
I have one parent div with onClick event, which takes full width and inside that div I have an image. I want to be able to know where it is clicked. Thus, I want to know if image (child) or div(parent) is clicked.
My code is as follows:
class APP extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div className="parent" onClick={(e) => console.log("PARENT CLICK")}>
<img src="https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg"
style={{maxWidth: "60%", maxHeight: "90%", pointerEvents: 'none', zIndex: 99999}}
onClick={e => console.log("IMAGE CLICK")}
/>
</div>
)
}
}
ReactDOM.render(<APP />, document.querySelector("#app"))
But it detects always the parent click. I tried to add z-index to the child, but I think that child can't be in front of parent.
Here is the fiddle.
class APP extends React.Component {
constructor(props) {
super(props)
}
handleclick(e){
e.stopPropagation();
console.log(e.target.tagName);
return false;
}
render() {
return (
<div className="parent" onClick={(e) => this.handleclick(e)}>
<img src="https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg"
style={{maxWidth: "30%", maxHeight: "30%", zIndex: 99999}}
onClick={(e) => this.handleclick(e)}
/>
</div>
)
}
}
ReactDOM.render(<APP />, document.querySelector("#app"))
please note here I added e.stopPropagation() in the click event which only executes with the target element.. here you can read more about propogation
and also please remove the CSS pointerEvents: 'none' from the img tag, it works fine.
Working Fiddle
pointerEvents : none will block the pointer events.Remove that from your styling
You have pointerEvents set to none in your img's inline style object. Remove that. Can remove zIndex as well.
From CSS-Tricks:-
pointer-events:none prevents all click, state and cursor options on
the specified HTML element
You don't need e.stopPropagation() here. Just set the event handler only on parent like so (this is known as event delegation) :-
class APP extends React.Component {
constructor(props) {
super(props)
}
handleclick(e){
console.log(e.target.tagName);
}
render() {
return (
<div className="parent" onClick={this.handleclick}>
<img src="https://interactive-examples.mdn.mozilla.net/media/cc0-images/grapefruit-slice-332-332.jpg"
style={{maxWidth: "30%", maxHeight: "30%"}}
/>
</div>
)
}
}
ReactDOM.render(<APP />, document.querySelector("#app"))
Im not sure if this is the react way of doing this but in javascript you can use the event object properties to get the element that triggered it with event.target and the element that handled it with event.currentTarget.
document.querySelector('#parent').addEventListener('click', (e) => {
console.log(`clicked element is: ${e.target.id}`);
console.log(`event handler element is: ${e.currentTarget.id}`);
});
<div id='parent'>parent
<div id='child'>child</div>
</div>
As CodeBug noted above it should be enough to stop the propagation on the click event by calling (inside of the onClick function):
event.stopPropagation();

What is the event that will trigger click release-Javascript/Reactjs

I'm trying to highlight a element on click and on release of click event anywhere on the page I want to de-highlight the same element:
fidde:
https://jsfiddle.net/3jvpg9t1/2/
code:
class Hello extends React.Component {
constructor(props) {
super(props);
this.state = {
activeState: false
};
}
onmouseup() {
this.setState({active: false});
}
onClick() {
this.setState({active:true});
}
render() {
const bgcolor = this.state.active ? "#e9e9e9" :""
return <div>
<span style={{backgroundColor: bgcolor}}onClick={this.onClick.bind(this)} onMouseUp={this.onmouseup.bind(this)}>helo world</span>
{this.state.active &&
<select><option>A</option><option>B</option></select>}
</div>
}
}
ReactDOM.render(
<Hello name="World" />,
document.getElementById('container')
);
but for some reason I'm not getting the event that will trigger when the mouse click is release.
I tried: onMouseUp, onBlur
any ideas?
I've never found a good way of doing this with just React, so I've fallen back on vanilla JS event listeners.
document.body.addEventListener('click', this.handleBodyClick);
This will listen for a click on the body and will fire off the function which will set the active state to false.
Here it is in action: https://jsfiddle.net/sq2ajn8o/30/
If you want to have the active state toggle on click you should use a function that does something like this:
handleClick() {
const { activeState } = this.state;
this.setState({ activeState: !activeState });
}
If the active state is true, it will change to false and vice versa.

Remove class from single div of a sequence that was rendered through map

I am rendering a sequence of divs with some content and a button. All divs have a certain class on them and they also have a button. When I click on that button, I want to remove the class of the div that it belongs to. My render code looks like this:
render () {
return (
<div>
{
arr.map(x => {
return (
<div className={this.state.displayClass ? 'someClass' : ''}>
<button onClick={() => this.handleClick()}> Remove class <button>
</div>
)
})
}
</div>
)
}
For the button click, I am simply setting the displayClass to false and I successfully remove the div:
handleClick () {
this.setState({ displayClass: false })
}
The problem here obviously is that the way I'm doing it, it will remove all classes from all divs. So my question is how can I make each button click to only remove the class from it's own div?
Just extract each div into separate component with his own state:
class ClassManagableDiv extends PureComponent {
state = {
displayClass: true
};
handleClick = () => {
this.setState({ displayClass: false })
}
render() {
return <div className={this.state.displayClass ? 'someClass' : ''}>
<button onClick={this.handleClick}> Remove class </button>
</div>
}
}
And next apply it to your array:
render () {
return (
<div>
{
arr.map(x => <ClassManagableDiv />)
}
</div>
)
}
You can create a stateful component for the individual div that will contain that state. Then when you click on the button, it will only update the state of that component, not of all.
render () {
return (
<div>
{
arr.map(x => {
return (
<div className={this.state.displayClass ? 'someClass' : ''}>
<button onClick={(ev) => this.handleClick(ev,"someClass")}> Remove class <button>
</div>
)
})
}
</div>
)
}
and
handleClick (ev,classN) {
ev.currentTarget.parentNode.classList.remove(classN)
}
what it does ? you sen de event "ev" when calling handleClick plus add the className to be removed, then in handleClick you get that target el that fired the event, then look for its parent / which is the one that holds the class / and remove it

How to call parent function in child component

I checked quite a lot of examples but found most of them having events fired in Child Component.
Can someone please suggest how can I call the Parent Component's function in child with the click event on Parent Component? Thanks.
Parent Component (app.js):
Class App extends Component {
handleClick = (e, id, text) => {
e.preventDefault();
this.setState({val: text})
}
render() {
return (
<div>
<Form val={this.state.val} Click={this.handleClick.bind(this) }/>
<Button onClick={(e) => this.handleClick(e, todo.id, todo.text)}>
<Icon>edit_icon</Icon>
</Button>
</div>
)
}
}
Child Component (form.js):
this.props.Click(); //where should i call this function since my button is in parent component
Class Form extends Component{
render() {
const { text } = this.state;
return (
<TextField
value={text}
color="secondary"
/>
)
}
}
}
If you want to call it in your Child component, you need an event to trigger it or maybe a condition. So, for example in your form.js, we will trigger it with a button click form your child component
render() {
const { text } = this.state;
return (
<TextField
value={text}
color="secondary"
/>
<Button onClick={this.props.Click} />
)
}
}
Maybe, using a Button in your child component is not a great choice for your case since you already have a Button to call the Click function in your parent component, this Button in child component I made is only for example
One way you can do this is use a ref to call the function..
// create the ref
constructor() {
super();
this.myFormRef = React.createRef()
}
// click handler for the button
handleClick = (e, id, text) => {
// here you have the child instance which gives you access to its functions
this.myFormRef.someChildMethodThatIsOnTheChildClass()
}
render() {
// notice here we use the ref on the form via... ref={this.myFormRef}
return (
<Form val={this.state.val} ref={this.myFormRef} Click={this.handleClick.bind(this) }/>
<Button onClick={(e) => this.handleClick(e, todo.id, todo.text)}>
<Icon>edit_icon</Icon>
</Button>
)
)
I would like to note though that it doesn't seem to make much sense as to why you want to do this. You should probably re-think your architecture. Also what is the button press supposed to be doing? submitting the form?

How to check the class name of the event target in ReactJS?

I now have a function handleRightClick(e) which will be called when I right click on the container. Inside the container, there are several Items and I expect the menu will be shown only when I right click one of the Items.
export default class ProjectContainer extends React.Component {
...
handleRightClick(e) {
console.log(e.target.name); // I want to check the event target whether is `Item` Class.
this.refs.rightClickMenu.reShow(e.clientX, e.clientY); // This will open the right click menu.
}
...
render() {
return (
<div style={styles.root} onContextMenu={this.handleRightClick} onClick={this.handleLeftClick}>
<Item /><Item /><Item /><Item /><Item /><Item /><Item />
<RightClickMenuForProjectItem ref='rightClickMenu'/>
</div>
);
}
}
If I console.log(e), I get this in Chrome console:
> Object {dispatchConfig: Object, _targetInst: ReactDOMComponent, _dispatchInstances: ReactDOMComponent, nativeEvent: MouseEvent, type: "contextmenu"…}
This is the class Item:
export default class Item extends React.Component {
render() {
return (
<Card style={styles.card} onClick={this.props.onClick}>
<img style={styles.img}/>
<div style={styles.divInfo}>
<h4 style={styles.title}>{this.props.title}</h4>
<div style={styles.projectType}>{this.props.projectType}</div>
</div>
</Card>
);
}
}
Finally, I will use it to form something like this:
handleRightClick(e) {
if (e.target.className == "Item") {
// Open the right click menu only when I right click one of the Item.
this.refs.rightClickMenu.reShow(e.clientX, e.clientY);
}
}
I want to check the event target whether is Item class. How can I access the class name of the event target?
To access at className an element use e.target.className
Try with this
export default class ProjectContainer extends React.Component {
...
handleRightClick(e) {
// To avoid get wrong class name, use this.
// But if the default context menu come up, without this is OK.
e.stopPropagation()
console.log(e.target.className); // This get the className of the target
this.refs.rightClickMenu.reShow(e.clientX, e.clientY);
}
...
}
This is the same on javascript without lib's
If an empty result is appeared in the console, this means that you haven't set the className of the Item class in the render return. You can change your class to be like this:
const className = 'Item';
export default class Project extends React.Component {
...
render() {
return (
<Card style={styles.card} onClick={this.props.onClick} className={className}>
<img style={styles.img} className={className}/>
<div style={styles.divInfo} className={className}>
<h4 style={styles.title} className={className}>{this.props.title}</h4>
<div style={styles.projectType} className={className}>{this.props.projectType}</div>
</div>
</Card>
);
}
}
Now the resulting handleRightClick(e) should be like this:
handleRightClick(e) {
if (e.target.className == 'Item')
//Show the menu if it is not visible, reShow the menu if it is already visible
this.refs.rightClickMenu.reShow(e.clientX, e.clientY);
else
//Hide the menu
this.refs.rightClickMenu.hide();
}
Result
The menu will be shown when click one of the Item.
The menu will not be shown when click outside the Item.

Categories