0

I'm having some problems showing my loading icon, before a function is executing. It seems to be showing at the same time my new elements are being appended to the DOM. I'm expecting to have many elements, so a loading icon is necessary as these new elements are being built.

A breakdown of what I want to happen

  1. Show loading icon
  2. Build new elements and append to DOM
  3. Hide loading icon

Generate Dummy Elements

<div class="loader"></div>
<button>Duplicate Items</button>

<section class="grid">
    <?php for ($i = 0; $i < 1000; $i++) :
        $randH = rand(1, 10);
        $randW = rand(1, 10);
        $imgUrl = 'http://placehold.it/' . $randH . 'x' . $randW;
    ?>
        <article class="grid__item">
            <img src="<?php echo $imgUrl; ?>" height="<?php echo $randH; ?>" width="<?php echo $randW; ?>">
            <p>Hello <?php echo $i; ?></p>
        </article>
    <?php endfor;?>
</section>

<section class="gallery></section>

Javascript Functions

let createGallery = () => {
    let gridItems = document.querySelectorAll('.grid__item'),
        gallery = document.querySelector('.gallery'),
        gridArray = Array.prototype.slice.call(gridItems, 0);

    // create new elements
    gridArray.forEach((item, index) => {
        let galleryItem = document.createElement('article'),
            img = item.querySelector('img').attributes,
            markup = {
                caption: item.querySelector('p').innerHTML
            },
            template = `<article class="gallery__item">
                            <img class="gallery__item__img" src="{{src}}" height="{{height}}" width="{{width}}">
                            <figcaption>{{caption}}</figcaption>
                        </article>`;

        for (let i = 0; i < img.length; i++) {
            markup[img[i].nodeName] = img[i].nodeValue;
        }

        Object.keys(markup).forEach(function(key) {
            let regEx = new RegExp('\{\{' + key + '\}\}', 'g');
            template = template.replace(regEx, markup[key]);
        });

        galleryItem.innerHTML = template;
        galleryItem.className = 'gallery__item ' + index;
        gallery.appendChild(galleryItem);
    })

    // after all images have been added > hide loader
    $('.loader').removeClass('is-active');
};

let initGallery = () => {
    // show loader, before creating gallery
    $('.loader').addClass('is-active');

    // create new elements
    createGallery();
}

let $button = document.querySelector('button');
$button.addEventListener('click', initGallery);
deleteddeleted
  • 155
  • 1
  • 13
  • there's a difference between the loop completing and the images being loaded from server – charlietfl Oct 21 '15 at 23:49
  • I've tested with not loading images as well, and it does not make a difference @charlietfl – deleteddeleted Oct 21 '15 at 23:52
  • since none of your code is asynchronous and javascript is single threaded there is no way for the `removeClass()` to fire before the loop completes. Perhaps you want to use an image preloader to tarck images are avilable – charlietfl Oct 21 '15 at 23:53
  • I don't think the problem is with `removeClass()`, but rather with `addClass()`, if all my code is synchronous, then shouldn't the code execute as 1.`addClass()`, 2. `createGallery()`? @charlietfl – deleteddeleted Oct 21 '15 at 23:57
  • that's my problem – it's not... and i'm trying to figure out why @charlietfl – deleteddeleted Oct 21 '15 at 23:59
  • try wrapping the call of `createGallery()` in really short `setTimeout()` – charlietfl Oct 22 '15 at 00:01
  • I've tried that, and it works.. but it feels a little hackish. especially when all of my code is synchronous @charlietfl – deleteddeleted Oct 22 '15 at 00:03
  • the code is but the dom repaint might be needing that really short breather – charlietfl Oct 22 '15 at 00:05
  • Changes to the DOM don't get rendered until after all the Javascript returns. This allows your script to make many changes without the user seeing all the intermediate steps. – Barmar Oct 22 '15 at 00:05
  • It looks like the code is working exactly as you have written it. Without any async code, your app doesn't have time to render the loader, as it is busy working on your loops. It is also likely running removeClass very soon after add. – Matt Way Oct 22 '15 at 00:06
  • What you need to do is use an `onload` function in the images that counts down the number of images that have been loaded. When the countdown reaches 0, the last `onload` function can remove the loader icon. – Barmar Oct 22 '15 at 00:08
  • Yeah that's what I assumed was happening. Do you know if it's possible to make it work as I want, without the `setTimeout()` on `createGallery()`. I would rather it paint each element at time rather than all at once @charlietfl – deleteddeleted Oct 22 '15 at 00:14
  • That makes sense, not really sure how to rewrite it to be async though @MattWay – deleteddeleted Oct 22 '15 at 00:17
  • for anyone that has the same problem, reading this helped me understand what was going on http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful – deleteddeleted Oct 22 '15 at 00:33

0 Answers0