0

Firstly, I have been researching this for a while, but I feel like this problem is a little beyond my abilities to solve on my own. Or at least, beyond my experience at this point, as I have never done this before.

I have a <div> that contains an <img> tag which I use JavaScript to change the source of to create a simple slideshow.

The Simple HTML:

<div id="slideShowWrapper">
    <img id="slideShowImage" src="~/Images/City_Images/Okmulgee_Clock_2.jpg" alt="Okmulgee Clock" title="Slide Show Paused" />
</div>

The problem I am having is that when a user visits the site for the first time, the images take too long to load during their first pass through the slideshow, and it throws of the timing of the slideshow. Once the browser has the images cached, it works fine. If there were a way to load all of the pictures before the slideshow starts I believe it would fix the problem. During testing, this problem never arose because the images were already cached by my browser so no excessive loading time was needed.

Here is the JavaScript (jQuery) I use for the slide-show. The images originally come from a server-side database so I use AJAX to get the file name values I need (currently there are only 14 entries/pictures in the database, but this number could increase or decrease as site admins will be able to edit the pictures in the slideshow through a partial CMS). To explain some of the code, this slide-show has the functionality to pause on mouse-over and resume on mouse-out.

jQuery(function ($) {
    //Slideshow functionality
    var paths = new Array();
    var timer = new Array();
    var pathsString = "";
    var i = 1;
    var panel = $("img#slideShowImage");
    var fTimer;
    var tTimer;
    var t2Timer;
    var fadingOut = false;
    var slideShowOn = false;
    var showTimer;

    $.ajax({
        url: "/AJAX Pages/Compute_Slide_Show.cshtml",
        async: false,
        type: "GET",
        success: function (response) {
            paths = response.split("/*\\");
        },
        error: function (jqXHR, textStatus, error) {
            paths[0] = "Okmulgee_Clock_2.jpg";
        }
    });

    if (paths.length > 0) {
        panel.attr("src", "/Images/SlideShowPics/" + paths[0])
        if (paths.length > 1) {
            swapImage();
        }
    }
    else {
        panel.attr("src", "/Images/City_Images/Okmulgee_Clock_2.jpg");
    }

    panel.mouseout(function () {
        if (paths.length > 1) {
            runSlideShow();
        }
    });

    panel.mouseover(function () {
        if (paths.length > 1) {
            stopSlideShow();
        }
    });

    function runSlideShow() {  //Calls the swapImage function to begin or resume the slide show
        if (slideShowOn == false) {
            slideShowOn = true;
            if (fadingOut == false) {
                clearTimeouts();
            }
            showTimer = setTimeout(swapImage, 1552);
        }
    };

    function stopSlideShow() {  //Pauses the slide show
        clearTimeout(showTimer);
        if (fadingOut == true) {
            fTimer = setTimeout(clearTimeouts, 1551);
        }
        else if (fadingOut == false) {
            clearTimeout(tTimer);
            clearTimeouts();
        }
        slideShowOn = false;
    };


    function swapImage() {  //Fades out the slideshow image
        tTimer = setTimeout(function () { fadingOut = true }, 4549);
        timer[0] = setTimeout(function () { panel.css('opacity', '0.9') }, 4550);
        timer[2] = setTimeout(function () { panel.css('opacity', '0.8') }, 4600);
        timer[4] = setTimeout(function () { panel.css('opacity', '0.7') }, 4650);
        timer[6] = setTimeout(function () { panel.css('opacity', '0.6') }, 4700);
        timer[8] = setTimeout(function () { panel.css('opacity', '0.5') }, 4750);
        timer[10] = setTimeout(function () { panel.css('opacity', '0.4') }, 4800);
        timer[12] = setTimeout(function () { panel.css('opacity', '0.3') }, 4850);
        timer[14] = setTimeout(function () { panel.css('opacity', '0.2') }, 4900);
        timer[16] = setTimeout(function () { panel.css('opacity', '0.1') }, 4950);
        timer[18] = setTimeout(function () { panel.css('opacity', '0') }, 5000);
        timer[20] = setTimeout(swapImage2, 5050);
    }

    function swapImage2() {  //Changes and fades in the slideshow image
        panel.attr("src", "/Images/SlideShowPics/" + paths[i]);
        if (i < paths.length - 1) {
            i++;
        }
        else {
            i = 0;
        }
        timer[21] = setTimeout(function () { panel.css('opacity', '0.1') }, 550);
        timer[23] = setTimeout(function () { panel.css('opacity', '0.2') }, 600);
        timer[25] = setTimeout(function () { panel.css('opacity', '0.3') }, 650);
        timer[27] = setTimeout(function () { panel.css('opacity', '0.4') }, 700);
        timer[29] = setTimeout(function () { panel.css('opacity', '0.5') }, 750);
        timer[31] = setTimeout(function () { panel.css('opacity', '0.6') }, 800);
        timer[33] = setTimeout(function () { panel.css('opacity', '0.7') }, 850);
        timer[35] = setTimeout(function () { panel.css('opacity', '0.8') }, 900);
        timer[37] = setTimeout(function () { panel.css('opacity', '0.9') }, 950);
        timer[39] = setTimeout(function () { panel.css('opacity', '1') }, 1000);
        t2Timer = setTimeout(function () { fadingOut = false }, 1050);
        timer[41] = setTimeout(swapImage, 1050);
    }

    function clearTimeouts() {  //Clears all slide show timers
        for (key in timer) {
            clearTimeout(timer[key]);
        }
    }
});

