React dynamically add html element with event handler - javascript

Is it possible to append buttons with event handler in react ?
// This is working fine but its pure javascript onclick , how can I make this element with react js onClick ?
This is reference code , my scenario is appending html code dynamically
const Button = () => {
const dynamicElement = () => {
let li = document.createElement('li');
li.className = 'dynamic-link'; // Class name
li.innerHTML = "I am New"; // Text inside
document.getElementById('links').appendChild(li);
li.onclick = function(){ alert(0) }
}
useEffect(() => {
dynamicElement();
}, [])
}

You can just convert your function to a functional component like this.
const DynamicElement = () => {
return (
<li onClick={()=>alert(0)} className="dynamic-link">I am New</li>
)
}
const Button = () => {
const [visible, setVisible] = useState(false);
useEffect(()=>setVisible(true), [])
// if visible is true return <DynamicElement/>, either way return null
return visible ? <DynamicElement/> : null
}
BTW useState is hooks implementation of a state

Related

what's the difference in using closure and private class in javascript

const BUTTON = document.querySelector('.clickBtn');
const P = document.querySelector('.click');
const click = (() => {
let click = 0;
return () => ++click;
})();
BUTTON.onclick = () => {
P.textContent = click();
};
const BUTTON2 = document.querySelector('.clickBtn2');
const P2 = document.querySelector('.click2');
class Click {
#click = 0;
click = () => ++this.#click;
}
const c = new Click();
BUTTON2.onclick = () => {
P2.textContent = c.click()
};
<button class="clickBtn">CLOSURE</button>
<p class="click"></p>
<button class="clickBtn2">CLASS</button>
<p class="click2"></p>
say I have a code like above, trying to manage real-time state in vanilla JS and also wanting to hide the actual data from exposing.
I don't need multiple method
nor do I need to copy multiple instances
in case like this, would there be any difference between closure and class except class being slightly more verbose?

Vanilla JS Reusable Dynamic Function taken as a string

I'm trying to make a reusable function that can take in a dynamic "action" for reusability...
In this case, adding the background color is the action.
const button = document.querySelector("#button");
const items = document.querySelectorAll(`*[id^="eventHandlerCreatedItem"]`);
var eventHandler = (refEl, event, focusEls, action) => {
refEl.addEventListener(`${event}`, () => {
focusEls.forEach((focusEl) => {
// focusEl.style.backgroundColor = "orange"; // works
focusEl.`${action}`; // doesn't work
});
});
};
eventHandler(button, "click", items, 'style.backgroundColor = "orange"');
Thanks!
Don't use a string for this. An "action" semantically describes a "function", use a function:
var eventHandler = (refEl, event, focusEls, action) => {
refEl.addEventListener(`${event}`, () => {
focusEls.forEach((focusEl) => {
action(focusEl);
});
});
};
eventHandler(button, "click", items, (el) => el.style.backgroundColor = "orange");

How to set state with a setInterval() when a button is clicked?

I'm trying to update the count of activeIndex within the setInterval when the checkbox is ticked. handleNext() and handlePrevious() are working fine when buttons are clicked but when the checkbox is checked the value of activeIndex is not getting updated in handleNext() but it's getting updated on the screen, so there is no condition check for activeIndex and it goes beyond 3.
import { useState } from "react";
import "./styles.css";
export default function App() {
const [slideTimer, setSlideTimer] = useState(null);
const [activeIndex, setActiveIndex] = useState(0);
const slideDuration = 1000;
const handleNext = () => {
if ((activeIndex) >= 3) {
setActiveIndex(0);
} else {
setActiveIndex(prev => prev + 1);
}
};
const handlePrev = () => {
if (activeIndex <= 0) {
setActiveIndex(3);
} else {
setActiveIndex((prev) => prev - 1);
}
};
const toggleSlider = () => {
if (slideTimer) {
clearInterval(slideTimer);
setSlideTimer(null);
return;
}
setSlideTimer(setInterval(handleNext, slideDuration));
};
return (
<div className="App">
<h1>{activeIndex}</h1>
<button onClick={() => handleNext()}>Next</button>
<button onClick={() => handlePrev()}>Previous</button>
<input onClick={() => toggleSlider()} type="checkbox" />
</div>
);
}
I have tried putting the code for toggleSlider inside useEffect() but that too is not working.
Your problem is that when handleNext gets defined (which occurs on every rerender), it only knows about the variables/state in its surrounding scope at the time that it's defined. As a result, when you queue an interval with:
setInterval(handleNext, slideDuration)
You will be executing the handleNext function that only knows about the component's state at that time. When your component eventually rerenders and sets a new value for activeIndex, your interval will still be exeucting the "old" handleNext function defined in the previous render that doesn't know about the newly updated state. One option to resolve this issue is to make the hanldeNext function not rely on state obtained from its outer scope, but instead, use the state setter function to get the current value:
const handleNext = () => {
setActiveIndex(currIndex => (currIndex + 1) % 4);
};
Above I've used % to cycle the index back to 0, but you very well can also use your if-statement, where you return 0 if currIndex >= 3, or return currIndex + 1 in your else.
I would also recommend that you remove the slideTimer. This state value isn't being used to describe your UI (you can see that as you're not using slideTimer within your returned JSX). In this case, you're better off using a ref to store your interval id:
const slideTimerRef = useRef(0); // instead of the `slideTimer` state
...
clearInterval(slideTimerRef.current);
slideTimerRef.current = null;
...
slideTimerRef.current = setInterval(handleNext, slideDuration);

