How to reset toggled item(s) back to default state

How to reset toggled item(s) back to default state

Problem Description:

I’m working no an app that onclick shows a div. Now if someone clicks the button to open another div, I want to hide the previously opened div. So far I was able to get this functionality by manually setting this.movies[index].open = false and this.movies[index].hideImg = true however – if there were thousands of entries this would be impossible to solve with this solution. I’m talking about the toggle(id) method

<template>
  <div v-for="(movie, index) in movies" :key="index" class="movieContainer">
    <div class="center">
      <h3>{{ movie.name }}</h3>
      <p class="duration">{{ movie.duration }}</p>

      <button @click="toggle(movie.id)" class="watchBtn">
        <p v-if="movie.hideImg">►</p>
        <p v-else>▼</p>
      </button>
    </div>
    <div v-if="movie.open">
      <video controls="controls" autoplay name="media">
        <source
          :src="require(`@/assets/movie${index + 1}.mp4`)"
          alt="video"
          type="video/mp4"
          width="500px"
          autoplay
        />
      </video>
    </div>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      movies: [
        {
          name: "Windy Highway",
          duration: "15 seconds",
          hideImg: true,
          open: false,
          id: 1,
          movieStart: "0:00",
          movieMid: "0.08",
          movieEnd: "0:15",
        },
        {
          name: "Sunny Station",
          duration: "32 seconds",
          hideImg: true,
          open: false,
          id: 2,
          movieStart: "0:00",
          movieMid: "0.16",
          movieEnd: "0:32",
        },
        {
          name: "Abstract Material",
          duration: "9 seconds",
          hideImg: true,
          open: false,
          id: 3,
          movieStart: "0:00",
          movieMid: "0.05",
          movieEnd: "0:09",
        },
        {
          name: "Pumpkin Drilling",
          duration: "17 seconds",
          hideImg: true,
          open: false,
          id: 4,
          movieStart: "0:00",
          movieMid: "0.09",
          movieEnd: "0:17",
        },
      ],
    };
  },
  methods: {
    toggle(id) {
      
      this.movies[0].open = false;
      this.movies[1].open = false;
      this.movies[2].open = false;
      this.movies[3].open = false;
      this.movies[0].hideImg = true;
      this.movies[1].hideImg = true;
      this.movies[2].hideImg = true;
      this.movies[3].hideImg = true;

      this.movies[id - 1].hideImg = !this.movies[id - 1].hideImg;
      this.movies[id - 1].open = !this.movies[id - 1].open;
      console.log(
        this.movies[id - 1].movieStart,
        "-",
        this.movies[id - 1].movieMid,
        "-",
        this.movies[id - 1].movieEnd
      );
    },
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin: 60px auto 0;
}
.watchBtn {
  background-color: red;
  border-radius: 10px;
  margin-left: 10px;
  height: 20px;
  display: flex;
  align-items: center;
}
.watchBtn:hover {
  background-color: rgb(255, 191, 107);
  cursor: pointer;
}
.movieContainer {
  margin: 5px auto;
  display: flex;
  flex-direction: column;
}
.center {
  margin: 0 auto;
  display: flex;
  align-items: center;
}
.duration {
  margin: 0 5px 0 10px;
}
.movieContainer video {
  width: 500px;
}
</style>

here’s StackBlitz

Solution – 1

...
toggle(id) {
  this.movies.forEach(movie => { movie.open = false, movie.hideImg = true })
  this.movies[id - 1].hideImg = false;
  this.movies[id - 1].open = true;
},
...

I would also make the following changes:

  • change v-if to v-show, it’s faster to show/hide DOM instead of adding/removing
  • Remove hideImg, and base all logic on a single value open
  • save the current state, so you can close the currently open item again

StackBlitz

<template>
  <div v-for="(movie, index) in movies" :key="index" class="movieContainer">
    <div class="center">
      {{ movie.name }}
      <button @click="toggle(movie.id)" class="watchBtn">
        <p v-show="!movie.open">►</p>
              👆           👆
        <p v-show="movie.open">▼</p>
              👆          👆
      </button>
    </div>
    <div v-show="movie.open">
      <!-- <video controls="controls" autoplay name="media">
        <source
          :src="require(`@/assets/movie${index + 1}.mp4`)"
          alt="video"
          type="video/mp4"
          width="500px"
        />
      </video> -->
      <h2>Test</h2>
    </div>
  </div>
</template>

<script>
export default {
  name: 'App',
  methods: {
    toggle(id) {
👉    const state = this.movies[id - 1].open;
👉    this.movies.forEach((movie) => (movie.open = false));
👉    this.movies[id - 1].open = !state;
    },
  },
  data() {
    return {
      movies: [
        {
          name: 'Pokemon',
          duration: '1hr 12min',
👉
          open: false,
          id: 1,
        },
        {
          name: 'Digimon',
          duration: '2hr 37min',
👉
          open: false,
          id: 2,
        },
        {
          name: 'Transformers',
          duration: '1hr 51min',
👉
          open: true,
          id: 3,
        },
        {
          name: 'Kiepscy',
          duration: '1hr 51min',
👉
          open: true,
          id: 4,
        },
      ],
    };
  },
};
</script>
Rate this post
We use cookies in order to give you the best possible experience on our website. By continuing to use this site, you agree to our use of cookies.
Accept
Reject