0

I'm trying to change the CSS class of a div based on if an image is loaded. The image URL is taken from an <input> field, so <img src> can change. I've managed to set the CSS class on the first page-load using the @load event. But if I change the image's URL in the input field to a non-existent image, then the CSS doesn't change. How do I track if the input's value has changed and "re-check" if the image is loaded?

In the below example, I want to have green-bg if the image exists and red-bg if the image doesn't exist.

 <div id="app">

    <div :class="imgLoaded ? 'green-bg' : 'red-bg'">
      <img :src=imgURL @load="imgLoaded = true" />
      <br/>
      <input v-model="imgURL" />
    </div>
    
  </div>
<script>
export default {
  data() {
    return {
      imgURL: 'https://picsum.photos/200/300',
      imgLoaded: false,
    };
  }
};
</script>

Link to CodePen

tony19
  • 125,647
  • 18
  • 229
  • 307
veesar
  • 422
  • 3
  • 11

3 Answers3

1

Add to you input:

<input type="text" v-model="imgURL" @input="updateURL">

and create function on methodth section:

updateURL() {
    this.imgLoaded = false
}
Skaneris
  • 29
  • 3
  • I couldn't exactly get this to work. `imgLoaded` changes to false but the CSS class did not change. Also `imgLoaded` would not change to true again when input was changed such that @load was true again. – veesar Nov 24 '21 at 15:30
1

This is a nice place to use a watcher.

watch: {
    imgURL(newVal, oldVal) {
      if (newVal !== oldVal) this.imgLoaded = false
    }
  }

Hooking to the input event would work too, but there is an edge case where the new value is similar to the old value. If you then set the imgLoaded to false, the CSS class won't change to green-bg because the load event does not fire (since the url did not change).

When using a watcher you can compare the old value to the new value and only set imgLoaded to false if the values are different.

Here is the pen.

gijswijs
  • 1,958
  • 19
  • 24
  • Thank you. This works, but how I can change the value of `imgLoaded` if it is in an array. I've got as far as [watching values in arrays](https://v3.vuejs.org/guide/migration/watch.html#_3-x-syntax), but not how to update the value. I've [updated your pen](https://codepen.io/veesar/pen/bGrPYVG) with this question. – veesar Nov 24 '21 at 16:12
  • With arrays you open yourself up for a world of hurt. I have fixed some issues in your code so that it *does* fire on a changed url, but the problem with deep watching an array is that you can't get the old value of the array, so there's nothing to compare it to. https://codepen.io/gijswijs/pen/abyepea See this answer for a possible solution (by cloning the array into a string and watch that string) https://stackoverflow.com/a/50696834/119129 In the mean time, could you accept this answer as the correct one, since it does answer the initial question. – gijswijs Nov 26 '21 at 02:15
  • 1
    Thanks for the additional info! I've accepted your answer as it does indeed answer the original question: "How do I track if the input's value has changed and "re-check" if the image is loaded?". I'm also linking to [this answer](https://stackoverflow.com/a/70098751/16094181) which checks if the image exists or not and solves the same problem differently. Is there a difference in performance between the two methods? – veesar Nov 26 '21 at 10:34
1

I managed to solve my issue by using the @error method. imgLoaded would be set to false on error and set to true on load.

<div id="app">

    <div :class="imgLoaded ? 'green-bg' : 'red-bg'">
      <img :src=imgURL @load="imgLoaded = true" @error="imgLoaded = false" />
      <br/>
      <input v-model="imgURL" />
    </div>
    
  </div>

This works for the case where the CSS class is changed based on whether the image exists or not.

veesar
  • 422
  • 3
  • 11