3

I am trying to strike through a list item once a user has clicked on that particular item.

I have created a function to conduct the style change

const completed = () =>{
     return document.getElementById("demo").style.textDecoration='line-through'
};

The list is generated as below, i have used material ui library

 <List dense={dense} >
      {items.slice(0).reverse().map(x=> (
           <ListItem key={x.id} button id="demo" onClick={completed} divider>
                 <ListItemText primary={listItems(x)}/>
                 <ListItemSecondaryAction />
           </ListItem>

       ))}                              
</List>

From the code i have written i am able to strike only through the first item of the list. Whenever i add new items , always the only item i am able to strike through is the first item.

I am trying to find a way to apply this to all the elements on the list

iamhuynq
  • 2,359
  • 1
  • 7
  • 27
George Silva
  • 355
  • 2
  • 9

2 Answers2

4

It is not a good practice to use document.getElementById() in React because then you are accessing the DOM directly. Instead, you have to use ref.

From official React documentation

When to Use Refs There are a few good use cases for refs:

  • Managing focus, text selection, or media playback.
  • Triggering imperative animations.
  • Integrating with third-party DOM libraries.

But in your case we can easily do this by using React state. I assume that your items is stored in your component state and in a todo item, you need to store whether it is completed or not ( a boolean value ). You can update the code like the following.

const completed = (id) => {
   /* update the state by changing the completed value of the
      clicked todo item */
   this.setState({
      items : this.state.items.map(item => {
         if(item.id === id){
            item.completed = true;
         }
         return item;
      })
   })

}

<List dense={dense}>
  {this.state.items
    .reverse()
    .map(x => (
      <ListItem
        key={x.id}
        button
        // add a unique id
        id={x.id}
        
        // add a strike style depending on the completed property of the todo item
        style={{ textDecoration : x.completed ? 'line-through' : 'none' }} 
        
        // call completed with the id
        onClick={() => completed(x.id)} 

        divider
      >
        <ListItemText primary={listItems(x)} />
        <ListItemSecondaryAction></ListItemSecondaryAction>
      </ListItem>
    ))}
</List>
Chetan Gawai
  • 2,133
  • 18
  • 34
2

Id's should be all unique in HTML. You should add dynamic id-values to each element and send the id in function-call like following.

const completed = (id) => {
 document.getElementById(id).style.textDecoration='line-through'
}

<List dense={dense}>
  {items
    .slice(0)
    .reverse()
    .map(x => (
      <ListItem
        key={x.id}
        button
        id={x.id} // Add dynamic ID
        onClick={() => completed(x.id)} // Send ID to function
        divider
      >
        <ListItemText primary={listItems(x)} />
        <ListItemSecondaryAction></ListItemSecondaryAction>
      </ListItem>
    ))}
</List>

Jimi Pajala
  • 2,044
  • 6
  • 17