I want to create a button that will hide each ticket and one general button that will restore them all.
this is the Code:
return (
<ul className="tickets">
{filteredTickets.map((ticket) => (
<li key={ticket.id} className="ticket">
<h5 className="headline">{ticket.headline}</h5>
<p className="text">{ticket.text}</p>
<footer>
<div className="data">
By {ticket.address} | {new Date(ticket.time).toLocaleString()}
</div>
</footer>
</li>
))}
</ul>
);
here is an example of what you want!
you have to replace myFunction() for your button and myDIV into your element that you want to hide it!
<button onclick="myFunction()">Click Me</button>
function myFunction() {
var x = document.getElementById("myDIV");
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
for react =
const [visible, setVisible] = useState(true)
here is for button
<button onlick={() =>setVisible(!visible)}>hide/show
here is a demo in JS, modify to what you want exactly
<ul class="ticket">
<li>
<p>hey, I'm a P</p>
<div class="data">I'm a Div</div>
</li>
</ul>
.hide {display:none}
const generalBtn = document.getElementById(`btn`);
const divContainer = document.querySelector(`.ticket`);
const eachDiv = divContainer.getElementsByClassName(`data`);
generalBtn.addEventListener(`click`, () => {
[...eachDiv].forEach((div) => {
div.classList.toggle(`hide`);
});
});
There is a good solution in your case but as mentioned in the comments, it needs to manipulate the filteredTickets array.
You need to add a property/value to each item of filteredTickets to track or change their state. For example, it can be isVisible property which is a boolean with false or true value.
Now, isVisible value will determine the behavior. let's modify the ticket:
const handleHideTicket = (id) => {
// find selected ticket and change its visibility
const updatedFilterdTickets = filteredTikcets.map(ticket => (ticket.id === id ? {...ticket, isVisible: false} : ticket))
// now the updatedFilterdTickets need to be set in your state or general state like redux or you need to send it to the server throw a API calling.
}
return (
<ul className="tickets">
{filteredTickets.filter(ticket => ticket.isVisible).map((ticket) => (
<li key={ticket.id} className="ticket">
<h5 className="headline">{ticket.headline}</h5>
<p className="text">{ticket.text}</p>
<footer>
<div className="data">
By {ticket.address} | {new Date(ticket.time).toLocaleString()}
</div>
// add a button to control visibility of each ticket
<button onClick={() => handleHideTicket (ticket.id)}> click to hid / show </button>
</footer>
</li>
))}
</ul>
);
Explanation:
a new button added to each ticket and pass the handleHideTicket handler to it. If the user clicks on this button, the handler finds that ticket and sets the isVisible property to the false.
On the other hand, we can remove the hidden tickets by applying a simple filter method before map method. so only visible tickets will be displayed.
Now, create a general button to show all the tickets. In this case, you need a handler function that sets all ticket's isVisible value to true
const handleShowAllTickets = () => {
const updatedFilteredTickets = filteredTickets.map(ticket => ({...ticket, isVisible: true}))
// now put the updatedFilteredTickets in your general store/post an API call/ update state
}
Note: as I mentioned in the code's comments, you need to update your filteredTickets array after changing via handlers to reflect the changes in your elements.
Related
setIsStarted(true)
setCurrentScore(0)
setResultCard(false)
setCurrentQuestion(0)
}
const checkOption = (isCorrect) =>{
setDisability(true)
if(isCorrect){
setCurrentScore(currentScore+1);
}
setCurrentQuestion(currentQuestion+1);
if(currentQuestion==questions.length-1){
setShowResult(true);
}
}
function showResultButton(showResult){
if (showResult) {
return <button className='show-result' onClick={showResultCard}>Show results</button>
}
}
const showResultCard = () =>{
setShowResult(false)
setResultCard(true);
setIsStarted(false)
}
function displayResultCard(resultCard){
if(resultCard){
return <div className='score-card'>You have answerd {currentScore}/5 correctly</div>
}
}
const updateDisability = () =>{
setDisability(false)
}
function DisplayQuesion(){
const quesList = questions.map((element) => {
return(
<div className='question-card'>
<h2>{element.text}</h2>
<ul>
{element.options.map((option) => {
return <button key={option.id} onClick={() => checkOption(option.isCorrect)} disabled={updateDisability}>{option.text}</button>
})}
</ul>
</div>
)
})
return (<div>{quesList}</div>)
}
In this code my all options are getting disabled even after clicking one option.When an option for a question is clicked, all other buttons for the question are disabled. when all questions are answered, the show results button gets shown.
This happens because you're not splitting down the list of questions in child components and so on.
You need to create three components to achieve this:
Questionary
Question
Answer
By doing so, you'll be able to define a separate state for each question/answer so they don't get mixed up.
Within your code, notice that if you click on an item it will update the state of the entire component which consequentially will update also all the other answers because they shared the same state.
You can check this link to have a better understanding:
https://www.pluralsight.com/guides/passing-state-of-parent-to-child-component-as-props
I created a filter gallery. I want to animate the filter items every time I click to a buttons. But my codes are not doing it properly. It animates filter items like toggle. If I click on a button first time it animates items, then If I click on another button it shows nothing. After that If I click on another button it animates again. What's wrong with my code? Experts please help me to find out the proper solution. Thanks in advance.
Here is my code:
import React, { useState } from 'react';
import suggestData from '../data/suggest-data.json';
const allCategories = ['All', ...new Set(suggestData.map(item => item.area))];
const Suggest = () => {
const [suggestItem, setSuggestItem] = useState(suggestData);
const [butto, setButto] = useState(allCategories);
const [selectedIndex, setSelectedIndex] = useState(0);
const [anim, setAnim] = useState(false);
const filter = (button) => {
if (button === 'All') {
setSuggestItem(suggestData);
return;
}
const filteredData = suggestData.filter(item => item.area === button);
setSuggestItem(filteredData);
}
const handleAnim = () => {
setAnim(anim => !anim);
}
return (
<div>
<h1>Suggest</h1>
<div className="fil">
<div className="fil-btns">
<div className="fil-btn">
<button className='btn'>Hello</button>
{
butto.map((cat, index) => {
return <button type="button" key={index} onClick={() => { filter(cat); setSelectedIndex(index); handleAnim(); }} className={"btn" + (selectedIndex === index ? " btn-active" : "")}>{cat}</button>
})
}
</div>
</div>
<div className="fil-items">
{
suggestItem.map((item, index) => {
return (
<div className={"fil-item" + (anim ? " fil-item-active" : "")} key={index}>
<h1>{item.name}</h1>
<h2>{item.category}</h2>
<h3>{item.location}</h3>
<h4>{item.type}</h4>
<h5>{item.area}</h5>
</div>
);
})
}
</div>
</div>
</div>
);
}
export default Suggest;
In your handleAnim() function, you are simply toggling the value of anim state. So initially, its value is false and when you click the button for the first time, it is set to true. On clicking the next button, the anim state becomes false because the value of !true is false and hence your animation doesn't work. On next click, becomes true again since !false is true and the toggle continues again and again.
If you want to make your animations work on every click you will need to set the anim state value to true on every button click as below since you seem to depend on the value to set animations. As an alternative, I think it will do just fine if you simply add the animation directly to the enclosing div with class .filter-item instead of relying on states to trigger the animation since after every filter you apply, the elements will be re-rendered and the animation will happen after every re-render.
const handleAnim = () => {
setAnim(true);
}
I am trying to change a div's visibility from hidden to visible using button click. But even when I am clicking the button, the visibility is not changing. I have logged the console after the clickHandler, and it still says false even after I set it to true inside the function. So far, I have this,
let clicked = false;
//change image on fortune cookie click
const clickHandler = (e) => {
e.target.setAttribute(
"src",
"https://i.ibb.co/cksx7kr/kisspng-fortune-cookie-drawing-clip-art-fortune-cookie-5b237469209879-9079770415290502171335.png"
);
clicked = true;
};
console.log(clicked);
return (
<div className="App">
<button onClick={clickHandler}>
<img
className="cookie"
src="https://i.ibb.co/9YQV2qq/kisspng-fortune-cookie-biscuits-bakery-drawing-clip-art-fortune-cookies-5b0ec5e3013c23-3980054715276.png"
/>
</button>
<div
className="fortuneMessage"
style={{ visibility: clicked ? "visible" : "hidden" }}
>
{fortune}
</div>
</div>
);
When you are using React, you need a state in order to have a DOM update. That would fork for you (don't forget to import useState at the top of your file) :
const [clicked, setClicked] = useState(false);
const [src, setSrc] = useState("https://i.ibb.co/9YQV2qq/kisspng-fortune-cookie-biscuits-bakery-drawing-clip-art-fortune-cookies-5b0ec5e3013c23-3980054715276.png");
//change image on fortune cookie click
const clickHandler = (e) => {
setSrc("https://i.ibb.co/cksx7kr/kisspng-fortune-cookie-drawing-clip-art-fortune-cookie-5b237469209879-9079770415290502171335.png");
setClicked(true);
};
console.log(clicked);
return (
<div className="App">
<button onClick={clickHandler}>
<img
className="cookie"
src={src}
/>
</button>
<div
className="fortuneMessage"
style={{ visibility: clicked ? "visible" : "hidden" }}
>
{fortune}
</div>
</div>
);
You'll need to use state instead of the plain clicked variable.
const [clicked, setClicked] = useState(false);
Call the setState function inside the clickHandler function instead of setting clicked to true.
setClicked(true);
in React, the variable is not two way binding.
The clicked variable in your function will only get to be true in the function itself but not outside the function
So in this case you need to use useState so that the clicked state will be re-rendered again with updated boolean
const [clicked, setClicked] = useState(false)
const clickHandler = () => {
setClicked(true)
}
so that your clicked variable will be updated, hence the css will be updated again.
for more reference with useState can check their documentation https://reactjs.org/docs/hooks-state.html
i want to improve my code, with several buttons that has custom class names (attr), when clicked should add to body tag (toggle), now is adding the first button only because for ("button")[0] but should work for each button
import React, { useState, useEffect } from "react"
function Test() {
const [isClass, setIsClass] = useState(false)
useEffect(() => {
const x = document.getElementsByTagName("button")[0].getAttribute("custom-class")
document.body.classList.toggle(x, isClass)
}, [isClass])
return (
<>
<button custom-class='test1' onClick={() => setIsClass(!isClass)}>
Setting test1 className
</button>
<button custom-class='test2' onClick={() => setIsClass(!isClass)}>
Setting test2 className
</button>
</>
)
}
export default Test
Thanks
Please use this code.
let oldStyle = "";
const handleClick = (index) => {
const x = [...document.getElementsByTagName("button")].map(value => value.getAttribute("custom-class"));
document.body.classList.contains(x[index]) ? document.body.classList.remove(x[index]) : document.body.classList.add(x[index]);
if(document.body.classList.length > 1) document.body.classList.replace(oldStyle, x[index]);
oldStyle = x[index];
}
return (
<>
<button custom-class='test1' onClick={() => handleClick(0)}>
Setting test1 className
</button>
<button custom-class='test2' onClick={() => handleClick(1)}>
Setting test2 className
</button>
</>
)
It is better not to use DOM querying and manipulation directly with elements that are created and controlled by react. In your particular example it is ok to use document.body, but not ok to search for buttons, especially when you try to find them by tag name. To actually toggle a class in classList you don't need second parameter in most cases, so additional state is also not needed.
React way to get reference to element renderend by React would be to use Ref. However, in your particular case side effect can be launched inside event handler, so you don't need useEffect or useRef.
Your onClick handler can accept event object that is Synthetic Event. It holds property target that holds reference to your button.
So, the easiest way would be simply to write like this:
function Test() {
function clickHandler(event) {
let classToToggle = event.target.getAttribute("custom-class");
document.body.classList.toggle(classToToggle);
}
return (
<>
<button key="test1" custom-class="test1" onClick={clickHandler}>
Setting test1 className
</button>
<button key="test2" custom-class="test2" onClick={clickHandler}>
Setting test2 className
</button>
</>
);
}
export default Test;
If you need to have only single className from the list, you can decide which class to enable or disable with a bit of a state. Since anything can add classes on body it might be useful to operate only on some set of classes and not remove everything.
Also, not mentioned before, but consider using data attribute as its purpose is to keep some additional data.
function Test() {
// this can come from props or be hardcoded depending on your requirements
// If you intend to change it in runtime, consider adding side effect to cleanup previous classes on body
let [classesList] = React.useState(["test1", "test2"]);
let [activeClass, setActiveClass] = React.useState("");
// You can switch actual classes in effect, if you want to
function clickHandler(event) {
let classToToggle = event.target.dataset.customClass;
// we remove all classes from body that are in our list
document.body.classList.remove(...classesList);
if (activeClass === classToToggle) {
setActiveClass("");
} else {
// if class not active - set new one
document.body.classList.add(classToToggle);
setActiveClass(classToToggle);
}
}
return (
<>
{classesList.map((cn) => (
<button key="cn" data-custom-class={cn} onClick={clickHandler}>
Setting {cn} className
</button>
))}
</>
);
}
I have a functional component in react that receives an array with numeric values.
For each value I build a div and place them next to each other in a flexbox.
When I click on a div I have to select it by adding or removing the class selection-bar-selected which simply changes the color of a bar placed at bottom of the div.
What I did works but it selects all the divs, how can I make it select only the div I clicked?
Here is my code and thanks in advance for the answers.
const [selected, setSelected] = useState(false);
const SelezionaTab = () =>{
selected ? setSelected(false) : setSelected(true);
}
return (
<div className="months-tab">
{props.data.map((v, k) =>
<div className="single" key={k} id={"tab-"+k} onClick={SelezionaTab}>
<div className="single-header">
<p className="mese">{k}</p>
</div>
<div className="single-body">
<div className="value">
<p className="numero-spese">{v.doc} doc.</p>
<p className="importo">{v.val}</p>
</div>
<div className={selected ? "selection-bar selection-bar-selected" : "selection-bar"}>
</div>
</div>
</div>
)}
</div>
)
You need to distinguish which tab is selected in the selected state, so for example you can change its value from being a boolean to a number or string that holds the index or name or id of the currently selected tab(s), so:
const [selected, setSelected] = useState({});
// This is really `ToggleTab` rather than `SelezionaTab`
const SelezionaTab = (tabId = null) => {
setSelected({ ...selected, [tabId]: !selected[tabId] })
}
// in the component
onClick={() => SelezionaTab(k)}
// and the selected class becomes:
<div className={selected[k] ? "selection-bar selection-bar-selected" : "selection-bar"}>
The selected state that you are storing could be the id or index of the element. Then when you loop over the list you only change the class when the id or index matches currently selected.
Right now you are only suggesting that there is something selected, but true/false doesn’t tell you which item in the list.
You are very close otherwise.