fencepencil
fencepencil

Reputation: 407

Vue v-for: iterate one element individually in an array

I'm looking to loop through an array of span tags and add is-active to the next one in line, every 3 seconds. I have it working but after the first one, it adds all the rest. How do I just pull that class from the active one and add it to the next array item?

I've read through the official documentation several times and there doesn't seem to be any mention of iterating individual items, just listing them all or pushing an item onto the list.

I'm not sure if 'index' comes in to play here, and how to grab the index of the span element to add/subtract is-active. what am I doing wrong?

var firstComponent = Vue.component('spans-show', {
  template: `
<h1>
	<span class="unset">Make</span>
	<br>
	<span class="unset">Something</span>
    <br>
	<span v-for="(span, index) of spans" :class="{ 'is-active': span.isActive, 'red': span.isRed, 'first': span.isFirst }" :key="index">{{ index }}: {{ span.name }}</span>
</h1>
`,

  data() {
    return {
      spans: [
        {
          name: 'Magical.',
          isActive: true,
          isRed: true,
          isFirst: true
        },
        {
          name: 'Inspiring.',
          isActive: false,
          isRed: true,
          isFirst: true
        },
        {
          name: 'Awesome.',
          isActive: false,
          isRed: true,
          isFirst: true
        }
      ]
    };
  },

  methods: {
    showMe: function() {
      setInterval(() => {
        // forEach
        this.spans.forEach(el => {
          if (el.isActive) {
            el.isActive = false;
          } else {
            el.isActive = true;
          }
        });
      }, 3000);
    }
  },

  created() {
    window.addEventListener('load', this.showMe);
  },

  destroyed() {
    window.removeEventListener('load', this.showMe);
  }
});

var secondComponent = Vue.component('span-show', {
  template: `
<span v-show="isActive"><slot></slot></span>
`,

  props: {
    name: {
      required: true
    }
  },

  data() {
    return {
      isActive: false
    };
  }
});

new Vue({

  el: "#app",
  components: {
    "first-component": firstComponent,
    "second-component": secondComponent
  }
});
.container {
  position: relative;
  overflow: hidden;
  width: 100%;
}

.wrapper {
  position: relative;
  margin: 0 auto;
  width: 100%;
  padding: 0 40px;
}

h1 {
  font-size: 48px;
  line-height: 105%;
  color: #4c2c72;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-family: archia-semibold, serif;
  font-weight: 400;
  margin: 0;
  height: 230px;
}

span {
  position: absolute;
  clip: rect(0, 0, 300px, 0);
}

span.unset {
  clip: unset;
}

span.red {
  color: #e43f6f;
}

span.is-active {
  clip: rect(0, 900px, 300px, -300px);
}
<div id="app">
  <div class="container">
    <div class="wrapper">
      <spans-show>
        <span-show></span-show>
      </spans-show>
    </div>
  </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.min.js"></script>

Upvotes: 0

Views: 970

Answers (1)

aBiscuit
aBiscuit

Reputation: 4732

To achieve desired result, I'd suggest to change the approach a bit.

Instead of changing value of isActive for individual items, we can create a variable (e.g. activeSpan, that will be responsible for current active span and increment it over time.

setInterval(() => {
  // Increment next active span, or reset if it is the one
  if (this.activeSpan === this.spans.length - 1) {
    this.activeSpan = 0
  } else {
    this.activeSpan++
  }
}, 3000);

In component's template, we make class is-active conditional and dependent on activeSpan variable:

:class="{ 'is-active': index === activeSpan, 'red': span.isRed, 'first': span.isFirst }"

If you still need to update values inside spans array, it can be done in more simple way, via map for example. Also included such case as optional in solution below.

Working example: JSFiddle

Sidenote: there is no need to add window listeners for load event, as application itself is loaded after DOM is ready. Instead, method can be invoked inside created hook. It is included in solution above.

Upvotes: 1

Related Questions