How to toggle an active class on list items? - javascript

I'm having a issue in trying to implement a basic functionality in React. I have a list of <img>, and when I click in one, I want to add an active class to this img and remove this class from the other images.
class DefaultImages extends React.Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick(e) {
console.log("Element:", e.target)
// I can add the class here, but where to remove from the other images?
}
render() {
var imgSize = "100"
return (
<div>
<img onClick={this.handleClick} src="img/demo.png" width={imgSize} height={imgSize} />
<img onClick={this.handleClick} src="img/demo2.png" width={imgSize} height={imgSize} />
<img onClick={this.handleClick} src="img/demo3.jpg" width={imgSize} height={imgSize} />
<img onClick={this.handleClick} src="img/demo4.png" width={imgSize} height={imgSize} />
</div>
)
}
}
I know how to toggle the class from the clicked image, but how can I remove the active class from the siblings images?

Use the component's state to store the active item and rerender the view when it changes:
import React, { Component } from 'react'
const IMG_SIZE = 100
const imgs = [{ id: 1, src: 'img/demo.png' }, { id: 2, src: '...' }, etc]
class DefaultImages extends Component {
constructor() {
this.state = { activeItem: {} }
this.toggleActiveItem = this.toggleActiveItem.bind(this)
}
toggleActiveItem(imgId) {
this.setState({ activeItem: { [imgId]: true } })
}
render() {
return (
<div>
{imgs.map(img =>
<img
className={this.state.activeItem[img.id] ? 'active' : ''}
onClick={e => this.toggleActiveItem(img.id)}
src={img.src}
width={IMG_SIZE}
height={IMG_SIZE}
alt={`Default image ${img.id}`}
/>
)}
</div>
)
}
}

Related

GlideJs adding buttons

I created a glider that works good, no problem with the functionality, what I'm trying to achieve is to add a button inside the slides. So far, the links and all the slide content works good but not the button click event. I tried adding the .disable() and the pause() methods but it doesn't work. And I can't find anything like this, nor anything in the documentation. If anyone would have an approach, it'll help me a lot.
Glide holder:
import React, { Component } from 'react';
import Glide, {Swipe, Controls} from '#glidejs/glide';
import myComponent from './myComponent';
class myHolder extends Component {
constructor(props) {
super(props);
}
}
componentDidMount() {
const glide = new Glide(`.myGliderComponent`, {
type: carouselType,
focusAt: 0,
perTouch: 1,
perView: 4,
touchRatio: 1,
startAt: 0,
rewind: false,
});
glide.mount({Controls});
const CAROUSEL_NUMBER = 3;
const carouselType = this.props.displayedProducts.length <= CAROUSEL_NUMBER ? 'slider' : 'carousel';
}
render() {
return (
<div>
<div data-glide-el="track" className="glide__track">
<ul className="glide__slides">
{
this.props.displayedProducts.map(({ name, image,} = product, index) => (
<li className="glide__slide slider__frame">
<MyComponent
name={name}
image={image}
/>
</li>
))
}
</ul>
</div>
</div>
);
}
}
export default myHolder;
myComponent:
import React from 'react';
const myComponent = (
{
name,
image,
}
) => {
const buttonClicked = () => {
console.log("button clicked")
}
return (
<div>
<p>{name}</p>
<img
alt=""
src={image}
/>
<button onClick={buttonClicked}>Testing btn</button>
</div>
);
}
export default myComponent;
For anyone trying the same, I just added position:static to the class, and the glide.mount({Anchor}); to the mount() method

How do i get a button to display one image instead of all the images in an array

