Implement SVG Hover Color Change with a Vue Custom Directive
This article explains how to simplify SVG hover color changes in Vue by creating a reusable custom directive that dynamically modifies the fill attribute or swaps SVG markup, reducing duplicated markup and improving maintainability for both simple and complex SVG graphics.
In front‑end development, a common requirement is to change the style of an SVG image on hover. The naïve approach is to embed two separate SVGs and toggle their visibility with v-show , which quickly becomes cumbersome.
For simple SVGs whose appearance can be controlled solely by the fill attribute, you can change the color directly without duplicating the markup. The article demonstrates a more elegant solution by creating a Vue custom directive that handles the hover effect.
<template>
<div>
<button @click="toggleColor">切换svg颜色</button>
<svg v-show="isRed" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red"/>
</svg>
<svg v-show="!isRed" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="blue"/>
</svg>
</div>
</template>
<script>
export default {
data() {
return { isRed: true };
},
methods: {
toggleColor() {
this.isRed = !this.isRed;
}
}
};
</script>To avoid this verbosity, the article introduces a custom directive named v-color . By applying the directive to the parent element of the SVG, the directive’s lifecycle hooks can access the SVG DOM node and modify its fill on mouseenter/mouseleave.
// directives/color.js
const color = {
// other hooks omitted for brevity
beforeMount(el, binding) {
const svg = el.querySelector('svg');
const original = svg.style.fill;
el.onmouseenter = () => { svg.style.fill = binding.value; };
el.onmouseleave = () => { svg.style.fill = original; };
}
};
export default {
install(app) {
app.directive('color', color);
}
};Registration in main.js :
import { createApp } from 'vue';
import App from './App.vue';
import color from './directives/color';
const app = createApp(App);
app.directive('color', color);
app.mount('#app');Usage in a component:
<template>
<div v-color="'blue'">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100" height="100">
<circle cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red"/>
</svg>
</div>
</template>For complex SVGs where multiple attributes need to change, the directive can accept an array containing two SVG strings (normal and hover states) and replace the parent element’s innerHTML on hover.
// directives/color.js (complex case)
const color = {
beforeMount(el, binding) {
const [normalSvg, hoverSvg] = binding.value;
el.onmouseenter = () => { el.innerHTML = hoverSvg; };
el.onmouseleave = () => { el.innerHTML = normalSvg; };
}
};Importing SVGs as raw strings (e.g., using Vite’s ?raw query) enables this approach.
In summary, the article provides a reusable Vue directive that simplifies SVG hover effects by either adjusting the fill attribute for simple graphics or swapping entire SVG markup for more complex cases, improving code maintainability and reducing duplication.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.