Previously added image in the dom start showing the spinner when new image is added

32 Views Asked by At

I have this simple app where I am showing images and also can add more. So when I add an image I show a spinner while it loads and then hide the spinner when its done loading. But the issue is when I'm adding the second image, the spinner doesn't show up. This is because of the boolean I'm using and I think it is being used globally.

Please note that I add an image through image url and rendering it with img html tag of course. I'm using RactiveJS and I feel like there is something I can do with an unique identifier but just dont know how. All the images have unique id attached to its data.

Here's the Ractive code:

let isImageLoaded = false;

const ractive = new Ractive({
  el: '#container',
  template: `
    {{#each images}}
      <div class="container">
        {{#if !isImageLoaded}}
           <span class="spinner"></span>
        {{/if}}

         <img 
            alt="" 
            src="{{url}}" 
            on-load="imageLoaded"
            style="display: {{isImageLoaded ? 'block' : 'none'}};"
         />
      </div>
    {{/each}}
  `,
  data: {
    images: [
      { id: 1, url: 'https://www.some-image.com/image1.jpg' },
    ],
    isImageLoaded : isImageLoaded
  }
});

ractive.on('imageLoaded', function(e) {
  ractive.set('isImageLoaded', true); 
});


function addImage(image) {
  const allImages = ractive.get('images');

  allImages.push(images);

  ractive.set('isImageLoaded', false); 
  ractive.set('images', allImages);
}

If I set isImageLoaded to false in addImage function then adding a new image makes all the other spinners to show up.

How can I use ids to make each image and spinner unique and show spinner only when adding a new image?

2

There are 2 best solutions below

0
Julian On

You need to track for each image separately whether it has been loaded or not. One possible way is to turn isImageLoaded into an object, with image IDs as keys and booleans as values.

let isImageLoaded = {};

const ractive = new Ractive({
  el: '#container',
  template: `
    {{#each images}}
      <div class="container">
        {{#if !isImageLoaded[id]}}
           <span class="spinner"></span>
        {{/if}}

         <img 
            alt="" 
            src="{{url}}" 
            on-load="imageLoaded"
            style="display: {{isImageLoaded[id] ? 'block' : 'none'}};"
         />
      </div>
    {{/each}}
  `,
  data: {
    images: [
      { id: 1, url: 'https://www.some-image.com/image1.jpg' },
    ],
    isImageLoaded : isImageLoaded
  }
});

ractive.on('imageLoaded', function(e) {
  ractive.set(`isImageLoaded[${e.context.id}]`, true); 
});


function addImage(image) {
  const allImages = ractive.get('images');

  allImages.push(images);

  ractive.set('images', allImages);
}
0
ZenitoGR On

add a loaded: false in each image object

add a function imageLoaded AFTER data

change the {{#if}} statement to:

{{#if !.loaded}}
   <span class="spinner"></span>
{{/if}}

call it with on-load="@this.imageLoaded(@index)"

in the if you put .loaded cause you already iterate with each images

the imageLoaded function AFTER data is as follows:

imageLoaded: function(i){
    this.set('images['+i+'].loaded',true);
  }

in style you put:

style="display: {{.loaded ? 'block' : 'none'}};"

again as loaded is a key of the item from each images

I also added in data the var lastImageID where I store the highest id so I can add +1 to the next added image.

also I include a addImage(num) implementations and the code runs fine!

tip: you can call outside functions with onclick and ractivejs functions with on-click for example in a button

.spinner {
        background-color:black;
        width: 48px;
        height: 48px;
        border: 5px solid #FFF;
        border-bottom-color: transparent;
        border-radius: 50%;
        display: inline-block;
        box-sizing: border-box;
        animation: rotation 1s linear infinite;
        }
    
        @keyframes rotation {
          0% {
            transform: rotate(0deg);
          }
          100% {
            transform: rotate(360deg);
          }
        }
<script src="https://cdnjs.cloudflare.com/ajax/libs/chance/0.5.6/chance.min.js"></script>
    <script src='https://cdn.jsdelivr.net/npm/ractive'></script>



        <div id="target" ></div>
        <script id='template' type='text/ractive'>
        {{#each images}}
              <div >
                {{#if !.loaded}}
                   <span class="spinner"></span>
                {{/if}}
                 <img 
                    alt="" 
                    src="{{url}}" 
                    on-load="@this.imageLoaded(@index)"
                    style="display: {{.loaded ? 'block' : 'none'}};"
                 />
              </div>
            {{/each}}
            <button onclick="addImage(1)">add 1 images</button>
            <button onclick="addImage(2)">add 2 images</button>
            <button onclick="addImage(5)">add 5 images</button>
            <button onclick="addImage(10)">add 10 images</button>
        </script>
        
        <script>
        const ractive = new Ractive({
          el: '#target',
          template: '#template',
          data: {
            images: [
              { id: 1, url: 'https://randomuser.me/api/portraits/med/women/52.jpg',loaded:false },
            ],
          },
          imageLoaded: function(i){
          //simulate that image takes some time to load with setTimeout
          setTimeout(() => { this.set('images['+i+'].loaded',true); }, 1000);
            
          }
        });
        
        
        function addImage(num) {
          for (var i = 1; i<=num; i++) {
                var gender = chance.bool() ? 'men' : 'women';
                ractive.push('images',
              {
                id: [...ractive.get('images')].pop().id+1,
                url: 'https://randomuser.me/api/portraits/med/' + gender + '/' + (chance.natural() % 100) + '.jpg',
                loaded: false
              }
            );
          }
        }
        
       
        </script>