14

I made some code where the user can upload some images from a zip. On the next page I need to show all the images seperatly in a 85*85 px frame.

The problem is that it may take some time for all the images to load. So I want to show a loading gif while the user waits for the image to load.

I've set the src of the images to be the loading gifs, while I created some checkboxes with the real source as id

echo "<td><img src=\"beelden/ajax-loader-black-16.png\" id=\"img".$image."\" style=\" width: 85px; height: 85px; border: 1px solid gray; background-color: #fff; padding: 10px;\">";
echo "<input type=\"checkbox\" id=\"img[".$image."]\" name=\"check_image[]\" value=\"".$filename."\" /></td>";
<input type="hidden" name="aantal" id="aantal" value="<?=$image?>" >

Then I created some javascript to check if the image is loaded, and when it is, it is supposed to replace the source of the image.

<script>
    var aantal = document.getElementById("aantal").value;
    for(var i = 0; i < aantal; i++){
        var source = document.getElementById("img["+i+"]").value;
        var img = new Image();
        img.onload = function(){
            $("#img"+i).attr('src', source);
        }();
        img.src = source;
    }
</script>

But this does not work the way I expected, I think it fires for all of the images as soon as the first one is loaded. Any ideas what I am doing wrong or how to fix this?

PirateSoul
  • 255
  • 1
  • 2
  • 10
  • how you call your javascript function?? – Saty Apr 20 '15 at 08:18
  • 4
    The classic case of loop variable under closure. – Amadan Apr 20 '15 at 08:18
  • 1
    create a fiddle for quick edit and demo – codesnooker Apr 20 '15 at 08:20
  • Can you create a fiddle or a plunker? you write ` document.getElementById("img["+i+"]").value;` and `$("#img"+i).` so confusing – Bellash Apr 20 '15 at 08:22
  • Consider NOT to use a closure, since in this case would definately cause a memory leak on the DOM-node. Declare an external function instead that takes `i` as an argument, and just call that in every loop instead. – Eric Apr 20 '15 at 08:22
  • possible duplicate of [How to show loading gif while image preview loading via javascript](http://stackoverflow.com/questions/17901159/how-to-show-loading-gif-while-image-preview-loading-via-javascript) – roryok Apr 20 '15 at 08:23
  • Oh, and another thing that I didn't even notice - the function is executed immediately (see `()` at the end), and `img.onload` is assigned the exit value - which is `undefined`. Closed-over loop variable doesn't even have time to become a bug. – Amadan Apr 20 '15 at 08:24

2 Answers2

50

You can set the background of an image to the loading gif. It is a simple css trick. You wouldn't need then to make a js script.

.loading {
  background: transparent url('http://thinkfuture.com/wp-content/uploads/2013/10/loading_spinner.gif') center no-repeat;
}
<img class="loading" src="http://placehold.it/106&text=1" width="106px" height="106px" />
<img class="loading" src="http://placehold.it/106&text=2" width="106px" height="106px" />
<img class="loading" src="http://placehold.it/106&text=3" width="106px" height="106px" />
<img class="loading" src="http://placehold.it/106&text=4" width="106px" height="106px" />
<img class="loading" src="http://placehold.it/106&text=5" width="106px" height="106px" />
<img class="loading" src="http://placehold.it/106&text=6" width="106px" height="106px" />
<img class="loading" src="http://placehold.it/106&text=7" width="106px" height="106px" />

Update :

In case you have transparent images then the story becames a bit more complicated but, still can be done with css and some div elements.

.image-wrapper {
  overflow: hidden;
  width: 106px;
  height: 106px;
  display: inline-block;
}

.image-wrapper img {
  float: left;
  display: block;
  opacity: 0.2; /* simulating a semitransparent image */
}

.image-wrapper:after, .loading {
  content: ' ';
  background: transparent url('http://thinkfuture.com/wp-content/uploads/2013/10/loading_spinner.gif')  center no-repeat ;
  background-size : auto 100%;
  width: 106px;
  height: 106px;
  float: left;
  display: block;
}
<div class="image-wrapper">
  <!-- simulates a hard loading image -->
  <img src="http://placehold.it/not-existing" alt="" />
</div>
<div class="image-wrapper">
  <img src="http://placehold.it/106x106&text=2" alt="" />
</div>
<div class="image-wrapper">
  <img src="http://placehold.it/106x106&text=3" alt="" />
</div>
<div class="image-wrapper">
  <img src="http://placehold.it/106x106&text=4" alt="" />
</div>
<div class="image-wrapper">
  <img src="http://placehold.it/106x106&text=5"  alt="" />
</div>
<div class="image-wrapper">
  <img src="http://placehold.it/106x106&text=6"  alt="" />
</div>
<div class="image-wrapper">
  <img src="http://placehold.it/106x106&text=7"  alt="" />
</div>

Unfortunately the browser adds a broken icon or a ? while loading, this is why the image contains an empty alt;

Update 2 :

The second variant relies very much on the image size, if you have difrent sizes than the loading gif won't be pushed away properly, as an alternative would be to use the first variant and a little js script that will remove the background as soon as the image is loaded:

$('img').load(function(){
   $(this).css('background','none');
});
   .loading {
      background: transparent url('http://thinkfuture.com/wp-content/uploads/2013/10/loading_spinner.gif') center no-repeat;
    }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img class="loading" src="http://upload.wikimedia.org/wikipedia/en/2/2d/SRU-Logo-Transparent.png" width="106px" height="106px" />
    <img class="loading" src="http://placehold.it/106&text=2" width="106px" height="106px" />
    <img class="loading" src="http://placehold.it/106&text=3" width="106px" height="106px" />
    <img class="loading" src="http://placehold.it/106&text=4" width="106px" height="106px" />
    <img class="loading" src="http://placehold.it/106&text=5" width="106px" height="106px" />
    <img class="loading" src="http://placehold.it/106&text=6" width="106px" height="106px" />
    <img class="loading" src="http://placehold.it/106&text=7" width="106px" height="106px" />
Tiberiu C.
  • 3,365
  • 1
  • 30
  • 38
12

Angular 8 Solution with Hostlistener and Hostbinding.

1. Create a simple attribute directive

import { Directive, HostListener, Input, HostBinding } from '@angular/core';
@Directive({selector: '[imageLoader]'})
export class ImageLoaderDirective
{
  @Input('src') imageSrc;
  @HostListener('load')
  loadImage()
  {
  this.srcAttr=this.imageSrc;
  }

@HostBinding('attr.src') srcAttr="../../assets/pics/Loader.svg"
constructor() { }
}

Basically we set the initial image source to the loader image. Once the image loads(load event triggered), we listen to it and set the image source to the actual image.

2. Now use the just need to use the created attributes on your images.

<img imageLoader src='image.jpg'>

Using svg requires no styling changes, gif may require css changes.

You can visit the website for working implementation.

Dragonknot
  • 292
  • 4
  • 10
  • Can you provide the full directive code for this? Thank you – fromage9747 Jun 16 '20 at 05:10
  • @fromage9747 I have updated the code in the answer itself. Please upvote if it was helpful. – Dragonknot Jun 16 '20 at 10:32
  • Thank you! I just can't seem to find a way to test it to see if it's working. Even when setting my network speed to slow 3g in chrome it doesn't show the loader. – fromage9747 Jun 19 '20 at 16:42
  • @fromage9747 it might be related to image sizes you are trying to show , not sure though. For reference , I have this website- https://valleyviewhomestays.firebaseapp.com . For me , loader shows (but my image jpgs are in MBs). I would suggest you to apply the loader with an image of higher resolution and size for testing purposes. – Dragonknot Jun 20 '20 at 14:21
  • Although the question did not specifically ask for an Angular solution, I was happy to find one here. I consider your answer a good baseline for an Angular approach to the issue. Thanks! – David Brem Jul 01 '20 at 10:52