Clinton Green
Clinton Green

Reputation: 9977

Toggle class on click in VueJS

I am learning Vuejs and I am constantly finding simple things like removing a class to be a pain. Please tell me how I can allow the .active class to be added or removed based on clicks between the 3 links.

In the example below the adding is working fine but it adds .active to all the links, and does not remove when clicked on again.

<div id="app">
  <h2>App</h2>
  <ul>
    <li><a href="#" class="link" :class="{ active: isActive }" @click="activeLink">Link text</a></li>
    <li><a href="#" class="link" :class="{ active: isActive }" @click="activeLink">Link text</a></li>
    <li><a href="#" class="link" :class="{ active: isActive }" @click="activeLink">Link text</a></li>
  </ul>
</div>

JS

var app = new Vue({
  el: '#app',
  data: {
    isActive: false
},
methods: {
  activeLink() {
    // the line below did not work
    // document.getElementsByClassName("active").isActive = false,
    this.isActive = true
  }
 }
})

JSfiddle is here, https://jsfiddle.net/s9r4q0gc/

Upvotes: 3

Views: 25319

Answers (4)

cgraffeo
cgraffeo

Reputation: 63

Use V-for and an array of items so you have no need to statically type the links. This allows for the dynamic functionality you are looking for.

  var app = new Vue({
    el: '#app',
    data: {
      links: [
       {text: "Link Text", active: false},
       {text: "Link Text", active: false},
       {text: "Link Text", active: false}
      ]
    },
    methods: {
      activate(link) {
        link.active = !link.active
      }
    }
  })
.link{
  text-decoration: none;
  color: #555;
}
.active{
  text-decoration: underline;
  color: #42b983;
}
<div id="app">
      <h2>App</h2>
      <ul>
        <li v-for="link in links"><a href="#" class="link" :class="{ active: link.active }" @click="activate(link)">Link text</a></li>
      </ul>
    </div>

Upvotes: 3

Ash
Ash

Reputation: 2585

You need to catch the event handler in the method and using that you can refer to the callee i.e. anchor object in this case.

See the fiddle : https://jsfiddle.net/s9r4q0gc/2/

activeLink(event) {
    if(event.target.className == "noclass")
    {
        event.target.className = "link active";
    }
    else
    {
        event.target.className = "noclass";
    }
  }

UPDATED:

May be try this fiddle and see if it is hitting the bulls eye : https://jsfiddle.net/s9r4q0gc/4/

  var app = new Vue({
    el: '#app',
    data: {
      isActive: false
    },
    methods: {
      activeLink(event) {
        var checkboxes = document.getElementsByClassName ("noclass");

        for (var i=0; i<checkboxes.length; i++) {
           checkboxes[i].className = "link active";
           //checkboxes[i].className = "link";
        }
            event.target.className = "noclass";
      }
    }
  })

Upvotes: 7

Rommel Santor
Rommel Santor

Reputation: 1011

What you can do is use a data property to hold the currently active link. Then with that you'll be able to have any given link test if it is the active link and if the .active class should be applied.

Then it's just a matter of setting that property's new value when a link is clicked. If the same link that's currently active is clicked, then the property is cleared, thus removing the .active class from all links. Otherwise, the class is added to the link that was clicked.

This is a CodePen demonstrating what I mean in action, but your markup could look something like this:

<li><a href="#" class="link" :class="{ active: activeId == 'link-1' }"
    @click.prevent="activeLink('link-1')">Link text</a></li>

<li><a href="#" class="link" :class="{ active: activeId == 'link-2' }"
    @click.prevent="activeLink('link-2')">Link text</a></li>

<li><a href="#" class="link" :class="{ active: activeId == 'link-3' }"
    @click.prevent="activeLink('link-3')">Link text</a></li>

and your JS could look something like this:

data: {
  activeId: null
},
methods: {
  activeLink(linkIdent) {
    this.activeId = this.activeId === linkIdent ? null : linkIdent
  }
}

It's obviously not as clean a solution as it could be, but I am sticking to your requirement that the solution fit the markup you provided.

Upvotes: 4

mrogers
mrogers

Reputation: 1217

Here is an alternative solution that may modify your code more than you want. I thought this might be useful since you are just learning and may be interested in an alternative. I started by declaring your links as an array of objects in vue so that we can assign an active attribute to each of them. Then we can just toggle the active value inline or use the toggleActive function (not currently in use.. just there for illustration if you prefer to use functions over inline logic).

Html:

<div id="app">
  <h2>App</h2>
  <ul>
    <li v-for="l in links">
      <a href="#" class="link" :class="{ active: l.active}" @click="l.active = !l.active">{{l.text}}</a>
    </li>
  </ul>
</div>

Javascript:

var app = new Vue({
  el: '#app',
  data: {
    links: [{
      text: "Link text",
      active: false
    },{
      text: "Second text",
      active: false
    },{
      text: "Third text",
      active: false
    }]
  },
  methods: {
    //To use this function make @click in html to:
    //@click="toggleActive(l)"
    toggleActive(x) {
      x.active = !x.active;
    }
  }
})

https://jsfiddle.net/yrbt90v9/

Upvotes: 3

Related Questions