toggle between two button style in react js - css

I want the button's style to change when I click on them.
I want the option 1 button to be selected and background color to change to by default. If I click on the option 2 button, I want the option 2 button to change and unchanged option 1.
I already used the method found in this post
However, the method doesn't seems to be working correctly since the color of my buttons is the default HTML button color.
My code:
import React, {Component} from 'react';
import './OptionButtons.css'
export class OptionButtons extends Component{
constructor() {
super();
this.state = {
selected: "btn1"
};
}
changeColor = (btn) => {
this.setState({ selected: btn });
};
addClass = (btn) => {
if (this.state.selected === btn) return "selected";
else return "notSelect";
};
render() {
return (
<div class = "option">
<h2> Options </h2>
<div class = "buttons">
<button id = "option1Btn" className = {this.addClass("btn1")} onClick = {this.changeColor.bind(this, "btn1")}> Option 1 </button>
<button className = {this.addClass("btn2")} onClick = {this.changeColor.bind(this, "btn2")}> Option 2 </button>
</div>
</div>
);
}
}
OptionButtons.css
.option {
box-sizing: border-box;
position: relative;
margin-top: 655px;
margin-left: auto;
margin-right: auto;
width: 80%;
max-width: 650px;
}
.option .buttons {
flex: 20%;
display: flex;
justify-content: center;
align-items: center;
}
.option .buttons button {
flex-direction: row;
border-radius: 5px;
padding: 0 1.3rem;
font-family: 'Nunito';
font-style: normal;
font-weight: 700;
font-size: 1.2rem;
line-height: 27px;
text-align: center;
width: 320px;
height: 40px;
left: 50px;
cursor: pointer;
}
#option1Btn{
margin-right: 10px;
}
.selected {
color: "#fff";
background-color: "#00867D";
border: 1px solid "#00867D";
}
.notSelected {
color: "#00867D";
background-color: "#F2F2F2";
border: 1px solid "#F2F2F2";
}
Result of my code

I am not sure. did you say that when you click button 2, Its color will be changed and button 1 will be unchanged(active always)? or button 1 will be inactive?
https://codesandbox.io/s/priceless-tamas-yjr1lw?file=/src/Some.js
check it, do you want like this?

You can easily change colors on button clicks in react:
const [backgroundColor, setBackgroundColor] = useState("white");
const [buttonColors, setButtonColors] = useState({
color1: "red",
color2: "green"
});
const button1Clicked = () => {
setBackgroundColor("gray");
setButtonColors({
...buttonColors,
color1: "green"
});
};
const button2Clicked = () => {
setBackgroundColor("green");
setButtonColors({
...buttonColors,
color2: "red"
});
};
return (
<div
className="App"
style={{
backgroundColor
}}
>
<button
style={{ backgroundColor: buttonColors.color1 }}
onClick={button1Clicked}
>
Button 1
</button>
<button
style={{ backgroundColor: buttonColors.color2 }}
onClick={button2Clicked}
>
Button 2
</button>
</div>
);
You can find a working example here.

There are a couple things that are not ok with your react code and CSS.
Number one you have elements with the property class instead of className.
Second, the color codes you are trying to attribute are strings ("#000"), so your browser doesn't know what to do with them. They should either be clear hexadecimals or using the rgb function.
I'll leave a Codesandbox using your component/CSS so you can take a look.

