I'm creating a gameBoard, and having trouble toggling background colors for button divs. The toggle works for individual clicks on the "buttons" (styled divs), but when I click adjacent buttons it requires two clicks to get the next button to change its background color. How can I get adjacent buttons to toggle on first click? I've read some related posts like (Changing style of a button on click) but still struggling to get this working -
Related code below,
full code: https://jsfiddle.net/lydiademi/kt2qgfpr/
TY!
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
color: '#FFF',
color_white: true,
currentWord: '',
board1: [],
row1: ["aaafrs","aaeeee","aafirs","adennn","aeeeem"],
}
this.clicked = this.clicked.bind(this);
}
componentWillMount() {
let letter1 = '';
this.state.row1.forEach(die => {
letter1 = die[Math.floor(Math.random() * 6)].toUpperCase();
if (letter1 === 'Q') {
this.state.board1.push("Qu")
} else {
this.state.board1.push(letter1);
}
})
}
clicked(event) {
//change background color
let newColor= this.state.color === "#FFF" ? "#ACCEEC" : "#FFF";
this.setState({ color: newColor });
event.target.style.backgroundColor = newColor;
}
render () {
return (
<div id="board">
<div className="row">
{
this.state.board1.map((letter, index) => {
return (
<div className="btn" onClick={(e) => {this.clicked(e)}}>
{letter}
</div>
)
})}
</div>
)
}
Issue is :
You are maintaining one variable for all the elements bg toggle,
So the code is working as it should,
There is no need for maintaining state for that.
What you can do is :
Set extra attribute data-color like this
<div className="btn" data-color='#FFF' onClick={(e) => {this.clicked(e)}}>
And change bg color and attr based upon data-color , onClick like this
clicked(event) {
// get current color of element
let currentColor = event.target.attributes['data-color'].value;
// apply condition based upon currentColor
let newColor = currentColor === "#FFF" ? "#ACCEEC" : "#FFF";
// set the bg color
event.target.style.backgroundColor = newColor;
// change the data-color value to currentColor
event.target.setAttribute('data-color' , newColor);
// add letter to state.currentWord
let letter = event.target.innerText;
this.setState({ currentWord: this.state.currentWord + letter })
}
Here is the link to working fiddle :
https://jsfiddle.net/vivekdoshi2/kt2qgfpr/2/
Related
I'm new here and I have a question that I couldn't find answer to..
I am currently working on a website using ReactJS, and I want to have a button that fills itself whenever the user clicks on it. The button should have a total of 5 stages to it.
I am not asking for you to code it for me, but simply help me finding the best approach to this thing.
So what exactly am I asking? As you can see in this
It's a boxed element that whenever the user clicks on it (it can click on the whole element), the progress fills and it becomes something like this
So the first line is now marked. When the user presses on it again, the 2nd bar fills
Important - there will be text inside these bars that fills.
What have I done so far? I have been thinking of having 5 different images for every time the user presses on the element, but I was wondering if there might be a better approach to it (Like having the DIV background the image, and have sub-divs that fills up whenever the user presses... )
I hope I made myself clear, and thank you all for your time!
Here is a working example. You don't need an image for all of the different states. It is far more flexible to do this dynamically with HTML.
The key to this is keeping track of the number of times the button has been clicked. In this example it uses currentState to keep track of how many times it has been clicked.
const defaultStyle = {
width: 100,
padding: 5,
};
class MultiLevelButton extends React.Component {
constructor(props) {
super(props);
this.state = {
currentState: 0,
};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
const { currentState } = this.state;
if (currentState < this.props.states.length) {
this.setState({
currentState: currentState + 1,
});
}
}
reset() {
this.setState({
currentState: 0,
});
}
render() {
const { currentState } = this.state;
const { states } = this.props;
return (
<button onClick={this.handleClick} style={{border: 'none', outline: 'none'}}>
{
states
.map((s, i) => {
const stateNumber = states.length - i;
const overrides = stateNumber <= currentState ? { backgroundColor: '#000', color: '#fff' } : {};
const style = {
...defaultStyle,
backgroundColor: s,
...overrides,
};
return <div style={{...style}}>{stateNumber}</div>
})
}
</button>
)
}
}
const buttonRef = React.createRef();
ReactDOM.render(
<div>
<MultiLevelButton ref={buttonRef} states={['#bbb', '#ccc', '#ddd', '#eee', '#fff']} />
<MultiLevelButton states={['#fcc', '#cfc', '#ccf']} />
<div>
<button onClick={() => buttonRef.current.reset()}>Reset</button>
</div>
</div>,
document.getElementById('app')
);
<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="app" />
As you are using react. use create step/level as component and you will pass props of styles. you can map that component 5 times or n times depending upon requirement. The view you have shown no need images use css to achieve it.
change props of component when user click on it.
You can use state in order to keep the click count and change the button background on the basis of click count.
const colors = ["#dfddc7", "#f58b54", "#a34a28", "#211717"]; //color array
class App extends Component {
constructor(props) {
super(props);
this.state = {
index : 0
}
}
handleChange() { // function will be called whenever we click on button
let {index} = this.state;
if (index >= 5) {
return; // you don't want to change color after count 5
}
index++;
console.log(index);
this.setState({index})
}
render() {
const {index} = this.state;
return (
<div className="App">
<button style = {{background:colors[index]}} //we are setting dynamic color from array on the basis of index
onClick = {this.handleChange.bind(this)}> click to change images
</button>
</div>
);
}
}
You can place <div></div> inside a button with different background color instead of images.
In the following example, I hold the number of clicks in the state. By comparing this value with the index of the step, you can see if it needs to be green or transparent
const numberOfSteps = 5;
class App extends React.Component {
state = {
numberOfClicks: 0
};
handleClick = e => {
this.setState({
numberOfClicks: (this.state.numberOfClicks + 1) % (numberOfSteps + 1)
}); // Use module (%) to reset the counter after 5 clicks
};
render() {
const { numberOfClicks } = this.state;
const steps = Array(numberOfSteps)
.fill()
.map((v, i) => i)
.map((i, index) => {
const color = numberOfClicks >= index + 1 ? "limegreen" : "transparent";
return (
<div
style={{
backgroundColor: color,
height: "30px",
border: ".5px solid gray"
}}
>
Bar {index + 1}
</div>
);
});
return (
<button
className="button"
style={{ height: "200px", width: "200px", backgroundColor: "lightgreen" }}
onClick={this.handleClick}
>
{steps}
</button>
);
}
}
ReactDOM.render(<App />, document.getElementById("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">
<!-- This element's contents will be replaced with your component. -->
</div>
The key concept is state. When you click your button you should set some state, and when you render the button you should render it based on the state.
Here's a simple example where I render a button which contains 5 div elements that are filled (by setting backgroundColor style property) based on the state.
class Example extends Component {
constructor(props) {
super(props);
this.state = {
buttonState: 0,
};
}
onClick = () => {
let buttonState = this.state.buttonState;
buttonState++;
if(buttonState > 4) buttonState = 0; // wrap around from 4 - 0
this.setState({buttonState: buttonState});
}
// render a button element with some text, and a background color based on whether filled is true/false
renderButtonElement(elementText, filled) {
const backgroundStyle = filled ? {backgroundColor: 'green'} : {backgroundColor: 'white'};
const textStyle = {color: 'grey'};
return(
<div style={[backgroundStyle, textStyle]}>
<div>{elementText}</div>
</div>
);
}
render() {
return(
<button
onClick={this.onClick}
>
{/* make a temporary array of 5 elements, map over them and render an element for each one */}
{Array(5).fill().map((_, i) => this.renderButtonElement(i + 1, i <= this.state.buttonState))}
</button>
);
}
}
I have a column for buttons to toggle a modal. The problem is, I don't want to display the button for every single row. I only want to display the button on the first entry of the color.
Note that the colors are unpredictable (you don't know what colors will be displayed beforehand).
For example,
color toggler
black +
red +
red //don't display it here
yellow +
blue +
blue //don't display it here
blue //don't display it here
orange +
red +
black +
black //don't display it here
blue +
I have try to go through the document and some example, but I can't seem to find a solution to it (maybe something that I missed ?).
What I did was storing the first color in the state. Then I did with the theCheckFunc:
let flag = true
if (nextColor !== this.state.color)
this.setState({color: nextColor})
flag = false
return flag
Then in the columns I did.
Cell: props => (this.theCheckFunc(props) && <div onClick={somefunc}> + <div>)
However, everything seems to be frozen. The browser doesn't even respond.
Any good suggestion on how to do this ?
Don't use state with this, since you don't want to re-render based on new input. Instead, compute the array as part of the render.
For example, assuming that when you get to your render statement, you have a random array of colors like this:
['red', 'red', 'black', 'purple', 'purple']
Then this function could create the array you need with the data for render:
function getTableRowData(arr) {
let tableRowData = []
arr.forEach((color, n) => {
let toggler = true
if (n !== 0 && arr[n - 1] === color) {
toggler = false
}
tableRowData.push({ color, toggler, })
})
return tableRowData
}
Then you can iterate over the tableRowData in your render return and have it display the way you want to.
First set your color control variables in state or in class wherever you choose. In this example i'm choosing to control them over state.
constructor(props) {
super(props);
this.state = {
firstRedAlreadyHere: false,
firstBlueAlreadyHere: false,
firstGrayAlreadyHere:false,
....
...
}
}
then open a function to prepare a table. Later Use that function in render() to put table on component.
function putValuesToTable()
{
let table = [];
for (let i = 0; i < (YOUR_LENGTH); i++) {
{
let children = []; /* SUB CELLS */
/* IF RED COLOR IS NEVER CAME BEFORE, PUT A BUTTON NEAR IT */
if(!this.state.firstRedAlreadyHere)
children.push(<td>
<SomeHtmlItem></SomeHtmlItem></td> <td><button </button></td>)
/* ELSE DON'T PUT BUTTON AND CHANGE STATE. */
else
{
children.push(<SomeHtmlItem></SomeHtmlItem>);
this.state.firstRedAlreadyHere = true;
}
table.push(<tr>{children}</tr>);
}
}
return table;
}
I am changing state directly instead of this.setState(); because I don't want to trigger a refresh :). In render function, call putValuesToTable like this
render()
{
return (<div>
<table>
<tbody>
<tr>
<th>SomeParameter</th>
<th>SomeParameter2</th>
</tr>
{this.putValuesToTable}
</tbody>
</table>
</div>);
}
Use this example to extend your code according to your aim.
So i have an array like this const divs = ["Text 1","Text 2","Text 3"].
I create divs (a small menu) from this array in my render function
var createThreeDivs = divs.map((category) => {
return <div key={category} onClick={this.handleClick} className="myDivClass">{category}</div>
});
I want to style one of these divs when i click on them, and the remove the styling on the rest of them. So when i select one of the divs it gets a color and removes the color on the rest of them
In normal javascript with no virtual DOM i could do like this:
handleClick(e) {
//remove styling from others
var allDivs = document.getElementsByClassName("myDivClass");
for(var i = 0; i < allDivslength; i++) {
allDivs[i].classList.remove("myDivClass-styled");
}
//Add styling class to selected,
e.target.classList.add("myDivClass-styled");
}
But this manipulate the DOM directly. How do i do something like this in React?
I have seen examples of how this can be done using state with only one element and by not having an array creating the divs. But i can't come up with a good solution for this scenario. Any suggestions?
Using the component's state you can update the color based on the active div. Update the index of the active div when the user clicks, and when the index equals the div that was clicked on update the color of that div.
See example below.
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
active: 0
};
}
render() {
const divs = ["Text 1", "Text 2", "Text 3"];
const updateActiveDiv = (value) => {
this.setState(() => {
//this line will reset the value to
//null if same element is clicked twice
if(value === this.state.active) {
value = null;
};
return {
active: value
}
});
};
let divText = divs.map((div, i) => {
let color = this.state.active === i ? 'red' : 'black';
return <div style={{ color }} onClick={() => updateActiveDiv(i)}>{div}</div>;
});
return (
<div>
{ divText }
</div>
);
}
}
ReactDOM.render(<Example />, 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>
Pass current text from html
handleClick = (text)=>{
this.setState({activeText:text})
}
Inside create div function add class dynamically
Div className = { stat condition ? Class : no class }
On hover of any of the above items I want to surround item with a box having grey color background.
and onclick I want to change it to something like below (surround with a box having background color as text color, change text color to white and have a x in the box). Clicking on x should bring it back to the normal state as depicted in image 1.
Current code:
export default class GridSummary extends Component {
renderJobStateSummary() {
const jobCountSummaryDiv = [];
if (this.props.jobStateCount.size !== 0) {
jobCountSummaryDiv.push('Summary: ');
for (const state of ['Total', ...jobStatesPriorityOrder]) {
if (this.props.jobStateCount.has(state) &&
this.props.jobStateCount.get(state) !== 0) {
const cssClass = `${JOB_STATES_CSS_CLASS[state]} clickable`;
jobCountSummaryDiv.push(
<span
className={cssClass}
role="link"
tabIndex="-1"
key={state}
>
{JOB_STATE_DISPLAY_NAME[state]}: {this.props.jobStateCount.get(state)}
</span>
);
jobCountSummaryDiv.push(' | ');
}
}
}
return jobCountSummaryDiv;
}
render() {
return (
<div className="summary-panel">
{ this.renderJobStateSummary() }
</div>
);
}
}
Try with pseudo class combination of :hover and :active. For guest it likes button.
In style border option use combination of inset and outset, it gives very good visuel.
I have a div mapped so it would render any number of times according to data sent from the database. I'm setting a background color in the div when clicking on the div. How can i revert the changes if i click the div again?
My code
handleClick(id) {
let el = document.getElementById(id);
if (el) {
el.style.backgroundColor = "#0b97c4";
el.style.color = "#FFFFFF";
this.setState({
voteCount: this.state.voteCount + 1
})
}
this.props.submitvote(vote_object)
}
render() {
let {contents, submitvote, postId, voted_id} = this.props
return (
<div className="txt_vote_bar_div" id={this.props.contents.post_poll_content_id}>
<p className="txt_vote_choice" id={this.props.contents.post_poll_content_id}
onClick={() => {this.handleClick(this.props.contents.post_poll_content_id);}}>
{contents.content}
</p>
<p className="txt_tot_votes"> {this.props.contents.votes_percentage}%
({this.state.voteCount} Votes)
</p>
</div>
);
};
using the above code i'm changing the background of the div successfully. How can i remove the background color if i select the same div again (remove background color or change it to another color)
I would make txt_vote_choice "old" CSS name, and txt_vote_clicked is new className that you want to change. By onClick, toggle the className between these 2 classes would do the trick.
handleClick(id) {
let el = document.getElementById(id);
if (el) {
if el.class == "txt_vote_choice" ? "txt_vote_clicked": "txt_vote_choice";
this.setState({
voteCount: this.state.voteCount + 1
})
}
this.props.submitvote(vote_object)
}
render() {
let {contents, submitvote, postId, voted_id, voted_css} = this.props
return (
<div className="txt_vote_bar_div"
id={this.props.contents.post_poll_content_id}>
<p className={voted_css}
id={this.props.contents.post_poll_content_id}
onClick={() => {this.handleClick(this.props.contents.post_poll_content_id);}}>
{contents.content}
</p>
<p className="txt_tot_votes">
{this.props.contents.votes_percentage}%({this.state.voteCount} Votes)
</p>
</div>
);
};
Then update the CSS accordingly
if it is just an "on-off" feature, you can check for the "active" style already on your div like this : if (el.style.backgroundColor == "#0b97c4";) //if the style is already there: then you run some code that put your div the right color
if (el)
{
if (el.style.backgroundColor == "#0b97c4";) //if the style is already there:
{
el.style.backgroundColor = "#FF0000";
el.style.color = "#000000"; //put theese 2 colors to the "unactivated" ones
//other code when you unselect goes here like this.state.voteCount - 1
}
else
{
el.style.backgroundColor = "#0b97c4";
el.style.color = "#FFFFFF";
this.setState({
voteCount: this.state.voteCount + 1
})
}
}