I'm using the react-image-lighbox npm package https://www.npmjs.com/package/react-image-lightbox and i am trying to get it to work with my custom code. I have a gallery and i am trying to get it to display an image in a lightbox when you click on a button in the array. At the moment when you click on any of the buttons it opens up all of them at the same time in an array. I need to have it filtered so that it only opens the image that you clicked on not all of them.
At the moment i am playing around with filter and if statements to try and find a solution.
Example of some of the state passed down through props.
portfolioContent: [
{
id: uuidv1(),
imgURL: image01,
text: 'Copy 1',
section: 1,
work:
'https://images2.minutemediacdn.com/image/upload/c_crop,h_1193,w_2121,x_0,y_64/f_auto,q_auto,w_1100/v1565279671/shape/mentalfloss/578211-gettyimages-542930526.jpg'
},
{
id: uuidv1(),
imgURL: image02,
text: 'Copy 2',
section: 1,
work:
'https://images2.minutemediacdn.com/image/upload/c_crop,h_1193,w_2121,x_0,y_175/f_auto,q_auto,w_1100/v1554921998/shape/mentalfloss/549585-istock-909106260.jpg'
}
]
<Main portfolio={this.state.portfolioContent} />
<PortfolioWork work={this.props.portfolio} section="1" />
import React, { Component, Fragment } from 'react';
import Lightbox from 'react-image-lightbox';
import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
const images = [
'//placekitten.com/1500/500',
'//placekitten.com/4000/3000',
'//placekitten.com/800/1200',
'//placekitten.com/1500/1500',
];
export default class LightboxExample extends Component {
constructor(props) {
super(props);
this.state = {
photoIndex: 0,
isOpen: false,
};
}
render() {
const { photoIndex, isOpen } = this.state;
return (
<Fragment>
{this.props.work.map(portfolio => {
if (portfolio.section === Number(this.props.section)) {
return (
<div className="portfolio-content" key={portfolio.id}>
<img src={portfolio.imgURL} alt={portfolio.text} />
<p>{portfolio.text}</p>
<a href={portfolio.work}>See work</a>
<button type="button" onClick={() => this.setState({ isOpen: true })}>
Open Lightbox
</button>
{isOpen && (
<Lightbox
mainSrc={portfolio.imgURL}
nextSrc={images[(photoIndex + 1) % images.length]}
prevSrc={images[(photoIndex + images.length - 1) % images.length]}
onCloseRequest={() => this.setState({ isOpen: false })}
onMovePrevRequest={() =>
this.setState({
photoIndex: (photoIndex + images.length - 1) % images.length,
})
}
onMoveNextRequest={() =>
this.setState({
photoIndex: (photoIndex + 1) % images.length,
})
}
/>
)}
</div>
);
}
})}
</Fragment>
);
}
}
Clicking on a button opens up that image in a lightbox. At the moment if you click on any button it opens up all of the images at the same time in a lighbox.
I figured it out. So i created a function in the main App.js file which took in an object. And then i passed down that function to the component through props and attached it to a click event. So that when the user clicks on the button it is assigned to its corresponding object. I then set that object to state and made the selected image object the main source for the lightbox image which gets displayed.
import React, { Component, Fragment } from 'react';
import Lightbox from 'react-image-lightbox';
import 'react-image-lightbox/style.css'; // This only needs to be imported once in your app
​
export default class LightboxExample extends Component {
constructor(props) {
super(props);
​
this.state = {
isOpen: false
};
}
​
render() {
const { isOpen } = this.state;
​
return (
<Fragment>
{this.props.work.map(portfolio => {
if (portfolio.section === Number(this.props.section)) {
return (
<div key={portfolio.id}>
<div className="portfolio-content">
<img src={portfolio.imgURL} alt={portfolio.text} />
<p>{portfolio.text}</p>
<button
type="button"
onClick={() => {
this.setState({ isOpen: true });
this.props.onImageSelect(portfolio);
}}
>
See work
</button>
</div>
<div>
{isOpen && (
<Lightbox
mainSrc={this.props.selectedImage.imgURL}
onCloseRequest={() => this.setState({ isOpen: false })}
/>
)}
</div>
</div>
);
}
})}
</Fragment>
);

Easy communication of image between siblings

I'm new to ReactJS and I would like to communicate between my components.
When I click an image in my "ChildA" I want to update the correct item image in my "ChildB" (type attribute in ChildA can only be "itemone", "itemtwo", "itemthree"
Here is what it looks like
Parent.js
export default class Parent extends Component {
render() {
return (
<div className="mainapp" id="app">
<ChildA/>
<ChildB/>
</div>
);
}
}
if (document.getElementById('page')) {
ReactDOM.render(<Builder />, document.getElementById('page'));
}
ChildA.js
render() {
return _.map(this.state.eq, ecu => {
return (
<img src="../images/misc/ec.png" type={ecu.type_eq} onClick={() => this.changeImage(ecu.img)}/>
);
});
}
ChildB.js
export default class CharacterForm extends Component {
constructor(props) {
super(props);
this.state = {
items: [
{ name: "itemone" image: "defaultone.png"},
{ name: "itemtwo" image: "defaulttwo.png"},
{ name: "itemthree" image: "defaultthree.png"},
]
};
}
render() {
return (
<div className="items-column">
{this.state.items.map(item => (<FrameCharacter key={item.name} item={item} />))}
</div>
);
}
}
I can retrieve the image on my onClick handler in my ChildA but I don't know how to give it to my ChildB. Any hints are welcomed, thanks you!
What you need is for Parent to pass an event handler down to ChildA which ChildA will call when one of the images is clicked. The event handler will call setState in Parent to update its state with the given value, and then Parent will pass the value down to ChildB in its render method.
You can see this working in the below example. Since I don't have any actual images to work with—and to keep it simple—I've used <button>s instead, but the principle is the same.
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = {
clickedItem: 'none',
};
}
render() {
return (
<div>
<ChildA onClick={this.handleChildClick}/>
<ChildB clickedItem={this.state.clickedItem}/>
</div>
);
}
handleChildClick = clickedItem => {
this.setState({ clickedItem });
}
}
const items = ['item1', 'item2', 'item3'];
const ChildA = ({ onClick }) => (
<div>
{items.map(name => (
<button key={name} type="button" onClick={() => onClick(name)}>
{name}
</button>
))}
</div>
);
const ChildB = ({clickedItem}) => (
<p>Clicked item: {clickedItem}</p>
);
ReactDOM.render(<Parent/>, document.querySelector('div'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.development.js"></script>
<div></div>

Change Icon of clicked element (React.js)

I've done this before but it's not an optimized code, I was trying to do this in another way but I couldn't. So, what I need to achieve is to change the icon of only the clicked element. Right now, when I click on one of the icons, all of them change.
For easier understanding, there is a list with multiple colors and the user has to select one of them.
I'll leave the important code down below:
import React from 'react';
export class Costura extends React.Component {
constructor(props) {
super(props);
this.state = {
token: {},
isLoaded: false,
modelTextures: {},
changeIcon: false
};
this.changeIcon = this.changeIcon.bind(this);
}
changeIcon = () => {
this.setState(prev => ({
changeIcon: !prev.changeIcon
}));
};
render() {
let icon;
if (this.state.changeIcon === true) {
icon = (
<img src="../../../ic/icon-check.svg"
alt="uncheck" className="Checking"
onClick={this.changeIcon} />
);
} else {
icon = (
<img src="../../../ic/icon-uncheck.svg"
alt="uncheck" className="Checking"
onClick={this.changeIcon} />
);
}
const { modelTextures } = this.state;
return (
<div id="Options">
<div id="OptionsTitle">
<img src="../../../ic/icon-linha.svg" alt="costura" />
<h2>Costura</h2>
</div>
{modelTextures.textures.map(texture => (
<div>
<img src={"url" + texture.image} />
<p key={texture.id}>{texture.name}</p>
{icon}
</div>
))}
</div>
);
}
}
You can set the selectedTextureId in the state and make a check against that when rendering the component to display the unchecked or checked image icon. Following is the code for reference.
import React from 'react';
export class Costura extends React.Component {
constructor(props) {
super(props);
this.state = {
token: {},
isLoaded: false,
modelTextures: {},
selectedTexture: null
};
this.selectedImageIcon = '../../../ic/icon-check.svg';
this.unselectedImageIcon = '../../../ic/icon-uncheck.svg';
}
changeIcon = (textureId) => () => {
this.setState({
selectedTexture: textureId
})
};
render() {
const { modelTextures } = this.state;
return (
<div id="Options">
<div id="OptionsTitle">
<img src="../../../ic/icon-linha.svg" alt="costura" />
<h2>Costura</h2>
</div>
{modelTextures.textures.map(texture => (
<div key={texture.id}>
<img src={"url" + texture.image} />
<p key={texture.id}>{texture.name}</p>
<img
src={this.state.selectedTexture === texture.id ? this.selectedImageIcon: this.unselectedImageIcon }
alt="uncheck"
className="Checking"
onClick={this.changeIcon(texture.id)}
/>
</div>
))}
</div>
);
}
}

Add class to siblings when Component clicked in React

I'm working on building my portfolio using React.js. In one section, I have four components laid out in a grid. What I want to do achieve is when one component is clicked, a css class is added to the siblings of this component so that their opacity is reduced and only the clicked component remains. In jQuery, it would be something like $('.component').on('click', function(){ $(this).siblings.addClass('fadeAway')}). How can I achieve this effect? Here is my code, thanks in advance for any and all help!
class Parent extends Component{
constructor(){
super();
this.state = {fadeAway: false}
this.handleClick = this.handleClick.bind(this)
}
handleClick(){
//Add class to siblings
}
render(){
const array = ["Hello", "Hi", "How's it going", "Good Times"]
return(
array.map(function(obj, index){
<Child text={obj} key={index} onClick={() => this.handleClick} />
})
)
}
}
A working example for this problem could look something like this, with a marginally more complex initialization array:
class Parent extends Component{
constructor(){
super();
this.state = {
elements: [
{
id: "hello",
text: "Hello",
reduced: false,
},
{
id: "hi",
text: "Hi",
reduced: false,
}
{
id: "howsItGoing"
text: "How's it going",
reduced: false,
}
{
id: "goodTimes",
text: "Good Times",
reduced: false,
}
],
}
this.handleClick = this.handleClick.bind(this)
}
handleClick(e){
// copy elements from state
const elements = JSON.parse(JSON.stringify(elements));
const newElements = elements.map(element => {
if (element.id === e.target.id) {
element.reduced = false;
} else {
element.reduced = true;
}
});
this.setState({
elements: newElements,
});
}
render(){
return(
this.state.elements.map(function(obj, index){
<Child
id={obj.id}
text={obj.text}
reduced={obj.reduced}
key={index}
onClick={() => this.handleClick} />
});
);
}
}
Then you would just add a ternary, like so, to the Child component:
<Child
id={this.props.id}
className={this.props.reduced ? "reduced" : ""} />
This adds a bit more boilerplate than other examples, but it's extremely brittle to tie business logic to the text inside a component, and a stronger solution requires a stronger piece of identification, like an ID or class on the rendered DOM element. This solution also, if you so wish, easily allows you to expand your logic so that more than one element can remain at maximum opacity at once.
I would simply store in state index of selected item, and then pass fadeAway prop into Child component defined as
fadeAway={this.state.selectedIndex !== index}
After that you only need to set a fade-away class in Child based on this.prop.fadeAway and define necessary CSS rules.
Here is how it could look in your case:
class Parent extends React.Component{
constructor () {
super();
this.state = {selectedIndex: null}
}
handleClick (selectedIndex) {
this.setState({ selectedIndex })
}
render () {
const array = ["Hello", "Hi", "How's it going", "Good Times"]
return (
<div>
{array.map((obj, index) => {
const faded = this.state.selectedIndex && this.state.selectedIndex !== index
return <Child
text={obj}
fadeAway={faded}
key={index}
onClick={() => this.handleClick(index)} />
})}
</div>
)
}
}
class Child extends React.Component {
render () {
return (
<h2
onClick={this.props.onClick}
className={this.props.fadeAway ? 'fade-away' : ''}>
{this.props.text}
</h2>
)
}
}
ReactDOM.render(
<Parent />,
document.body
);
.fade-away {
opacity: 0.3;
}
<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>
You can achieve that using using a toggle variable :
handleClick(){
this.setState({fadeAway} => ({
fadeAway: ! fadeAway
)};
}
...
<Child
text={obj}
key={index}
onClick={() => this.handleClick}
className={this.state.fadeAway? 'class1' : 'class2'}/>
I perfer use state like currentWord to save the word was clicked in Parent component, presudo code is like below:
class Parent extends Component {
constructor(){
super();
this.state = {
fadeAway: false,
currentWord: ''
};
this.handleClick = this.handleClick.bind(this)
}
handleClick(currentWord){
this.setState({
currentWord: currentWord,
});
}
render(){
const array = ["Hello", "Hi", "How's it going", "Good Times"]
const currentWord = this.state.currentWord;
return(
array.map(function(obj, index){
<Child currentWord={currentWord} text={obj} key={index} onClick={() => this.handleClick} />
})
)
}
}
And in Child component
class Child extends Component {
// some other code
handleClick(e) {
this.props.handleClick(e.target.value);
}
render() {
const isSelected = this.props.text === this.props.currentWord;
// use isSelected to toggle className
<div
onClick={this.handleClick.bind(this)}
>{this.props.text}
</div>
}
}

Categories