最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - v-for causing actions to be applied to all divs - Stack Overflow

programmeradmin1浏览0评论

Previously I asked a question about removing a custom truncate filter in Vue. Please see the question here:

Removing a Vue custom filter on mouseover

However, I neglected to mention that I am using a v-for loop and when I hover over one div, I am noticing that all the divs in the loop are having the same action applied to them. I'm not sure how to target only the div that is being hovered over. Here is my template:

 <div id="tiles">
    <button class="tile" v-for="(word, index) in shuffled" @click="clickWord(word, index)" :title="word.english">
      <div class="pinyin">{{ word.pinyin }}</div>
      <div class="eng" @mouseover="showAll = true" @mouseout="showAll = false">
        <div v-if="showAll">{{ word.english }}</div>
        <div v-else>{{ word.english | truncate }}</div>
      </div>
    </button>
  </div>

And the data being returned:

  data(){
    return {
      currentIndex: 0,
      roundClear: false,
      clickedWord: '',
      matchFirstTry: true,
      showAll: false,
    }
  },

If you know Vue, I would be grateful for advice. Thanks!

Previously I asked a question about removing a custom truncate filter in Vue. Please see the question here:

Removing a Vue custom filter on mouseover

However, I neglected to mention that I am using a v-for loop and when I hover over one div, I am noticing that all the divs in the loop are having the same action applied to them. I'm not sure how to target only the div that is being hovered over. Here is my template:

 <div id="tiles">
    <button class="tile" v-for="(word, index) in shuffled" @click="clickWord(word, index)" :title="word.english">
      <div class="pinyin">{{ word.pinyin }}</div>
      <div class="eng" @mouseover="showAll = true" @mouseout="showAll = false">
        <div v-if="showAll">{{ word.english }}</div>
        <div v-else>{{ word.english | truncate }}</div>
      </div>
    </button>
  </div>

And the data being returned:

  data(){
    return {
      currentIndex: 0,
      roundClear: false,
      clickedWord: '',
      matchFirstTry: true,
      showAll: false,
    }
  },

If you know Vue, I would be grateful for advice. Thanks!

Share Improve this question asked Jul 19, 2017 at 18:27 lnambalnamba 1,7313 gold badges20 silver badges26 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 8

In your example, showAll is being used for each of the buttons generated by the v-for to determine whether or not to show the plete text of the word.english value. This means that whenever the mouseover event of any the .eng class divs fires, the same showAll property is being set to true for every button.


I would replace the showAll Boolean value with a showWordIndex property initially set to null:

data() {
  showWordIndex: null,
},

And then in the template, set showWordIndex to the index of the word on the mouseover handler (and to null in the mouseleave handler):

<button v-for="(word, index) in shuffled" :key="index">
  <div class="pinyin">{{ word.pinyin }}</div>
  <div 
    class="eng" 
    @mouseover="showWordIndex = index" 
    @mouseout="showWordIndex = null" 
  >
    <div v-if="showWordIndex === index">{{ word.english }}</div>
    <div v-else>{{ word.english | truncate }}</div>
  </div>
</button>

Here's a working fiddle.


Even better would be to make a new ponent to encapsulate the functionality and template of everything being rendered in the v-for, passing the properties of each word object to the child ponent as props.

This way, you would still use the showAll property like you are in your example, but you would define it in the child ponent's scope. So now the showAll property will only affect the instance of the ponent it's related to.

Below is an example of that:

Vue.ponent('tile', {
  template: '#tile',
  props: ['pinyin', 'english'],
  data() {
    return { showAll: false };
  },
  filters: {
    truncate: function(value) {
      let length = 50;
      if (value.length <= length) {
        return value;
      } else {
        return value.substring(0, length) + '...';
      }
    }
  },
})

new Vue({
  el: '#app',
  data() {
    return {
      words: [
        {pinyin: 1, english: "really long string that will be cut off by the truncate function"},
        {pinyin: 2, english: "really long string that will be cut off by the truncate function"},
        {pinyin: 3, english: "really long string that will be cut off by the truncate function"},
        {pinyin: 4, english: "really long string that will be cut off by the truncate function"},
      ],
    }
  }
})
<script src="https://cdnjs.cloudflare./ajax/libs/vue/2.4.1/vue.min.js"></script>
<div id="app">
  <tile v-for="word, i in words" v-bind="word" :key="word"></tile>
</div>

<script id="tile" type="x-template">
  <button :title="english">
    <div class="pinyin">{{ pinyin }}</div>
    <div class="eng" @mouseover="showAll = true" @mouseout="showAll = false">
      <div v-if="showAll">{{ english }}</div>
      <div v-else>{{ english | truncate }}</div>
    </div>
  </button>
</script>

In order to do this, you can't use a puted property (as I originally suggested in the answer of mine that you linked), since you need to be aware of the context that you are in. That said, you CAN use a filter if you apply a showAll property to each individual instance. If you declare this up front in your data model, the property will be reactive and you can toggle each item individually on mouseover and mouseout.

template:

<div id="app">
  <div id="tiles">
    <div class="tile" v-for="(word, index) in shuffled" :title="word.english">
      <div class="pinyin">{{ word.pinyin }}</div>
      <div class="eng" @mouseover="word.showAll = true" @mouseout="word.showAll = false">
        {{ word.english | truncate(word) }}
      </div>
    </div>
  </div>
</div>

js:

new Vue({
    el: '#app',
    data() {
        return {
            shuffled: [
                { english: 'here', showAll: false}, 
                { english: 'are', showAll: false }, 
                { english: 'there', showAll: false },
                { english: 'words', showAll: false }
            ],
            currentIndex: 0,
            roundClear: false,
            clickedWord: '',
            matchFirstTry: true,
        }
    },
    filters: {
        truncate: function(value, word) {
            console.log(word)
            let length = 3;
            if (word.showAll || value.length <= length) return value;

            return value.substring(0, length) + '...';
        }
    },
})

See working JSFiddle

The key is to apply showAll to each word instance and to then pass that word instance back to the filter so that we can check the value of the showAll property. As long as you declare it up front, Vue's reactivity system handles the rest for you.

Note that in this example it isn't necessary to use two elements with a v-if/else. A single element with a filter works perfectly.

发布评论

评论列表(0)

  1. 暂无评论