I tried reading up on this myself, but I am not getting answers that I feel I can adapt to my code. Either that or the solution is above my head.

SO sites I read up on:

Slideshow starts while images are loading but first image isn't displayed until all are downloaded

loading all images before slideshow

If you want to check out the problem yourself, you should be able to get there by visiting this link: http://test.cityofokmulgee.org:54543

Remember that this error is probably only going to naturally show up the first time you load up the page. After that your browser will have cached the images and the load time won't throw off the timing of the slide-show (unless you clear the images from the browser cache, something I have been unsuccessful in doing, myself). Also, I'm not sure if this issue will even show up in Chrome, but I know it does in IE.

Any help is much appreciated, as this is the major bug disallowing this site to go live, and I have never done anything like pre-loading images before, so I don't have any idea where to start.

Additional Info That Might Be Useful:

The contents of the server-side file, Compute_Slide_Show.cshtml (written in C#):

@{
    Layout = "";

    string fileNames = "";

    if(IsAjax)
    {
        var db = Database.Open("Content");

        bool firstRun = true;

        foreach (var row in db.Query("SELECT FileOrder, FileName FROM SlideShow WHERE FileName IS NOT NULL AND FileName <> '' ORDER BY FileOrder ASC"))
        {
            if (firstRun == true)
            {
                firstRun = false;
                fileNames += row.FileName;
            }
            else
            {
                fileNames += "/*\\";
                fileNames += row.FileName;
            }
        }
    }
    else
    {
        Context.RedirectLocal("~/home.cshtml");
    }
@:@fileNames
}
Community
  • 1
  • 1
VoidKing
  • 6,282
  • 9
  • 49
  • 81

1 Answers1

2

Oh dear, I see a lot of weird stuff in there!

First idea: do you really need to create your own slideshow? In your case I'd rather create the markup with jQuery/AJAX and apply an existing slider, like flexslider, to your images. Why dont you do that?

The second thing, just for the sake of it, is how you solved that opacity dimming. In jQuery, you can easily animate an element via .animate() (which expects a css map) or just use fade(), like so:

function swapImage() {  //Fades out the slideshow image
    tTimer = setTimeout(function () { fadingOut = true;
        panel.stop().fadeTo(500, 0, function() {
            // callback after the element has been faded
            swapImage2();
        });
    }, 4549);
}

just besides: setting the opacity via css istn crossbrowser anyways.

To get to your loading thingy, there are several ways how to do this, my advice would be to append an image for each key in paths in a hidden div:

for (var i = 0; i < paths.length; i++) {
   $('.hidden').append('<img src="' + paths[i] + '" />');
}

var checkforloaded = setInterval(function() {
        var _loaded = 0;

        for (var i = 0; i < paths.length; i++) {
            var image = $('.hidden').children().eq(i).get(0);

            if (image.complete || image.readyState == 'complete' || image.readyState == 4) {
                _loaded++;
            }
        }

        if (_loaded === paths.length) {
            clearInterval(checkforloaded);

            // start the slider
        }
    }, 80);
Alex
  • 9,911
  • 5
  • 33
  • 52
  • Firstly, thank you for all of the advice. To help clarify why my code may seem different, I was forced to do this on my own, and I had to find a way to make it work. Also, before I used jQuery, I use to use both opacity and filter (for IE), but since I used jQuery, it seemed quite cross-browser compatible. Also, I remember trying "fading" (although I don't think I tried "animate") and for some reason I can't remember, it didn't pan out, so I went with this. In any case, you have given me a lot to think about and I really appreciate your advice on how to solve the loading issue. – VoidKing Jan 10 '14 at 15:33
  • It may take some time to try this, but rest assured I will not forget to accept this when/if it solves my problem. +1, for now, for your help and your advice. I really, really needed this :) – VoidKing Jan 10 '14 at 15:34
  • One thing that's giving me trouble is that I can't seem to clear the pictures from IE's cache. I do the traditional Ctrl+Shift+Del and check the "Temporary Internet files and website files" box while making sure the "Preserve Favorites website data" box is unchecked, but it seems to still have the pictures cached somewhere so that I can't test the loading problem but once on each machine. Is there something I am missing here? – VoidKing Jan 10 '14 at 15:46
  • Oh, that was why I didn't use `.fadeTo()` it doesn't handle pausing the slideshow on mouseover and resuming on mouseout correctly. That's why I had to do it this way. I will now try your loading solution, though. – VoidKing Jan 10 '14 at 15:56
  • you can apply the .stop() function on mouseenter and resume the fade calculating the duration left by getting the current opacity/filter value – Alex Jan 10 '14 at 16:07
  • Oh, I think I understand. Thanks for that. Is there anything overtly wrong with leaving it this way, since it seems to work (apart from the loading issue, of course)? Either way, in the future I will be trying it your way, should I need to do another slideshow. – VoidKing Jan 10 '14 at 16:10
  • there is nothing wrong about it but be aware that it may not work as aspected in all browsers because setting timeouts are not 100% reliable – Alex Jan 10 '14 at 16:12
  • Okay, I tried what you said, and it seems to work pretty well for the most part. Only problem I am having is in IE(8). IE(11) seems to work fine, but in 8 I get the following error: "'complete' is null or not an object". This error is on the following line: `if (image.complete || image.readyState == 'complete' || image.readyState == 4) {` – VoidKing Jan 10 '14 at 19:35
  • Okay, scratch that, it seems broken in all IE browsers. Don't know why it worked the first time in IE-11 but anyway. Seems this solution only works for non-IE browsers. – VoidKing Jan 10 '14 at 19:40
  • Sorry to keep bugging you about this, but everything seems to work great except its functionality in IE. Apparently IE does not support the `image.complete`. Is there a possible workaround to this? I haven't found any help with a replacement for `.complete` in IE online yet. Also, I tried "flexslider" but it just seemed to mess everything up when mixed with my code/markup, so that didn't pan out either. Any further help with this would be so very much appreciated, and again, sorry to keep bothering you. – VoidKing Jan 13 '14 at 14:34
  • welcome to web programming! IE is just a little piece of s**t :) IE has other properties than .complete, readyState I believe. Flexslider only hides/shows the different LI-elements. – Alex Jan 13 '14 at 15:03
  • I think I may have gotten this working, but I won't be sure until I can access a slower machine than the two I use for testing. Tell me if this makes any sense to you. First, to avoid the actual IE 8 Error I got, I simply switched the order in the if branch (knowing that programming languages will proceed with a logical branch as soon as the first condition is met when OR (||) is the operator. I used: `if (image.readyState == 'complete' || image.readyState == 4 || image.complete)`. – VoidKing Jan 13 '14 at 15:24
  • But then the slideshow just wouldn't fire, either, so I added one more condition: `if (image.readyState == 'complete' || image.readyState == 4 || image.complete || image.readyState)` and it seems to be working. Does this make any sense to you, or am I crazy? – VoidKing Jan 13 '14 at 15:24
  • IE never makes sense! – Alex Jan 13 '14 at 15:36
  • Right you are... I think if Microsoft decided it was going to decommission and discontinue all implementations of IE, it would throw off the Earth's orbit due to every IT professional jumping for joy at the same time. – VoidKing Jan 14 '14 at 14:08
  • Just wondering, if I used Flexslider (or the like) would I still have to check for loaded images or is that likely included in the slide-show plugin I would use (is it included in Flexslider)? – VoidKing Feb 12 '14 at 15:44
  • i dont think flexslider waits for all images to be loaded, but maybe the newer versions do – Alex Feb 12 '14 at 16:09