I think what you are doing is just wrong and very confusing as there is a very elegant way to do it.
import React, {
Component
} from 'react';
import './OptionButtons.css'
export class OptionButtons extends Component {
constructor() {
super();
this.state = {
selected: "btn1" //make this state a boolean so that it will turn true when you press button 1 and false when you press button 2 or vice versa
};
}
changeColor = (btn) => {
this.setState({
selected: btn
});
};
//I have read it many times but I still don't understand what this function does in the code. In my opinion, it has no value to the action you are trying to achieve.
addClass = (btn) => {
if (this.state.selected === btn) return "selected";
else return "notSelect";
};
render() {
return ( <
div class = "option" >
<
h2 > Options < /h2> <
div class = "buttons" >
//it its better and easy to use a turnery operator rather than a function
<
button id = "option1Btn"
className = {
this.addClass("btn1")
}
onClick = {
this.changeColor.bind(this, "btn1")
} > Option 1 < /button> <
button className = {
this.addClass("btn2")
}
onClick = {
this.changeColor.bind(this, "btn2")
} > Option 2 < /button> <
/div> <
/div>
);
}
Instead, you can do like this
import React, {
Component
} from 'react';
import './OptionButtons.css'
export class OptionButtons extends Component {
constructor() {
super();
this.state = {
selected: null //null so that when you click, you can assign a boolean value
};
}
changeColorBtn1 = () => {
this.setState({
selected: true
})
}
changeColorBtn2 = () => {
this.setState({
selected: false
})
}
render() {
return ( <
div class = "option" >
<
h2 > Options < /h2> <
div class = "buttons" >
//I'm using turnery expression which means if selected: true then classname will be btn1
<
button id = "option1Btn"
className = {
this.state.selected && 'btn1'
}
onClick = {
this.changeColorBtn2.bind(this, "btn1")
} > Option 1 < /button>
//same as I did in the previous button but it's vice-versa
<
button className = {!this.state.selected && 'btn2'
}
onClick = {
this.changeColorBtn2.bind(this)
} > Option 2 < /button> <
/div> <
/div>
);
}
I hope it answers your question

Related

CSS hover applies to child elements (absolute positioned) also however I want it to affect only the parent container

I have a header with lots of navItems, for the last navItem, I have another expander div attached which also captures the hover effect of the parent div. How can I disable the pointer-events to none for this child absolute expander?
If I set pointer event of the absolute expander to none then the elements inside of expander also lose hover effect. (Note that they're also of same hover class).
Here's my JSX (HTML like template):
import React from 'react';
import classes from './NavItems.module.css';
import navItems from '#/constants/navItems.json';
import { BiChevronDown } from 'react-icons/bi';
import { useAppDispatch, useAppSelector } from '#/redux/hooks';
import { updateActiveSlice } from '#/slices/navigation.slice';
import { motion } from 'framer-motion';
const getActiveItem = (activeItem: string) => {
const result = navItems.find((element) => element.value === activeItem);
return result;
};
const moreItems = {
value: 'more',
label: 'More',
index: -1
};
const getItems = (activeItem: string) => {
const activeElement = getActiveItem(activeItem);
if (navItems.length > 5 && activeElement) {
if (activeElement.index > 3) return [...navItems.slice(0, 4), activeElement];
return [...navItems.slice(0, 4), moreItems];
}
return navItems;
};
export default function NavItems() {
const { activeSection } = useAppSelector((state) => state.navigation);
const dispatch = useAppDispatch();
const items = getItems(activeSection);
const activeItem = getActiveItem(activeSection);
return (
<div className={classes.NavItemContainer}>
{items.map((item, index) => {
const isLastItem = index === items.length - 1;
const isActive =
(activeItem ? activeItem.value : activeSection) === item.value;
return (
<h4
key={item.value}
className={[
classes.NavItem,
isLastItem ? classes.LastItem : null,
isActive ? classes.ActiveItem : null
].join(' ')}
onClick={() => {
// TODO: verify more click
dispatch(updateActiveSlice(item.value));
}}
>
{item.label}
{isLastItem && (
<React.Fragment>
<BiChevronDown className={classes.ShowMoreIcon} />
<motion.div
initial={{ y: 50 }}
animate={{ y: [-50, 20, 0] }}
className={classes.Expander}
>
<h4 className={classes.NavItem}>Rest</h4>
<h4 className={classes.NavItem}>Next Next Next</h4>
<h4 className={classes.NavItem}>Trust</h4>
<h4 className={classes.NavItem}>Rust</h4>
</motion.div>
</React.Fragment>
)}
</h4>
);
})}
</div>
);
}
and here's my CSS:
.NavItemContainer {
display: flex;
align-items: center;
color: var(--inactive-silver);
gap: 2rem;
}
.NavItem {
letter-spacing: 0.05em;
font-family: var(--raleway);
font-style: normal;
font-weight: 600;
font-size: 1.2rem;
line-height: var(--lineheight);
cursor: pointer;
transition: all 0.1s ease-in;
}
.NavItem:hover {
scale: 0.96;
}
.LastItem {
display: flex;
align-items: center;
position: relative;
}
.ActiveItem {
color: var(--active-blue);
}
.ShowMoreIcon {
font-size: 2rem;
color: var(--inactive-silver);
}
.Expander {
position: absolute;
top: 130%;
right: 5%;
width: max-content;
padding: 0 1rem;
background-color: var(--light-background-color);
box-shadow: var(--box-shadow-thick);
text-align: end;
border-radius: 0.4rem;
}
.Expander:hover {
scale: 1;
}

Render two Buttons in React but only one got the styling of the css Stylesheet

I am currently in the process of adding a stylesheet to my React Todo app. In the Todo component, two buttons are rendered that get the class clearButton or addButton depending on the label.
Button Component:
import './Button.css'
interface ButtonProps {
lable: string,
disabled: boolean,
onClick: MouseEventHandler<HTMLButtonElement>
}
export const Button: FunctionComponent<ButtonProps> = ({ lable, disabled, onClick}): ReactElement => {
let style: string = lable == 'ADD' ? 'addButton' : 'clearButton';
console.log(lable);
console.log(style);
return(
<div>
<button className= {`button ${style}`} type='button' disabled= { disabled } onClick= { onClick }>
{lable}
</button>
</div>
);
}
Strangely, only one styling of style class is ever applied to a button. It depends on which style class is in first place in the stylesheet. If the addButton class is in first place, only the styling of the addButton is displayed, if the clearButton class is in first place, only the styling of the other button is loaded.
.button {
width: 70px;
height: 25px;
border-radius: 10px;
border: none;
cursor: pointer;
font-family: 'Poppins', sans-serif;
font-weight: bold;
};
.clearButton {
background-color: #8c00ff;
}
.addButton {
background-color: #7ce0ff;
}
Addutional
Parent Component
export const InputBar: FunctionComponent<InputBarProps> = ({ enterTodo, handleEnterTodo, handleCreateTodo, handleClearTodos}): ReactElement => {
return(
<>
<div className="inputBar">
<form>
<TextField value={ enterTodo } onChange={ handleEnterTodo }/>
<div className="buttonsContainer">
<Button lable= 'ADD' disabled= { enterTodo == '' } onClick= { handleCreateTodo } />
<Button lable= 'CLEAR' disabled= { false } onClick= { handleClearTodos } />
</div>
</form>
</div>
</>
);
}

Change background color of a single mat-card in Angular

This is the HTML code of my mat-card list in my Angular application:
<div [ngClass]="verticalResultsBarStyle">
<div class="innerDiv">
<mat-card [ngClass]="barStyle" *ngFor="let card of obs | async | paginate: { id: 'paginator', itemsPerPage: 10, currentPage: currentPage, totalItems: totalItems }" (click)="highlightCard()">
<mat-card-header>
<mat-card-title>{{card[0].title}}</mat-card-title>
<mat-card-subtitle>{{card[0].subtitle}}</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<p>{{card[0].info1}}</p>
<p>{{card[0].info2}}</p>
<p>{{card[0].info3}}</p>
<p>{{card[0].info4}}</p>
<p>{{card[0].info5}}</p>
</mat-card-content>
</mat-card>
<pagination-controls (pageChange)="OnPaginateChange($event)" id="paginator"></pagination-controls>
</div>
</div>
this is the CSS:
.verticalResultsBarContainerEnabled {
background-color: #eeeeee;
width: 16%;
float: right;
overflow: visible;
}
.verticalResultsBarContainerDisabled {
display: none;
}
.card {
margin-left: 10px;
margin-right: 10px;
margin-bottom: 10px;
}
.cardHighlight {
margin-left: 10px;
margin-right: 10px;
margin-bottom: 10px;
background-color: #c12400;
color: white;
}
.innerDiv {
height: 755px;
overflow: auto;
}
.paginator {
margin: auto;
width: 92%;
}
and finally this is a snippet of TS :
highlightCard() {
if (this.barStyle === 'card') {
this.barStyle = 'cardHighlight';
} else {
this.barStyle = 'card';
}
}
I would to click on a single mat card and highlight only the selected mat card, but when I click on a single card, the entire list changes color. How can I fix that?
You need to use index for each of your cards. Your issue is normal.
I see barStyle is one generall for all your cards. You need to update barStyle for each card, when your cards come from backend.
First, in your .ts :
service.subscribe(cards => {
let i = 0
cards.forEach(card => {
this.barStyle[i] = // your default barStyle;
i++;
});
If you have an id for each car, better, you can avoid to use i.
then, in template:
<mat-card [ngClass]="barStyle[i]" *ngFor="let card of obs; let i = index | async | paginate: { id: 'paginator', itemsPerPage: 10, currentPage: currentPage, totalItems: totalItems }" (click)="highlightCard(i)">
I see you use async function in template. So, in ts, you expect and Observable. To update style of each card, you need to subscribe at your service and update objects. So, need to do some changes:
cards: Observable will become cards: Card[];
loadingData = true;
call the service as:
getData() {
this.loadingData = true;
service().subscribe(cards => {
cards.forEach(card => {
this.barStyle[card.id] = // your default barStyle;
this.cards = cards;
this.loadingData = false; // this is set false only after data is loaded
});
}
highlightCard(id: number) {
if (this.barStyle[id] === 'card') {
this.barStyle[id] = 'cardHighlight';
} else {
this.barStyle[id] = 'card';
}
}
in template:
<mat-card ***ngIf="!loadingData"**
[ngClass]="barStyle[card.id]"
*ngFor="let card of obs | async |
paginate: { id: 'paginator',
itemsPerPage: 10,
currentPage: currentPage,
totalItems: totalItems }"
(click)="highlightCard(card.id)">
Somethimes, your template will render before to load data, so you need to use a boolean to wait for it.
**To keep clean code, for pagination you can use a variable initialized in .ts*

Having trouble getting rid of the blue highlight

I've been working on a section with expandable/collapsible sections. When I click on a section to expand or collapse it, a blue focus area shows up but it is placed on a weird angle. I don't know what is causing it and would like a solution to either get rid of it or place it back at the normal horizontal angle. Does anybody have any suggestions as to how to fix this?
I am using a Macbook and Chrome browser.
The entire grey block that this component appears in is placed at an angle as you can see from the top of the image attached below but in the reverse direction from the highlighted focus area.
My css:
#import '../../theme/variables.css';
.rotatedSection {
padding-bottom: 2rem;
}
.container {
max-width: 64rem;
margin: 0 auto;
display: flex;
padding: 2rem 0;
#media screen and (max-width: 68rem) {
margin: 0 3rem;
}
}
.accordianContainer {
flex: 1;
margin-right: 2rem;
min-width: 500px;
#media screen and (max-width: $tablet-lg-max-width) {
margin-right: 0;
}
#media screen and (max-width: 900px) {
min-width: 0;
}
}
.imageContainer {
flex: 1;
margin-left: 2rem;
max-height: 300px;
display: flex;
justify-content: center;
img {
flex: 1;
}
#media screen and (max-width: $tablet-lg-max-width) {
margin-left: 0;
}
}
.heading {
composes: h2 from 'theme/text';
margin-left: auto;
margin-right: auto;
}
My react code:
import React, {Component, PropTypes} from 'react';
import RotatedSection from 'components/RotatedSection';
import AccordionItem from './AccordionItem';
import css from './styles.css';
class AccordionSectionWithImage extends Component {
constructor (props) {
super(props);
this.state = {
activeIndex: null,
};
this.onOpen = this.onOpen.bind(this);
this.onClose = this.onClose.bind(this);
this.setActive = this.setActive.bind(this);
this.handleClickOutside = this.handleClickOutside.bind(this);
}
onOpen = (index) => {
this.setActive(index);
};
onClose = (callback = () => null) => {
this.setActive(null);
callback();
};
setActive = (activeIndex) => this.setState({activeIndex});
handleClickOutside = () => this.props.collapseOnBlur && this.onClose();
render () {
const {
entry: {
items,
heading,
image,
},
showIndex,
classNames,
meta = {},
} = this.props;
const {routeParams, toggleHamburger} = meta;
const {activeIndex} = this.state;
return (
<RotatedSection color='whisper' className={css.rotatedSection}>
<div className={css.container}>
<div className={css.accordianContainer}>
<h2 className={css.heading}>{heading}</h2>
{items && items.map((item, index) => (
<AccordionItem
key={index}
showIndex={showIndex}
entry={item}
meta={{
position: index,
isOpen: (index === activeIndex),
onOpen: () => this.onOpen(index),
onClose: () => this.onClose(),
onChildClick: () => this.onClose(toggleHamburger),
routeParams,
}}
classNames={classNames}
/>
))}
</div>
<div className={css.imageContainer}>
<img src={image && image.fields && image.fields.file.url} alt='Educational assessment' />
</div>
</div>
</RotatedSection>
);
}
}
AccordionSectionWithImage.propTypes = {
meta: PropTypes.object,
entry: PropTypes.object,
collapseOnBlur: PropTypes.bool,
showIndex: PropTypes.bool,
classNames: PropTypes.object,
};
export default AccordionSectionWithImage;
React component for individual section:
function AccordionItem (props) {
const {
meta: {
isOpen,
onOpen,
onClose,
},
entry: {
heading,
text,
},
} = props;
const handleClick = () => (isOpen ? onClose() : onOpen());
return (
<div className={css.itemContainer}>
<div className={css.innerContainer}>
<h3 className={css.heading} onClick={handleClick}>
<span className={css.titleText}>{heading}</span>
<i className={`zmdi zmdi-plus ${css.titleToggle}`} />
</h3>
{isOpen && (
<div className={css.contents}>
{text}
</div>
)}
</div>
</div>
);
}
For anybody else experiencing a similar problem:
Problem only appeared on mobile phones and the device mode of chrome inspector. It was due to the tap-highlight property.
Setting -webkit-tap-highlight-color to rgba(0,0,0,0) hid the problem but it's a non standard css property so the solution may not work for all devices/browsers/users.

toggle css class for two buttons when either is clicked

import React, { Component } from 'react';
class Buttons extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div className="displayButtons">
<input className='button1' onClick={this.props.get_menu_items_api}
value="Categories" type="button" ref="button"></input>
<input className='button2' onClick={this.props.get_addons_items_api}
value="Add Ons" ref="button1" type="button"></input>
</div>
)
}
}
export default Buttons;
I have these two buttons in react class.Their css is given below. What I want to do is on whichever button I click on it should turn orange and other should turn white. Initially Categories button is orange and Addons button is white.
I tried calling a function onClick that changes its class but how will it change the class of other button also.
.button2 {
border: none;
padding: 11px 32px;
text-align: center;
text-decoration: none;
line-height: 14px;
font-family: Roboto-Regular;
font-size: 12px;
border-radius: 2px 0 0 2px;
display: inline-block;
float: left;
}
.button1 {
background-color:#F6A623;
color: white;
}
.button2 {
background-color:white;
color: black;
}
You can save the status of the component identifier of the orange button and change it with using onClick.
Component:
class App extends React.Component{
constructor(){
super();
this.state = {
orangeButtonId: null
}
this.setOrangeButton = this.setOrangeButton.bind(this);
}
setOrangeButton(id){
this.setState({orangeButtonId: id});
}
render(){
return(
<div>
<input className={this.state.orangeButtonId === 1? "button1 orange" : "button1"} onClick={() => this.setOrangeButton(1)} value="Categories" type="button" ref="button" />
<input className={this.state.orangeButtonId === 2? "button2 orange" : "button2"} onClick={() => this.setOrangeButton(2)}
value="Add Ons" ref="button1" type="button" />
</div>
)
}
}
And styles:
input[type="button"]{
background-color: white;
}
input[type="button"].orange{
background-color: orange;
}
Check the fiddle https://jsfiddle.net/69z2wepo/83389/.
it can easily achived by using the component inner state + classnames library:
class Buttons extends Component {
constructor(props) {
super(props);
this.onButtonClick = this.onButtonClick.bind(this);
this.state = {
selectedButton: 'categories'
}
}
onButtonClick(e) {
this.setState({
selectedButton: e.target.value
});
if (e.target.value === 'categories') {
this.props.get_menu_items_api();
} else {
this.props.get_addons_items_api();
}
}
render() {
return (
<div className="displayButtons">
<input className={classnames({'button': true, 'selected': this.state.selectedButton === 'categories'} onClick={onButtonClick}
value="Categories" type="button" ref="button"></input>
<input className={classnames({'button': true, 'selected': this.state.selectedButton === 'addons'})} onClick={onButtonClick}
value="Add Ons" ref="button1" type="button"></input>
</div>
)
}
}
You need to keep track of button state within your class. When onClick is called set your state.
onButton1Click() {
this.setState({button1Down: true});
}
Then in the render call you need to use this state to set the class names to apply to your buttons.
render() {
let button1ClassName = 'button1';
if (this.state.button1Down) {
button1ClassName += 'button-down';
}
return ...

Resources