How to change the target span onClick - javascript

I want a line-through to appear on the span content onClick in React. Have tried creating a state and associating it to the li. But onclick, it affects all the li tags. I want it to only change the style of the particular li tag. I am new to react and trying to learn slowly.
import React from 'react';
class Todos extends React.Component{
constructor(props) {
super(props);
this.state = {
clicked: false
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(){
const click = this.state.clicked;
console.log(click)
if(click){
this.setState({
clicked: false
})
}
else{
this.setState({
clicked: true
})
}
console.log(this.state.clicked);
}
render() {
return (
<ul className="bg-warning p-4 list-group">
{this.props.todos.length ? (
this.props.todos.map(todo => {
return (
<li className="list-group-item" key={todo.id}>
<span className="pr-2">{todo.content}</span>
<button onClick={()=>{this.props.deleteTodo(todo.id)}}>Delete</button>
</li>
)
})
) : (
<p className="center">You have no todo's left, yay!</p>
)}
</ul>
)
}
}
export default Todos;

#eramit2010 answer makes sense, but I am extending it to fit the requirement that, when clicked on todo item (i.e. span) show line-through and when clicked on delete button simply delete the todo item.
You need to maintain clicked todo's in a state array,
state ={
clickedTodo: []
}
You should have separate click event on your span where you want to show line-through,
<span
className="pr-2"
style={{textDecoration: this.state.clickedTodo.includes(index) ? 'line-through' : 'none', paddingRight: '20px', cursor:'pointer'}}
onClick={() => this.lineThrought(todo.id)}
>
{todo.content}
</span>
Here, I have applied CSS to make span line-through when it gets clicked.
Your lineThrought method should be,
lineThrought = (id) => {
if(this.state.clickedTodo.includes(id)){
//To remove line-through
this.setState({clickedTodo: this.state.clickedTodo.filter(item => item !== id)})
}else{
//To add line-through
this.setState({clickedTodo: [...this.state.clickedTodo, id]})
}
}
Demo

you can store deleted ids in local state and then if that perticular id is present in state, you can use custom class for that.
consturctor() {
this.state = {
deleted: []
};
}
render() {
return (
<ul className="bg-warning p-4 list-group">
{this.props.todos.length ? (
this.props.todos.map(todo => {
return (
<li className=`list-group-item ${this.state.deleted.includes(todo.id) ? "list-group-item-deleted" : ""}` key={todo.id}>
<span className="pr-2">{todo.content}</span>
<button onClick={()=> {
this.props.deleteTodo(todo.id);
this.setState(prevState => { deleted: [...prevState.deleted, todo.id]);
}>Delete</button>
</li>
)
})
) : (
<p className="center">You have no todo's left, yay!</p>
)}
</ul>
)
}
in your css file:
.list-group-item-deleted {
text-decoration: line-through
}
If you are already storing deleted ids in redux or somewhere else you can use same.

When someone click in li then it will perform deleteTodo, you said you want it to only change the style of the particular li tag, you can easily perform that using below code inside of you componentdidmount() function
$(document).on('click', 'your class name' , function()
{ $(this).css('color','red');
});
Otherwise, you have to use dynamic state, based on unique id, when someone click event then use that id as a state and change those li color.

Related

Switching classes between items from the list in React

im doing this simple ecommerce site, and on the product page you have different attributes you can choose, like sizes, colors - represented by clickable divs, with data fetched from GraphQl and then generated to the DOM through map function.
return (
<div className="attribute-container">
<div className="attribute">{attribute.id.toUpperCase()}:</div>
<div className="attribute-buttons">
{attribute.items.map((item) => {
if (type === "Color") {
return (
<AttributeButton
key={item.id}
className="color-button"
style={{ backgroundColor: item.value }}
onClick={() => addAtribute({type: type, value: item.value })}/>
);
}
return (
<AttributeButton
key={item.id}
className="size-button"
size={item.value}
onClick={() => addAtribute({ type: type, value: item.value })}
/>
);
})}
</div>
</div>
);
Im importing external component, then I check if an attribute's type is Color (type color has different styling), then render depending on that type.
What I want to implement is that when i click on one attribute button, its style changes, BUT of course when i click on another one, choose different size or color for the item i want to buy, new button's style changes AND previously selected button goes back to it's default style. Doing the first step where buttons style changes onClick is simple, but i cant wrap my head around switching between them when choosing different attributes, so only one button at the time can appear clicked.
Here is code for AttributeButton:
class Button extends PureComponent {
constructor(props){
super(props);
this.state = {
selected: false,
}
}
render() {
return (
<div
className={ !this.state.selected ? this.props.className : "size-selected "+this.props.className}
style={this.props.style}
onClick={() => {this.props.onClick(); this.setState({selected: !this.state.selected}) }}
>
{this.props.size}
</div>
);
}
}
export default Button;
PS - i have to use class components for this one, it was not my choice.
You need to have the state selected outside of your <Button> component and use it as a prop instead. Something like:
handleSelect = (button) => {
const isSelected = this.state.selected === button;
this.setState({ selected: isSelected ? null : button });
};
render() {
return (
<>
<Button
isSelected={this.state.selected === "ColorButton"}
onClick={() => this.handleSelect("ColorButton")}
/>
<Button
isSelected={this.state.selected === "SizeButton"}
onClick={() => this.handleSelect("SizeButton")}
/>
</>
);
}

ReactJS onClick not working on first element in menu

I'm making a custom dropdown list in reactjs. When I click on any element in the dropdown list I get it's value and put it inside an input and it works just fine. The problem is that the first element returns nothing and I can't get it's value. Also I developed the dropdown list to disappear when I choose any element inside of it. But like I said it works just fine on all elements except the first one.
I solved the problem by setTimeOut and hide the dropdown list in 50 milliseconds. But I don't think this's a right solution.
//Select Component
class Select extends Component {
showList = (e) => {
e.target.closest('.select').classList.add('active');
}
hideList = (e) => {
setTimeout(() => {
e.target.closest('.select').classList.remove('active');
}, 100);
}
selectValue = (e) => {
let value = e.target.getElementsByTagName('span')[0].innerHTML;
this.setState({ selectValue: value })
}
render() {
return (
<input
{...this.props}
type="text"
placeholder={this.props['placeholder']}
onFocus={this.showList}
onBlur={this.hideList}
value={this.state.selectValue}
onChange={this.changeSelectValue}
required
/>
<div className="select">
<div className="select-options menu full-width">
{
this.props.list.map(element => {
return (
<MenuItem text={element} onClick={(e) => this.selectValue(e)} />
)
})
}
</div>
</div>
);
}
}
==================
class MenuItem extends Component {
constructor(props) {
super(props);
this.state = {}
}
render() {
return (
<p className={"menu-item " + this.props['type'] } {...this.props}>
{this.props['icon'] ? <i className={this.props['icon']} ></i> : null}
<span>{this.props['text']}</span>
</p>
);
}
}
1.use key prop for every MenuItem Component when you are using the list to create a list of MenuItems.
2.Instead of getting value from target in selectValue function directly pass the value from onClick handler.
selectValue = (e , element) => {
this.setState({ selectValue: element })
}
<MenuItem text={element} key={element} onClick={(e) => this.selectValue(e , element)} />
Editted:-
Remove the onBlur handler and put the functionality of hideList inside selectValue function after setState,you can use setState with callback if normal setState doesn't work
selectValue = (e) => {
let value = e.target.getElementsByTagName('span')[0].innerHTML;
this.setState({ selectValue: value })
e.target.closest('.select').classList.remove('active');
}

Create and return a new component on-click in parent component

I have two components Class and Students. The Class component renders and returns a list of Classes in <li>. I want to add click events to display the Students for each Class in Class component.
I have the following in the render method of the Class component:
render(){
const renderClasses = () =>
this.props.classes.map(class => {
return (
<li>
{class.name}
//class object also has a property of 'students'
</li>
)
})
return(
<div>
{ renderClasses() }
</div>
)
}
I want to be able to click on the anchor tags and display the corresponding students for that class. Of course the Student component should receive a prop as follows:
<Students students={this.class.students} />
Thanks in advance!
You can keep a component state to save the class index that should show its students, and then add an onClick handler on the anchor to change that index.
Try the code below:
export default class Test extends Component {
constructor(props)
{
super(props);
this.state = {activeClassIndex : -1}
this.setActiveClassIndex = this.setActiveClassIndex.bind(this);
}
setActiveClassIndex(index){
this.setState({
activeClassIndex : index
})
}
render(){
const renderClasses = () =>
this.props.classes.map( ( currentClass , index ) => {
return (
<li>
<a href="#" onClick={ () => { this.setActiveClassIndex(index) } }>{currentClass.name}</a>
{this.state.activeClassIndex == index ? <Students students={currentClass.students} /> : "" }
</li>
)
})
return(
<div>
{ renderClasses() }
</div>
)
}
}

Applying react class to a single element onMouseEnter

I failed to apply a class to a Dom node, below code will apply class to every DOM node.
import { Component } from 'react';
export default class App extends Component {
constructor(props){
super(props)
this.state = {
active: false
}
}
onMouseEnter(){
this.setState({active:true})
}
render(){
const items = [1,2,3,4,5];
return (
<div>
{items.map((obj,i) => <div key={i} className={this.state.active ? 'active' : ''} onMouseEnter={this.onMouseEnter.bind(this)}>{obj}</div>)}
</div>
);
}
}
What has gone wrong here? Also, how to do onMouseLeave? Just set this.setState({active:false}) false?
You are close... What you want is something like assigning an "active index". Your onMouseEnter() function could be changed to take the index of the active item like this
onMouseEnter(index){
this.setState({active: index})
}
And your render function would look like this instead:
render(){
const items = [1,2,3,4,5];
return (
<div>
{items.map((obj,i) =>
<div key={i} className={this.state.active === i ? 'active' : ''} onMouseEnter={this.onMouseEnter.bind(this, i)}>{obj}</div>)}
</div>
);
}
The thing you did wrong in the example you posted is not differentiating between which item in the list is in fact active instead you applied the active class to every item.
Your comments on my answer to this question make no sense:
(as you can see my mouse is no longer hovering over the active item but it is still yellow)

Need Change active class in tab ( react)

If I click Second page,after reload page, all tabs get class CURRENT, How to fix this? How to disable current class on first TAB ?
If i remove activeClassName="current", After Reloading current class switch to first tab, but I saw second tab content
import React from 'react'
import { Link, browserHistory,IndexLink } from 'react-router'
class Tabs extends React.Component{
constructor(props) {
super(props);
this.state = {
index: ''
};
this.onclick = this.onclick.bind(this);
}
onclick(index) {
this.setState({index});
}
getListItem(){
let numbers = this.props.menuitems;
let listItems = numbers.map((item,index) =>
<li
onClick={this.onclick.bind(this, index)} key={index}>
<Link to={item.link} activeClassName="current"
className={index == this.state.index? "tab-link current" : "tab-link"}>{item.linkName}</Link>
</li>
);
return listItems;
}
render() {
return (
<div>
<ul className="tabs" >{this.getListItem()}</ul>
<div className="tabs-header-stripe"></div>
</div>
);
}
}
export default Tabs
According to the scenario u described, you need a stateful component instead of stateless function component. Store the index of current tab in state variable and update it inside onclick method, during the rendering compare the index of state variable with the index of item, if they are same then apply the class. Try this a similar example, it should work in ur case also:
class HelloWidget extends React.Component{
constructor(props) {
super(props);
this.state = {
index: ''
};
this.onclick = this.onclick.bind(this);
}
onclick(index) {
this.setState({index});
}
getListItem(){
let numbers = this.props.menuitems;
let listItems = numbers.map((item,index) =>
<li style={{color: this.state.index==index?'red': 'black'}} className={this.state.index == index ? "tab-link current" : "tab-link"} onClick={this.onclick.bind(this, index)} key={index}>{index}-{item.number}</li>
);
return listItems;
}
render() {
return (
<div>
<ul className="tabs" >{this.getListItem()}</ul>
<div className="tabs-header-stripe"></div>
</div>
);
}
}
React.render(<HelloWidget menuitems={[{number:0, index:0}, {number:1, index:1}, {number:3, index:3}]}/>, document.getElementById('container'));
check the jsfiddle for working example: https://jsfiddle.net/27po3p4b/
the className current is only on the first tab because you are checking if index === 0 (first tab) and if true - you are adding the current class.
You need to keep a state of activeTabIndex and on your onClick function change the activeTabIndex to the right index.
and then you can check
className={index === this.state.activeTabIndex ? "tab-link current" : "tab-link"}

Categories

Resources