Search code examples
vue.jsuniqueinstanceelement

Vue.js getElementById() targets wrong instance of component


I am trying to build a little project in Vue.js, I have defined a child component which a parent component has six instances of.

The child component has a div element:

<div class="player" id="player1">

and a function which will change its own opacity:

changeOpacity(){document.getElementById('playerName').style.opacity = 0.5;}

The problem is when I tell the second instance of child component to execute its function, the first instance of child will change opacity.

My suspicion is it has something to do with unique div id's, that is by creating multiple instances of the child component the id: playerName is no longer unique.

Does anyone have a suggestion for how a child instance can access its own component elements without interfering with other instances of itself?


Solution

  • As you've noted, this is because ids are global and getElementById will return the first element in the DOM with that id. It doesn't know anything about youe Vue component boundaries.

    The correct way to access an element within a component is using $refs, not getElementById.

    <div ref="playerName">...</div>
    
    changeOpacity () {
      this.$refs.playerName.style.opacity = 0.5
    }
    

    However, directly manipulating the DOM isn't a very Vue way of doing this. Instead you should bind the opacity to a data property and change that.

    <div :style="{ opacity }">...</div>
    
    data () {
      return {
        opacity: 1
      }
    }
    
    changeOpacity () {
      this.opacity = 0.5
    }
    

    In full:

    new Vue({
      el: '#app',
    
      data () {
        return {
          opacity: 1
        }
      },
      
      methods: {
        changeOpacity () {
          this.opacity = 0.5
        }
      }
    })
    <script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
    
    <div id="app">
      <div :style="{ opacity }">
        Content
      </div>
      <button @click="changeOpacity">Change</button>
    </div>

    If it's just a specific value for opacity, like 0.5, then you should really use a CSS class instead. That would look something like this:

    new Vue({
      el: '#app',
    
      data () {
        return {
          halfHide: false
        }
      },
      
      methods: {
        changeOpacity () {
          this.halfHide = true
        }
      }
    })
    .half-hide {
      opacity: 0.5;
    }
    <script src="https://unpkg.com/[email protected]/dist/vue.js"></script>
    
    <div id="app">
      <div :class="{ 'half-hide': halfHide }">
        Content
      </div>
      <button @click="changeOpacity">Change</button>
    </div>