Vue.js can't toggle a font-awesome icon
I ran into this issue recently when using Vue.js 2.5.x with FontAwesome 5.5.x — the icon classes were not being updated as expected.
After switching from the FontAwesome Web Fonts + CSS implementation to SVG + JS, the following code no longer worked:
<i :class="[sortByFirstNameAsc ? 'fa-chevron-up' : 'fa-chevron-down', 'fa']"></i>
What would happen is that FontAwesome JavaScript would fire and wrap the <i>
tag and replace it with an SVG element, as in the following simplified example:
<span data-v-2614dbd6="">
<svg data-v-2614dbd6="" class="svg-inline--fa fa-caret-up" ...">
...
</svg>
<!-- <i data-v-2614dbd6="" class="fa fa-caret-up"></i> -->
</span>
Unfortunately, the active class was being toggled on the inner, hidden <i>
tag and not the outer, visible SVG element.
The workaround that restored the dynamic active class toggling was to wrap the FontAwesome icons in a span and use the v-show
directive, as illustrated in the following code snippet:
<span v-show="sortByFirstNameAsc"><i class="fa fa-caret-up"></i></span>
<span v-show="sortByFirstNameDesc"><i class="fa fa-caret-down"></i></span>
The FontAwesome documentation now recommends using their Vue component to avoid conflicts in the DOM:
Compatibility Heads Up! If you are using Vue you need the vue-fontawesome package or Web Fonts with CSS.
The SVG core package is helpful and recommended in the following cases:
- to subset a large number of icons into only the icons that you are using
- as base libraries for larger integrations with tools like React, Angular, Vue, or Ember (in fact our own components use these packages)
- as CommonJS/ES6 Modules bundling tool like Webpack, Rollup, or Parcel
- as UMD-style loader library like RequireJS
- directly on the server with CommonJS (see our Server Side Rendering docs
"i" tag comments out after fire turning to svg, use some wrap <span v-if="marked"><i class="far fa-check-square"></i></span>
The Font Awesome library you used doesn't know about Vue. It takes the <i>
that you wrote and turns it into an <svg>
, and at that point, it's been stolen from Vue. Vue no longer controls it. The answer that Dialog gave was a good one: wrap it with a <span>
. But then you pointed out another scenario it doesn't work for.
To solve your scenario where wrapping with a <span>
still doesn't work, use key="fa-sort-up"
. This will force Vue to re-render the wrapper, at which point Font Awesome will update the icon. Here's the updated jsFiddle for your example:
<span key="fa-sort-up" v-if="sort && descending"><i class="fas fa-sort-up"></i></span>
<span key="fa-sort-down" v-else-if="sort"><i class="fas fa-sort-down"></i></span>
<span key="fa-sort" v-else><i class="fas fa-sort"></i></span>
You can use anything you want for the key, as long as it's unique.