how to not set off onclick in some parts of div? - javascript

I want to set off an onclick when you click the outside of a div but not the div. how can I make it that the onclick function is not set off when clicked in the specific div?
import React from "react"
import { Container, Backdrop, Card } from "./styles"
export default function FocusSneakerCard(props){
function click(){
props.setModal(false)
props.setData({})
}
return(
<Container>
<Backdrop onClick={() => click()}>
<Card>
</Card>
</Backdrop>
</Container>
)
}
PS I'm using styled-components.

I found something called event.stopPropagation() which "prevents further propagation of the current event in the capturing and bubbling phases". You can include event.stopPropagation in an onClick event of the child element like so:
const parentClick = () => {
alert("clicked");
};
const childClick = (e) => {
e.stopPropagation();
};
return (
<div className="outer" onClick={parentClick}>
<div className="inner" onClick={childClick}>
Cant Click
</div>
</div>
);
}
https://codesandbox.io/s/laughing-dubinsky-zw013?file=/src/App.js:101-382
Note: "It does not, however, prevent any default behaviors from occurring; for instance, clicks on links are still processed. If you want to stop those behaviors, see the preventDefault() method." from MDN

Related

React Hooks: useEffect for modal event listener

I have a modal dialog that I want to close if the user clicks outside of the modal. I have written the following useEffect code but I run into following issue:
The modal dialog contains a number of children (React Nodes) and those children might change (e.g. the user deletes an entry of a list). Those interactions trigger my onClick method but as the clicked list item has been removed from the modal, the modal closes even though the click was within the modal.
I thought adding [ children ] at the second parameter for useEffect would cleanup the old effect event listener quick enough that the method does not run again but this is not the case.
I handled the same issue in a class component with a ignoreNextClick-state but there must be a cleaner solution, right?
useEffect( () => {
const onClick = ( event ) => {
const menu = document.getElementById( 'singleton-modal' );
if ( !menu ) return;
// do not close menu if user clicked inside
const targetInMenu = menu.contains( event.target );
const targetIsMenu = menu === event.target;
if ( targetInMenu || targetIsMenu ) return;
onCloseModal();
};
window.addEventListener( 'click', onClick, false );
return () => window.removeEventListener( 'click', onClick, false );
}, [ children ] );
I found a solution that does not require any sort of storing old props.
The useEffect call looks like this:
useEffect( () => {
const onClickOutside = () => onCloseModal();
window.addEventListener( 'click', onClickOutside, false );
return () => window.removeEventListener( 'click', onClickOutside );
}, [] );
Adding the following click listener to the modal directly will stop the window click-listener from being called if the user clicked inside the modal.
<div
className={`modal ${ classes }`}
onClick={event => event.stopPropagation()}
role="presentation"
>
{children}
</div>`
I also added the role presentation to make the modal more accessible and aria-conform.
You can check parent of modal from the event.target.
If the current target is within the modal then return.
You can use closest to do that.
See the following solution.
...
if (event.target.closest( '.singleton-modal' ) || event.target.classList.contains('singleton-modal')) {
return;
}
...

How do I get the id of a button that was clicked? ReactJS

I was wondering how I would go about getting the id (becuase it is unknown) of a button that is clicked. So when the button is clicked, I know what the id of that specific button is. There are lots of buttons on the page and I would like to know which button is pressed (they all have unique id's). Currently the buttons look like this:
<button key={uuidv4()} id={this.props.keyword} value={this.props.keyword} onClick={this.props.onClick} className="removeButton">Remove</button>
Well if the elements are nested event.target won't always work since it refers to the target that triggers the event in the first place. See this link for the usage of event.currentTarget, which always refer to the element that the handler is bound to.
Another way to grab hold of the element and its attributes is to use React refs. This is more general when trying to get the DOM element in React.
You can use event.target.id to get the ID of button clicked
class App extends React.Component {
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(event) {
const id = event.target.id;
console.log(id);
}
render() {
return (
<button id="unique-id" onClick={this.handleClick}>Button</button>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<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 id="root"></div>
I know this has already been answered but i was working with react and faced similar issue, lost 2 hours trying to figure out why event.target.id was sometimes null or an empty string
this solved it for me:
class Button extends React.Component {
getButtonId = (e) => {
console.log(e.currentTarget.id);
}
render() {
return (
<button id="yourID" onClick={this.getButtonId}>Button</button>
);
}
}
You can use either event.target.[selector goes here] or event.currentTarget.[selector goes here] to solve your issue. See example code below.
class Button extends React.Component {
handleId = (e) => {
console.log(e.target.id);
console.log(e.currentTarget.id);
}
render() {
return (
<button id="yourID" onClick={this.handleId}>Button</button>
);
}
}
The very convenient way (not only for buttons but for list of elements) to do this is using custom attribute data-someName of DOM element and then reach it via event.currentTarget.dataset.someName
const openCard = (event) => {
console.log('event.currentTarget.dataset.id', event.currentTarget.dataset.id); // >> id
//do more actions with id
};
<Card onClick={openCard} data-id={item.id}>
<CardContent />
</Card>;
`
There is several way to do this
With manipulating onClick function
<button key={uuidv4()} id={this.props.keyword} value={this.props.keyword} onClick={(e)=>this.props.onClick(e,this.props.keyword)} className="removeButton">Remove</button>
onClick = (e,id) =>{
console.log(id);
}
Without manipulating onClick function
<button key={uuidv4()} id={this.props.keyword} value={this.props.keyword} onClick={this.props.onClick} className="removeButton">Remove</button>
onClick = (e) =>{
const id = e.target.getAttribute("id");
console.log(id);
}
You can do this:
function getId(id) {
console.log(id);
}
<button key={uuidv4()} id={this.props.keyword} value={this.props.keyword} onClick="getId(this.id)" className="removeButton">Remove</button>
if the function to handle the event is in the same component it is best to use the event.target.id, this will have its limitations when you try accessing a DOM element nested as a child component as event.target.id. Use event.currentTarget.id is nested,
This happens because event.currentTarget
can identify the element that caused the event deep down from the child as it burbles out, while event.target.id will set the target as the child component and will not be able to identify the child component that has that element since it target does not change and hence faulty.
You can watch this youtube video to understand better.
also read from their differences
Your onClick handler should receive the click event, which should include the id: event.target.id.
<button
key={uuidv4()}
id={this.props.keyword}
value={this.props.keyword}
onClick={(event) => /* you want to use event.target.id */}
className="removeButton">Remove</button>
This worked for me:
functionName = (event) => {
const id = event.target.id;
const value = event.target.value;
console.log("Value of the button: ", value)
console.log("Id of the button: ", id)}