Unable to obtain element through querySelectorAll("class") , class assigned in factory function

I created 3 buttons through factory function and assigned class "btn" to them and return the object, now I want to access all the button having class "btn" , but I am getting a empty NodeList
I want to store all the button having class "btn" in tabs variable
const tabButton = function(name)
{
const button = document.createElement("button") // button creation
button.innerText = name
button.classList.add("btn")
return {button}
}
const Header = function()
{
const homeButton = tabButton("Home").button
const MenuButton = tabButton("Menu").button
const ContactButton = tabButton("Contact").button
const tabs = document.querySelectorAll(".btn")
console.log(tabs) // giving NULL value
return {tabs}
}
Your tabButton function calls create the elements, but don't put them in the document anywhere. document.querySelectorAll only looks for elements that are in the document. If you put those buttons in the document, they'd be found by document.querySelectorAll, but there isn't a function that looks through every object everywhere in memory to see if it's a button that matches. There are only functions that look in documents or specific elements.
Here's an example showing the result before and after putting those buttons in the document:
const tabButton = function(name) {
const button = document.createElement("button");
button.innerText = name;
button.classList.add("btn");
return {button};
};
const Header = function() {
const homeButton = tabButton("Home").button;
const menuButton = tabButton("Menu").button;
const contactButton = tabButton("Contact").button;
console.log(document.querySelectorAll(".btn").length); // 0
document.body.appendChild(homeButton);
console.log(document.querySelectorAll(".btn").length); // 1
document.body.appendChild(menuButton);
console.log(document.querySelectorAll(".btn").length); // 2
document.body.appendChild(contactButton);
console.log(document.querySelectorAll(".btn").length); // 3
const tabs = document.querySelectorAll(".btn");
return {tabs};
};
Header();

Override a function already attached to an event

I want to override a function already attached to an event. Like that :
let orFn = () => {
console.log('old');
};
buttonEl.addEventListener('click', orFn);
orFn = () => {
console.log('new');
};
However, when I click on the button, the old function is still called.
I have seen this stackoverflow. I can't use a function wrapper in my case. And I want to understand why this is not working.
My code is available for testing here: jsfiddle.
One way is to remove first listener, then add another one
let orFn = () => {
console.log('old');
};
let orFnNew = () => {
console.log('new');
};
var buttonEl = document.querySelector('button')
buttonEl.addEventListener('click', orFn);
// remove old listener
buttonEl.removeEventListener('click', orFn);
// add another one
buttonEl.addEventListener('click', orFnNew);
<button>button</button>
Another way is to have one listener, that may call different functions inside. Example:
// button
const buttonEl = document.querySelector('button')
const orFn = () => {
console.log('old');
};
const orFnNew = () => {
console.log('new');
};
// function that will be called
let functionToCall = orFn;
buttonEl.addEventListener('click', (event) => { // single listener
functionToCall.call(event); // call a function with event
functionToCall = functionToCall === orFn ? orFnNew : orFn; // change function to be called
});
<button>button</button>
One way of using bind().
const btn = document.getElementById('btn');
let orFn = () => {
alert('old');
};
orFn = () => {
alert('new');
};
orFn.bind(orFn)
btn.addEventListener('click', orFn);
This will bind with new function and show new in alert Popup.

Categories

Resources