I want to change the background of my components based on the colors of the image inside the component. There are 20 instances of the component on the page, now all 20 instances get the same color, however they should all have their unique color. I don’t know how to do this. I tried using inline styling but that doesn’t work either.
The color is made when the image is loaded in the component. By this function in the component.
I tried it out with refs but it still changes all 20 cards instead of the 1. And I tried setting unique IDs based on pokemonid
, but that also did not work.
Here is the code.
The component is called pokemon-card
.
Component:
<template>
<div class="card">
<div class="card__front" :style="{'background' : `radial-gradient(circle at 50% 0%, ${cardCircleColor} 36%, #fff 36%)`}">
<div class="card__front__cardTop">
<div class="card__front__cardTop__id"><span></span>{{ id }}</div>
</div>
<img class="card__front__img"
:src="'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/other/official-artwork/' + id + '.png'"
alt="pokemonImage" crossorigin="anonymous">
<div class="card__front__name">
<h3>{{ name }}</h3>
</div>
</div>
<div class="card__back">
<router-link :to="`/pokemonDetails/${id}`" class="card__back__button">Details</router-link>
</div>
</div>
</template>
The color is made when the image is loaded in the component. By this function in the component
methods: {
//gets color for background based on pokemon image
getColor() {
const fac = new FastAverageColor();
fac.getColorAsync(document.querySelector(`.card__front__img`))
.then(color => {
// document.querySelector(`.card__front`).style.background = `radial-gradient(circle at 50% 0%, ${color.hex} 36%, #fff 36%)`
this.cardCircleColor = color.hex
})
},
},
mounted() {
this.getColor()
},
Relevant CSS of component (the color should be applied to radial-gradient (circle at 50% 0%, # 36%, #fff 36%);)
&__front {
transition: all 0.8s;
//background: radial-gradient(circle at 50% 0%, #90aac1 36%, #fff 36%);
//background-color: $cardBg_color;
Parent
<template>
<div class="container">
<div class="cardContainer">
<pokemon-card v-for="data in pokemonData.results" :name="data.name" :id="data.id"></pokemon-card>
</div>
<div class="PaginationcontrolContainer">
<div class="paginationControls">
<div @click="decreasePaginationCounter" class="paginationControls__btn"> < </div>
<input @change="manuallyChangingPaginationIndex" v-model="paginationIndex" type="number">
<div @click="increasePaginationCounter" class="paginationControls__btn"> > </div>
</div>
</div>
</div>
</template>
2
Answers
First, dont use query selectors inside vue, use
ref
Then use that ref to reference the image. Also in
v-for
use:key
Your question is a good example of why one should not manipulate DOM directly when using Vue.
The problem is here:
document.querySelector('.card__front__img')
will always return the first element matching the selector found in the entire DOM.Which means that each of the separate
fac
instances are finding the same exact element and using it as color source. The same input will generate the same output, 20 separate times.Instead, you should follow Vue’s guidelines on how to interact with DOM. Use template refs.
Specifically:
Now each component will reference its own
image
ref and, provided they load different images, results should vary.Also, make sure you follow @MichaelMano’s advice and
:key
yourv-for
‘s with unique identifiers. Although it’s not directly connected with the bug you’re currently experiencing, it’s a common source of subtle bugs, just like yours, which could consume hours, sometimes days, to understand and fix.Note: if my suggestion doesn’t fix the error, you should check
image
‘ssrc
at the time you’re runninggetColor()
, on each component, and make sure they’re what they should be (e.g.console.log(this.$refs.image.getAttribute('src'))
)Note for future questions: your question would have been easier to answer if you provided a runnable minimal reproducible example.