Search code examples
javascriptfadeinopacityfadeout

Javascript fade in/out effect freezes when hovering quickly over images


I'm playing around with pure JavaScript, so I created a small fade in/out object, to adjust images opacity onmouseover and onmouseout. Fading works fine when the mouseover and mouseout actions are precise:

  1. Start moving the cursor from the white background
  2. Hover over an image
  3. Hover back over the white background

The problem is, as soon as I start to move the mouse "naturally" from one image to another, the fading (or rather the script itself) freezes.

I'm not sure whether it's a animation-speed problem, or there's something I'm missing in the implementation.

If someone has the time to take a look, I would appreciate a peer check, so I can crack the issue and learn new stuff.

Here's a fiddle: http://jsfiddle.net/6bd3xepe/

Thanks!


Solution

  • As I see it, you have one INTERVAL for you FADER, you need one for each IMG. My jsfiddle fixes this. I added an ALT-attribute to each IMG with "dome" content, so as to circumvent the jsfiddle working on non-cat-images .. ignore that part - commented out below. There are some fundamental things wrong with the design - keeping track of objects & references is key. Usage of "this" & "that" aren't helping in the current implementation (see comments to OP). Also, on another note, the usage of "toFixed(2)" is not really required IMHO and you can shorten "o = o + 0.1" to "o += 0.1".

    JS:

    var fader = {
    
        target: document.getElementsByTagName('img'),
        interval: [],
        speed: 25,
        default_opacity: 1,
    
        init: function() {
            this.bindEvents();
        },
    
        // Get element's opacity and increase it up to 1
        fadeIn: function(element) {
            var element_opacity = this.getOpacity(element),
                that = this,
                idx = element.getAttribute('data-idx');
            console.log("fI: "+idx+" "+element_opacity);
            this.default_opacity = element_opacity.toFixed(2);
    
            this.interval[idx] = setInterval(function() {
                if (element_opacity.toFixed(2) < 1) {
                    element_opacity = element_opacity + 0.1;
                    element.style.opacity = element_opacity.toFixed(2);
                } else {
                    clearInterval(that.interval[idx]);
                }
            }, that.speed);
        },
    
        // Get current opacity and decrease it back to the default one
        fadeOut: function(element) {
            var element_opacity = this.getOpacity(element),
                that = this,
                idx = element.getAttribute('data-idx');
            console.log("fO: "+idx+" "+element_opacity);
            this.interval[idx] = setInterval(function() {
                if (element_opacity.toFixed(2) > that.default_opacity) {
                    element_opacity = element_opacity - 0.1;
                    element.style.opacity = element_opacity.toFixed(2);
                } else {
                    clearInterval(that.interval[idx]);
                    element.removeAttribute('style');
                }
            }, that.speed);
        },
    
        // Get opacity of an element using computed styles
        getOpacity: function(element) {
            var styles = window.getComputedStyle(element),
                opacity = parseFloat(styles.getPropertyValue('opacity'));
            return opacity;
        },
    
        bindEvents: function() {
            var that = this, count = 0;
            for (var i in this.target) {   
                // the whole "dome" is just a fsfiddle hack - otherwise it sees 7 images instead of 4!
                //if( this.target[i].alt == "dome" ){
                console.log("COUNT: "+count);
                this.target[i].setAttribute('data-idx',count);
                this.target[i].onmouseover = function() {
                    that.fadeIn(this);
                }
                this.target[i].onmouseout = function() {
                    that.fadeOut(this);
                }
                count++;
                //}
            }
        }
    
    };
    
    fader.init();