React document.addEventListener fire immediately - javascript

I have search about this problem.
It must be pass function reference.
document.addEventListener("click", () => {}, false)
But what I do is still fire immediately.
Do I make some wrong?
Test code is below
export default function App() {
const start = () => {
console.log("start");
};
const click1 = () => {
document.addEventListener("click", start);
};
const click2 = () => {
const element = document.getElementById("p");
element.addEventListener("click", start);
};
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<button onClick={click1}>Click1</button>
<hr />
<p id="p">123</p>
<button onClick={click2}>Click2</button>
</div>
);
}
// Click1 - immediately, problem here
// Click2 - no immediately
https://codesandbox.io/s/reverent-grass-5fok7b?file=/src/App.js
What I expect
click first, add click event, no anything is log.
click second, log 'start'
actually output
click first, add click event, log start.

You're handling a SyntheticEvent with your onClick handler and adding a native event listener at the document level. Adding the listener will complete before the event bubbles up to the top therefore you also see it execute for the original event.
If you don't want that to happen, stop the event from bubbling
const click1 = (e) => {
e.stopPropagation();
document.addEventListener("click", start);
};
FYI, you should also remove the DOM listener when your component unmounts
useEffect(() => () => document.removeEventListener("click", start), []);

Another option you might consider, keeping in mind the React paradigm to only attach listeners using React when possible, would be to attach the click handler to your App, rather than to the document with .addEventListener. (Best to only use addEventListener when there's no alternative with React handlers.)
function App() {
const [started, setStarted] = React.useState(false);
const tryLog = () => {
if (started) {
log();
}
};
const log = () => console.log("event");
return (
<div className="App" onClick={tryLog}>
<h1>Hello CodeSandbox</h1>
<button onClick={() => setStarted(true)}>Click1</button>
<hr />
<p id="p">123</p>
<button onClick={log}>Click2</button>
</div>
);
}
ReactDOM.createRoot(document.querySelector('.react')).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='react'></div>

Related

setTimeout macrotask in the UI event handler

i wrote following simple react component
in the onBlur event handler, i create a new macrotask by setTimeout
function App() {
const onBlur=()=>{
console.log('onBlur')
setTimeout(() => {
console.log('setTimeout')
}, 0);
}
const onClick=()=>{
console.log('onClick')
}
return (
<div className="App">
<div id="first" onClick={onClick}>aaa</div>
<div id="second" contentEditable onBlur={onBlur} suppressContentEditableWarning>qq</div>
</div>
);
}
and then
1、i move the mouse pointer into second div
2、then i click the first div
it print out with following sequence in the console
onBlur
setTimeout
onClick
my question is why setTimeout displays before onClick;
apprecaite your answers; please help me; thank you very much;
Because the blur event is fired from the mousedown event, while the click event is made of both mousedown and mouseup.
const d1 = document.querySelector("div");
const d2 = document.querySelector("div[contenteditable]");
d1.addEventListener("mousedown", ({type}) => {
console.log(type);
setTimeout(() => console.log("timeout from %s", type), 0);
});
d1.addEventListener("click", ({type}) => console.log(type));
d1.addEventListener("mouseup", ({type}) => console.log(type));
d2.addEventListener("blur", ({type}) => {
console.log(type);
setTimeout(() => console.log("timeout from %s", type), 0);
});
d2.focus();
/* expected output:
mousedown
blur
timeout from mousedown
timeout from blur
mouseup
click
*/
<div>CLICK ME</div>
<div contenteditable>focused element</div>
So by the time you release your mouse pointer, the blur event has already been fired and the 0 timeout also had time to get executed, unless if you can do so in less than one ms in Chrome, where they have a 1ms minimum timeout.

React : printing all past state when updating state

I'm curioused about the work of this code.
It's a simple code, I intended to raise counter for 1 and print on console.
but when I click the button the counter increases, also printing from 0 ~ to all the number that I increased.
run screenshot
Could you explain why this happens?
import { useState } from "react";
function App() {
const [counter, setCounter] = useState(0);
const onClick = () => {
window.addEventListener("click", () => {
console.log(counter);
});
setCounter((counter) => counter + 1);
};
return (
<div className="App">
<button onClick={() => onClick()}>Add & Print!</button>
<div>{counter}</div>
</div>
);
}
export default App;
You added onClick event to button tag and when the function is executed, onClick event to window is added. So whenever clicking button tag, addEventListener that order to add shows console.log is made. On the code, showing console.log doesn't need addEventListener because button tag already has onClick event.
In <button onClick={() => onClick()}>Add & Print!</button> tag, onClick event is executed whenever the tag is clicked,
Below,
window.addEventListener("click", () => {
console.log(counter);
});
addEventListener adds event.
So whenever clicking button tag, addEventListener is executed(that adds events).
import { useState } from "react";
function App() {
const [counter, setCounter] = useState(0);
const onClick = () => {
// doesn't need window.addEventListener
// window.addEventListener("click", () => {
console.log(counter);
// });
setCounter((counter) => counter + 1);
};
return (
<div className="App">
<button onClick={() => onClick()}>Add & Print!</button>
<div>{counter}</div>
</div>
);
}
export default App;
On every click you are adding an event listener. So on first click there is one event listener, on second 2, on third three and so on. (on nth click, n event listeners are there on window object).
There is also the situation of the event listener using stale state. The window event listener has closed over(closures) the old value of state and is logging it. So the first event listener is always using count = 0. The third event listener is using count = 2. So, on.

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

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

Fire event listener on element not rendered in React

I have added an external script to my index.html that renders a widget inside my react component (it will render the widget where ever I wanted to. It's not a reacting component.). I'm trying to render the widget inside the id renderDivHere.
handleClick() {
console.log("clicked on widget button")
}
render () {
<div>
......
......
<div id="renderDivHere" /> // the external scripts will render the widget here
......
</div>
}
The widget contains some graphs, data and a button with id="wl-nav-button". I want to listen the click events in #wl-nav-button and call this.handleClick() function.
I tried jquery.click() and document.getElementById(wl-nav-button).addEventListener("click", function(){this.handleClick()});. But both are returning errors.
Help me out on this issue.
Try with the following code:
componentDidMount() {
document.addEventListener("click", this.navButtonListener);
}
componentWillUnmount() {
document.removeEventListener("click", this.navButtonListener);
}
navButtonListener = (e) => {
if(e.target.id == "wl-nav-button"){
this.handleClick();
}
}
What this does is it adds an event listener to the entire document, which is fired anytime you click. Then, a check is done to see what the clicked items id attribute is set to. If the id is your button, it fires the handleClick() function.
This also removes the event listener from your component before un-mounting it.
Demo
Here is a simple demo that just demonstrates how to add an event handler in React to something that was not rendered with React.
class MyApp extends React.Component {
componentDidMount() {
document.addEventListener("click", this.navButtonListener);
}
componentWillUnmount() {
document.removeEventListener("click", this.navButtonListener);
}
navButtonListener = (e) => {
if (e.target.id == "wl-nav-button") {
this.handleClick();
}
}
handleClick() {
console.log("click!!");
}
render() {
return (
<div>
<p>A button from a simulated external non-react library will be inserted asynchronically below:</p>
<div id="my-container"></div>
</div>
);
}
}
ReactDOM.render(<MyApp />, document.getElementById("myApp"));
//simulate a non-react library asynchronically inserting into the DOM:
setTimeout(function() {
var button = document.createElement("BUTTON");
var text = document.createTextNode("Click me!");
button.appendChild(text);
button.setAttribute("id", "wl-nav-button");
document.getElementById("my-container").appendChild(button);
}, 2000);
<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="myApp"></div>

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 })
)
)

Categories

Resources