Simulate click event on react element

The bounty expires in 7 days. Answers to this question are eligible for a +50 reputation bounty.
ajaykools wants to reward an existing answer:
Worth bounty, only way simulate clicks on dynamic elements like svg, g, circle, etc which are generated on page load.
I'm trying to simulate a .click() event on a React element but I can't figure out why it is not working (It's not reacting when I'm firing the event).
I would like to post a Facebook comment using only JavaScript but I'm stuck at the first step (do a .click() on div[class="UFIInputContainer"] element).
My code is:
document.querySelector('div[class="UFIInputContainer"]').click();
And here's the URL where I'm trying to do it: https://www.facebook.com/plugins/feedback.php...
P.S. I'm not experienced with React and I don't know really if this is technically possible. It's possible?
EDIT: I'm trying to do this from Chrome DevTools Console.
React tracks the mousedown and mouseup events for detecting mouse clicks, instead of the click event like most everything else. So instead of calling the click method directly or dispatching the click event, you have to dispatch the down and up events. For good measure I'm also sending the click event but I think that's unnecessary for React:
const mouseClickEvents = ['mousedown', 'click', 'mouseup'];
function simulateMouseClick(element){
mouseClickEvents.forEach(mouseEventType =>
element.dispatchEvent(
new MouseEvent(mouseEventType, {
view: window,
bubbles: true,
cancelable: true,
buttons: 1
})
)
);
}
var element = document.querySelector('div[class="UFIInputContainer"]');
simulateMouseClick(element);
This answer was inspired by Selenium Webdriver code.
With react 16.8 I would do it like this :
const Example = () => {
const inputRef = React.useRef(null)
return (
<div ref={inputRef} onClick={()=> console.log('clicked')}>
hello
</div>
)
}
And simply call
inputRef.current.click()
Use refs to get the element in the callback function and trigger a click using click() function.
class Example extends React.Component{
simulateClick(e) {
e.click()
}
render(){
return <div className="UFIInputContainer"
ref={this.simulateClick} onClick={()=> console.log('clicked')}>
hello
</div>
}
}
ReactDOM.render(<Example/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>
If you don't define a class in your component, and instead you only declare:
function App() { ... }
In this case you only need to set up the useRef hook and use it to point/refer to any html element and then use the reference to trigger regular dom-events.
import React, { useRef } from 'react';
function App() {
const inputNameRef = useRef()
const buttonNameRef = useRef()
function handleKeyDown(event) {
// This function runs when typing within the input text,
// but will advance as desired only when Enter is pressed
if (event.key === 'Enter') {
// Here's exactly how you reference the button and trigger click() event,
// using ref "buttonNameRef", even manipulate innerHTML attribute
// (see the use of "current" property)
buttonNameRef.current.click()
buttonNameRef.current.innerHTML = ">>> I was forced to click!!"
}
}
function handleButtonClick() {
console.log('button click event triggered')
}
return (
<div>
<input ref={inputNameRef} type="text" onKeyDown={handleKeyDown} autoFocus />
<button ref={buttonNameRef} onClick={handleButtonClick}>
Click me</button>
</div>
)
}
export default App;
A slight adjustment to #carlin.scott's great answer which simulates a mousedown, mouseup and click, just as happens during a real mouse click (otherwise React doesn't detect it).
This answer adds a slight pause between the mousedown and mouseup events for extra realism, and puts the events in the correct order (click fires last). The pause makes it asynchronous, which may be undesirable (hence why I didn't just suggest an edit to #carlin.scott's answer).
async function simulateMouseClick(el) {
let opts = {view: window, bubbles: true, cancelable: true, buttons: 1};
el.dispatchEvent(new MouseEvent("mousedown", opts));
await new Promise(r => setTimeout(r, 50));
el.dispatchEvent(new MouseEvent("mouseup", opts));
el.dispatchEvent(new MouseEvent("click", opts));
}
Usage example:
let btn = document.querySelector("div[aria-label=start]");
await simulateMouseClick(btn);
console.log("The button has been clicked.");
Note that it may require page focus to work, so executing in console might not work unless you open the Rendering tab of Chrome DevTools and check the box to "emulate page focus while DevTools is open".
Inspired from previous solution and using some javascript code injection it is also possibile to first inject React into the page, and then to fire a click event on that page elements.
let injc=(src,cbk) => { let script = document.createElement('script');script.src = src;document.getElementsByTagName('head')[0].appendChild(script);script.onload=()=>cbk() }
injc("https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js",() => injc("https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js",() => {
class ReactInjected extends React.Component{
simulateClick(e) {
e.click()
}
render(){
return <div className="UFIInputContainer"
ref={this.simulateClick} onClick={()=> console.log('click injection')}>
hello
</div>
}
}
ReactDOM.render(<ReactInjected/>, document.getElementById('app'))
} ))
<div id="app"></div>
Kind of a dirty hack, but this one works well for me whereas previous suggestions from this post have failed. You'd have to find the element that has the onClick defined on it in the source code (I had to run the website on mobile mode for that). That element would have a __reactEventHandlerXXXXXXX prop allowing you to access the react events.
let elem = document.querySelector('YOUR SELECTOR');
//Grab mouseEvent by firing "click" which wouldn't work, but will give the event
let event;
likeBtn.onclick = e => {
event = Object.assign({}, e);
event.isTrusted = true; //This is key - React will terminate the event if !isTrusted
};
elem.click();
setTimeout(() => {
for (key in elem) {
if (key.startsWith("__reactEventHandlers")) {
elem[key].onClick(event);
}
}
}, 1000);
Using React useRef Hooks you can trigger a click event on any button like this:
export default const () => {
// Defining the ref constant variable
const inputRef = React.useRef(null);
// example use
const keyboardEvent = () => {
inputRef.current.handleClick(); //Trigger click
}
// registering the ref
return (
<div ref={inputRef} onClick={()=> console.log('clicked')}>
hello
</div>
)
}
This answer was inspired by carlin.scott code.
However, it works only with focusin event in my case.
const element = document.querySelector('element')
const events = ['mousedown', 'focusin']
events.forEach(eventType =>
element.dispatchEvent(
new MouseEvent(eventType, { bubbles: true })
)
)

React - Prevent Event Trigger on Parent From Child

I have this scenario, where when parent element is clicked, it flips to show a child element with different colours. Unfortunately, when the user clicks on one of the colours, the 'click' event on parent is also triggered.
How can I stop the event trigger on parent when the child is clicked?
Possible solutions I am wondering:
CSS?
Append pointer-events : none class to the parent when the child is clicked. However, this would mean that the parent will need to be cleansed of the pointer-events class later.
Using Ref?
Record the ref of the parent React element & upon click on the child, compare the event.target against the ref? I don't like this because I don't like the global ref.
Thoughts and the better solution would be much appreciated. The question is:
How can I stop the event trigger on parent when the child is clicked?
You can use stopPropagation
stopPropagation - Prevents further propagation of the current event in
the bubbling phase
var App = React.createClass({
handleParentClick: function (e) {
console.log('parent');
},
handleChildClick: function (e) {
e.stopPropagation();
console.log('child');
},
render: function() {
return <div>
<p onClick={this.handleParentClick}>
<span onClick={this.handleChildClick}>Click</span>
</p>
</div>;
}
});
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
I had the same issue in React and solved it using the solution bellow:
if(e.currentTarget != e.target ) return;
.......
Another solution is to attach to the event callback on the parent the following:
if(event.target == event.currentTarget){
event.stopPropagation()
....
}
This way you can intercept events, that originated in the attached DOM node and unrelated events are relayed to the next node.
I wanted to invoke function on props but at the same time wanted to stop event propagation from child to parent, here is how its handled
class LabelCancelable extends Component {
handleChildClick(e) {
e.stopPropagation()
}
closeClicked(e, props) {
e.stopPropagation();
props.onCloseClicked()
}
render() {
const {displayLabel} = this.props;
return (
<span className={ "label-wrapper d-inline-block pr-2 pl-2 mr-2 mb-2" } onClick={ this.handleChildClick }>
<button type="button" className="close cursor-pointer ml-2 float-right" aria-label="Close"
onClick={(e) => this.closeClicked(e, this.props) }>
<span aria-hidden="true">×</span>
</button>
<span className="label-text fs-12">
{ displayLabel }
</span>
</span>
);
}
}
export default LabelCancelable;
I find this solution the cleanest. Thank you #JohnFash!
onClick={(event) => event.currentTarget == event.target && doSomething(event)}
Here is an attempt at explaining this in more details:
when your mouse enters the parent element, the currentTarget is set (event), then when it enters the child element, the target changes. If you don't do the check, the parent's onClick triggers because the mouseleave event hasn't triggered.
Old question but had trouble then solved it myself I just stopped the event on the 'child' div like so this then passed down through children, so you can place where you want this to stop a click event;
<div onClick={doThisClickEvent}>
<div onClick={(e) => e.stopPropagation()}>
<div>
<p>This now wont trigger click event</p>
</div>
</div>
</div>
I had this problem with Material-UI DataGrid and solved it using this:
event.defaultMuiPrevented = true;
e.g:
<DataGrid
onCellDoubleClick={(params, event) => {
if (!event.ctrlKey) {
event.defaultMuiPrevented = true;
}
}}
{...data}
/>

Close/hide an element when clicking outside of it (but not inside)

I have a <div> that exists on a page and I need to make it so that when the user clicks outside of that element it will become hidden, but if the user clicks somewhere within the element, then it should stay.
I tried using
e.stopPropagation();
and
e.preventDefault();
adding it to the click event of that certain DIV but that didn't work.
Thanks!
Assign the desired event listener (like "click") to document or window using EventTarget.addEventListener()
Use Event.target in combination with Element.closest() as negation ! - in order to check whether the Event.target (the element that initiated the Event) - its self or closest ancestor have a specific selector.
To control an element visibility create a CSS class that does the necessary styling, and use Element.classlist to add, remove or toggle that class (as needed).
const elPopup = document.querySelector("#popup");
addEventListener("click", (evt) => {
if (!evt.target.closest("#popup")) elPopup.classList.remove("isOpen");
});
#popup {
padding: 2rem;
background: gold;
display: none; /* Hidden popup by default */
}
#popup.isOpen {
display: block;
}
<div id="popup" class="isOpen">
Click outside to close me.<br>
Click inside will do nothing.
</div>
Never use Event.stopPropagation() unless you really, really know what you're doing. Your app or third-party code should be always notified about all events happening in their context.
Usage example: Close popup modal on click outside
Probably the easiest way to do this will be to monitor clicks on the entire document, and ignore it if it's not that element. If it is, then hide it.
(function(div) {
$(document).click(function(e) {
if (e.srcElement !== div) $(div).hide();
});
})($('div')[0]);
Edit: Derp, misunderstood, click inside should stay, otherwise hide... invert the equality check.
http://jsfiddle.net/robert/QcPx4/
useOuterClick
Hi . you can create custom hook like this:
export const useOuterClick = (elementRef, setElementVisibility) => {
useEffect(() => {
document.addEventListener('click', handleClick);
return () => document.removeEventListener('click', handleClick);
function handleClick(e: any) {
if (elementRef && elementRef.current) {
const ref: any = elementRef.current;
if (!ref.contains(e.target)) {
setElementVisibility(false);
}
}
}
}, [])};
then use it this way in your component:
import { useState, useRef } from 'react';
import useOuterClick from './hooks/useOuterClick';
export const SampleComponent = () => {
const [activeElement, setActiveElement] = useState(false);
const elementRef = useRef();
useOuterClick(elementRef, setActiveElement);
return (
<>
<div ref={elementRef}>
<button
onClick={() => setActiveElement(!activeElement)}>
'this button can open and close div'
</button>
{activeElement &&
<div>'this div will be hidden if you click on out side except
button'
</div>
}
</div>
</>
);
};

Categories

Resources