Is there any way to add all the events from the parent element to all the child elements using ReactJS without hard coding all of the events?
<div className='myClass' onClick={handleSelect} onDoubleClick={handleOpen}>
<span className='child' />
</div>
Click handlers propagate, so there's no need. Attach a single handler to the parent.
const App = () => {
const handleSelect = () => console.log('click');
const handleOpen = () => console.log('double click');
return (
<div className='myClass' onClick={handleSelect} onDoubleClick={handleOpen}>
<span className='child'>child</span>
</div>
);
};
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div class='react'></div>
If you need to identify which child element was clicked on, or exclude certain children from resulting in the handler running, use the event passed to the handler and see if its target .matches what you're looking for.
Related
When I run the code below I see different behavior in the sandbox I'm using. When I click the button I get "Uncaught SyntaxError: function statement requires a name" in the console but here it is rendering the click event out as text in the button (not sure why?)
Question is—is there a way to pass functions like this to elements created in template literals that are added to the DOM using innerHTML?
const clickHandler = () => {
console.log("Hi");
};
const root = document.getElementById("root");
root.innerHTML = `<button onClick=${clickHandler}>Click</button>`;
<!DOCTYPE html>
<html>
<body>
<div id="root"></div>
</body>
</html>
Inline handlers, being attributes, are essentially strings. When you do
<button onClick=${clickHandler}>
the function gets turned into a string, which results in:
<button onclick="()" ==""> {
console.log("Hi");
}>Click</button>
which isn't what you want - the automatic fixing of the syntax caused by the interpolation doesn't work well.
When you do it properly, the string in the attribute, when evaluated as JavaScript, may only reference global variables (for all the cases worth mentioning). While you can get your code to work by using the name of the function, and by making sure the function is global:
const clickHandler = () => {
console.log("Hi");
};
const root = document.getElementById("root");
root.innerHTML = `<button onClick=clickHandler()>Click</button>`;
<div id="root"></div>
A much better approach would be to attach the event listener with JavaScript, instead of an eval-like attribute with scope problems. Insert the elements, then navigate to the element you want the listener attached to, and use addEventListener.
const clickHandler = () => {
console.log("Hi");
};
const root = document.getElementById("root");
root.innerHTML = `<button>Click</button>`;
root.querySelector('button').addEventListener('click', clickHandler);
<div id="root"></div>
Or use a framework designed for this sort of thing.
const App = () => {
const clickHandler = () => {
console.log("Hi");
};
return <button onClick={clickHandler}>Click</button>;
};
ReactDOM.createRoot(document.querySelector('.root')).render(<App />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div class='root'></div>
I am working on a React application.
I have a sort of card with image, title and a button to do stuff.
This is a simplified version:
<a href="www.example.com" class="card">
<div class="image-wrapper">
<div class="image">...</div>
<div class="cta-button" onClick={handleOnClick}>cta</div>
</div>
<div class="title">title</div>
</a>
cta-button is placed on bottom-right of image-wrapper using position: absolute.
How can I, when clicking on cta-button, prevent visiting card link in handleOnClick?
Having an interactive element within an a element is a violation of the specification. An a element cannot have interactive content. While your div is just a div and so it's not interactive by default, it is interactive if you add a click handler to it. I suggest moving the clickable element outside the a element.
But if you want to have the invalid structure, you need to prevent the default action of the click. Have handleClick accept its event parameter and call event.preventDefault() on it:
const {useState} = React;
const Example = () => {
const handleClick = (e) => {
console.log("Stopped");
e.preventDefault();
};
return <div onClick={handleClick}>Click here</div>;
};
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
For completeness, here's that same snippet with stopPropgation instead (which doesn't work):
const {useState} = React;
const Example = () => {
const handleClick = (e) => {
console.log("Stopped");
e.stopPropagation(); // Doesn't work
};
return <div onClick={handleClick}>Click here</div>;
};
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Example />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
I have this useState element, but the function doesn't get called onClick.
I have tried many solutions and debugging and it seems like the Click event doesn't get called, no matter what.
const [modalHotel, setModalHotel] = useState(false)
{modalHotel && <ModalHotel CloseModal = {setModalHotel} />}
<img src="./assets/square.svg" alt="expand" onClick={() => setModalHotel(!modalHotel)}/>
Sometimes elements won't register onClick events in React unless you specify a tabIndex. Try this:
const checkClick = () => {
console.log("It worked!!");
setModalHotel(!modalHotel);
}
<img tabIndex={0} src="./assets/square.svg" alt="expand" onClick={checkClick} />
this will help you to debug whether the click event is actually being fired.
Side Note:
From an accessibility perspective, it's almost always preferrable to use either button or a elements to handle clicks like this. They have tabIndexes by default, and better a11y support in general.
Found the problem!
I had this z-index: -1; in my css, removed it and it worked.
You haven't really provided enough information to completely diagnose your issue. But in the meantime, here is a working snippet with an element that is clickable (a button) to toggle state using a useState hook. You can compare what is different between your non-working code and this working example.
const { useState } = React;
const Thing = (props) => {
const [modalHotel, setModalHotel] = useState(false);
return (
<div>
<h1>{modalHotel.toString()}</h1>
<button onClick={() => setModalHotel(!modalHotel)}>Toggle</button>
</div>
);
}
ReactDOM.render(
<Thing />,
document.getElementById("react")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
I'm new to coding and react and i'm trying to update x4 divs so when the user clicks it will update the content inside. Currently i have the code updating however it updates all 4 div's instead of the specific div which is clicked.
I know this will be controlled by using event however slightly confused what exactly i need to include.
Any help in the correct direction would be greatly appreciated. Current code runs fine but content within all 4 divs update rather than the specific one which is clicked
const [isActive, setIsActive] = useState(false)
const toggleContent = e => {
e.preventDefault()
setIsActive(!isActive)
}
Example div i'm trying to update when user clicks (currently i have x4 others which are the same logic but different HTML content. Currently when any of the 4 buttons are clicked, all x4 divs content change however i only want it to change the specific div which contained the button that was clicked
{!isActive ?
<article>
<h1>BEFORE CLICK TEXT</h1>
<a onClick={toggleClass}>BUTTON</a>
</article>
:
<article>
<p>AFTER CLICK TEXT</p>
<a onClick={toggleClass}>Back</a>
</article>
}
maintain activeIndex instead of just active boolean. In render method, based on activeIndex and index decide the active attributes for div.
Try the snippet.
const content = ['abc', 'def', 'ghi', 'jkl'];
const Component = () => {
const [activeIndex, setActiveIndex] = React.useState(-1);
return (
<div>
{content.map((item, index) =>
<div key={item}
style={{color: activeIndex === index ? "red" : "black" }}
onClick={() => setActiveIndex(index)}> {item}
</div>)}
</div>
)
}
ReactDOM.render(<Component />, document.getElementById('app'));
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="app"> </div>
I have a React app which has this code:
<div className={ mainCss } data-appmode={ AppMode.MAIN } onClick={ this.handleAppModeClick.bind(this) }>
<div className="NavLinkIconContainer"></div>
<div>Main</div>
</div>
When I click on div with className="NavLinkIconContainer" the handleAppModeClick function does not work correctly. The function fires but does not pick up the data-* attribute. However, when I click on the div with Main as it's content it works perfectly picking up the data-* attribute. Why is this and how do I fix it?
You can the data-appmode value from event.currentTarget.dataset.
event - the event object
currentTarget - the element with the onClick
dataset - an easy access to data-* values
class Demo extends React.Component {
handleAppModeClick = event => console.log(event.currentTarget.dataset.appmode);
render() {
return (
<div
data-appmode="example"
onClick={this.handleAppModeClick}>
<div className="NavLinkIconContainer">NavLinkIconContainer</div>
<div>Main</div>
</div>
)
}
}
ReactDOM.render(
<Demo />,
root
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Ori Drori's suggestion will work. Alternatively, you can write your component as
<div className={ mainCss } data-appmode={ AppMode.MAIN } onClick={ ()=>this.handleAppModeClick(AppMode.MAIN) }>
<div className="NavLinkIconContainer"></div>
<div>Main</div>
This will fire off the function with a hard-coded argument. In my opinion, it's a little bit cleaner than using event.currentTarget.dataset. That being said, I am sure that others disagree with me, and if the solution works, it works.
Alternatively, you can use
handleAppModeClick = event => console.log(event.target.getAttribute('data-appmode'));
to get the data from the actual element.