Skip to content
Advertisement

Vue – Loop through an array of objects, and highlight the selected item on click

I have an array of objects which holds data, which I output to the DOM using map. Each item in the list, has a @click event listener. When you click on one of the items, I want it to be highlighted by way of adding a css class, for example ‘hover’. So basically it should work like a menu system. When you click on an item, that item is highlighted and the others should not be highlighted and vice versa.

The way i have it set up right now, all of the items in the list get highlighted, which is not the logic i’m trying to do. I have used console.log, to display which item has been clicked. However, I still have not figured out, how to make that selected item, the one thats highlighted instead of the whole list?

<template>
<div>
    <div>
        <h1>Dragonball</h1>
    </div>
    <div>
        <ul>
            <li v-for="(dragonBallFighters, index) of dragonBallFighter" @click="toggleClass(dragonBallFighters.id)" :key=dragonBallFighters.id :class="{hover: active}">
                <div class="dragonball-container">
                    <div class="dragonball-stats">
                    <h1>{{index}}, {{dragonBallFighters.name}}</h1>
                    <p>{{dragonBallFighters.race}}</p>
                    <p>{{dragonBallFighters.specialAttack}}</p>
                </div>
                <div class="dragonball-image">
                    <img :src="dragonBallFighters.img" :alt="dragonBallFighters.name" />
                </div>
                </div>
            </li>
        </ul>
    </div>
</div>
</template>

<script>
export default {
  data() {
    return {
      active: false,
      dragonBallFighter: [
        {
          id: 1,
          name: 'Goku',
          race: 'Sayain',
          specialAttack: 'Kamehameha Wave',
          img:
            'https://geeksnipper.com/wp-content/uploads/2018/03/Screen-Shot-2018-03-04-at-8.52.28-AM.png'
        },
        {
          id: 2,
          name: 'Vegeta',
          race: 'Sayain',
          specialAttack: 'Final Flash',
          img:
            'https://nerdreactor.com/wp-content/uploads/2018/01/vegeta-ssb.jpg'
        },
        {
          id: 3,
          name: 'Brolly',
          race: 'Sayain',
          specialAttack: 'Crusher Blast',
          img: 'http://media.comicbook.com/2017/05/broly-995283-1280x0.png'
        },
        {
          id: 4,
          name: 'Majin Buu',
          race: 'Unknown',
          specialAttack: 'Absorbtion',
          img: 'https://i.ytimg.com/vi/50GF26RBWjw/maxresdefault.jpg'
        },
        {
          id: 5,
          name: 'Janemba',
          race: 'Unknown',
          specialAttack: 'Teleportation Warp',
          img:
            'https://vignette.wikia.nocookie.net/villainstournament/images/f/f1/Super_janemba.png/revision/latest?cb=20140311163545'
        },
        {
          id: 6,
          name: 'Tien',
          race: 'Human',
          specialAttack: 'Tri Beam',
          img:
            'http://i1.wp.com/www.dragonball.co/wp-content/uploads/2016/08/tien_banner.png?fit=704%2C396'
        },
        {
          id: 7,
          name: 'Vegito SSJB',
          race: 'Sayian',
          specialAttack: 'Final kamehameha',
          img:
            'http://i1.wp.com/shoryuken.com/wp-content/uploads/2018/05/Vegito-Blue-SSGSS-Attack.png?fit=750%2C400&resize=750%2C400'
        },
        {
          id: 8,
          name: 'Toppo',
          race: 'Unknown',
          specialAttack: 'Finger Blasters',
          img: 'https://i.ytimg.com/vi/_Lz9bTEL1dM/maxresdefault.jpg'
        },
        {
          id: 9,
          name: 'Dyspo',
          race: 'Unknown',
          specialAttack: 'Super Hyper Lightspeed Mode',
          img:
            'https://pre00.deviantart.net/5458/th/pre/f/2017/148/1/8/dragon_ball_super_dyspo_by_giuseppedirosso-dbaqm0s.jpg'
        },
        {
          id: 10,
          name: 'Future Trunks',
          race: 'Human',
          specialAttack: 'Galick Gun',
          img:
            'https://static5.comicvine.com/uploads/original/11129/111290855/5809735-3904647274-14886.png'
        }
      ]
    };
  },
  methods: {
    toggleClass(id) {
      console.log('Clicked ' + id);

      const currentState = this.active;
      this.active = !currentState;
      //   this.selectedItemIndex = id;

      if (this.selectedItemIndex === id) {
        this.active === true;
      } else if (this.selectedItemIndex === !id) {
        this.active === false;
      }
    }
  }
};
</script>

<style lang="scss" scoped>
.dragonball-container {
  cursor: pointer;
  display: grid;
  padding: 20px;
  grid-template-columns: 1fr 1fr;
  //background: #666;
  border: 2px solid #666;
  grid-gap: 20px;
  margin-bottom: 20px;
  border-radius: 10px;
  -webkit-box-shadow: 10px 10px 5px 0px rgba(240, 240, 240, 1);
  -moz-box-shadow: 10px 10px 5px 0px rgba(240, 240, 240, 1);
  box-shadow: 10px 10px 5px 0px rgba(240, 240, 240, 1);
}

.dragonball-image img {
  display: grid;
  justify-content: center;
  align-items: center;
  width: 100%;
  max-width: 300px;
  border-radius: 100%;
}

ul li {
  list-style: none;
}
.hover {
  background: rgb(244, 244, 244);
  border-radius: 10px;
}
</style>

Advertisement

Answer

My prefered way to do this is by adding a new key to each of the fighters and using that key in the v-for. The only thing needed in the markup is to change the :class value to be the active key in from the fighters in the v-for loop.

   <li
      v-for="(dragonBallFighters, index) of dragonBallFighter"
      @click="toggleClass(dragonBallFighters.id)"
      :key=dragonBallFighters.id
      :class="{hover: dragonBallFighters.active}"
   >
    <div class="dragonball-container">
      <div class="dragonball-stats">
        <h1>{{index}}, {{dragonBallFighters.name}}</h1>
        <p>{{dragonBallFighters.race}}</p>
        <p>{{dragonBallFighters.specialAttack}}</p>
      </div>
      <div class="dragonball-image">
        <img :src="dragonBallFighters.img" :alt="dragonBallFighters.name" />
      </div>
    </div>
  </li>

Then for the Javascript, we use Vue.set() to tell the dom that we added a key that is not in the original object that v-for is aware of. This makes vue correctly update the dom when we change the fighter’s ‘activeness’.

toggleClass(id) {
  // Create variable for all fighters (name takes up less space)
  let allFigthers = this.dragonBallFighter;
  // Get the clicked fighter
  let fighter = allFigthers.find(e => e.id === id)
  // Set all fighters to have a active key of false so that they "loose focus"
  allFigthers = allFigthers.map(e => Vue.set(e, 'active', false))
  // Use Vue.set to tell vue that we updated the object and it needs to be re-rendered
  Vue.set(fighter, 'active', !fighter.active)
}

Live example

The way I toggle the active class is simply by doing !fighter.active. This will be translated to not {boolean} ~ If the fighter.active is true then not true will equivalent in false. If it is false, not false will equivalent to true

Btw; I would suggest you to rename the object of fighters to dragonBallFighters. This way, the v-for makes more sense == <v-for="fighter in dragonBallFighters" One could translate that to

for each fighter in the dragonBallFighters object.

Per now, you are indirectly saying

for each dragonBallFighters in the dragonBallFighter object

witch doesn’t float the same on the tongue 😉

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement