Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my jQuery function causing a 'Stack Overflow' error in IE 8?

I have a simple function which causes a Stack Overflow error in IE 8. The problem does not appear to occur in any other browser although I have not testing IE 7 or 6.

The exact error is as follow:-

 SCRIPT28: Out of stack space 
 jquery.min.js, line 2 character 7498
 SCRIPT2343: Stack overflow at line: 2 

The function in question:

function switchImage(size, objid, prefix, fullimage){

    if(size !== 'full'){
        var image = prefix + size + '/' + size +'_' + fullimage;
    }
    else {
        var image = prefix + size + '/' + fullimage;
    }

    $('#' + objid).data('type', size)
        .append('<img id="preload" src="' + image + '" style="display:none;" />')
            .find('#preload').load(function(){
                $('#' + objid).find('img').attr('src', image);
                $(this).remove();
            });
}

To give an overview of the use case I will explain the purpose of this function:

When a user resizes an image (using jqueryUI resize) the width/height is compared in another function.

Once the image grows to be of a certain size this function is then called which as you can see, appends a hidden <img> element to the DOM with the 'src' attribute of a higher resolution version of the image (or lower if the image is being downsized by the user.

Once it has been loaded the src attribute of the visible element is updated and the hidden element is removed.

This proved excellent dynamic switching of images as the user resizes them keeping the image quality good throughout the process....

I just can't seem to work out what is causing the problem in IE 8. With this function removed no errors occur, and although the error is present, the function still works as it should (although resize performance is poor anyway in IE 8).

Any help would be greatly appreciated.

UPDATE: I seem to have solved the original issue by rewriting the function to the following:-

function switchImage(size, objid, prefix, fullimage){

    var $el = $('#' + objid);

    if(size !== 'full'){
        var image = prefix + size + '/' + size +'_' + fullimage;
    }
    else {
        var image = prefix + size + '/' + fullimage;
    }

    $el.data('type', size);

    $('<img id="preload" src="' + image + '" style="display:none;" />')
        .appendTo($el)
            .one('load', function(){
                $('#' + objid).find('img').attr('src', image);
                    $(this).remove();
                });
}

As you can see, the only real difference is that I am using .appendTo() rather than .append() as well as using jQuery .one() method to ensure that the load event only occurs once. Though since the element is removed directly afterwards I don't understand why this should make any difference.

I really would be interested to see if anyone can shed any light on this so I can learn how to avoid such issues in the future. Cheers.

like image 751
gordyr Avatar asked May 17 '12 02:05

gordyr


1 Answers

Your initial function would have run straight into an infinite loop if you didn't have $(this).remove(). Essentially what you were doing was setting the src attribute to all img tags, including the preload image itself. (replace $(this).remove() with console.log('loaded') and watch it loop infinitely in Firebug)

I would guess that in IE8, once the attribute is set to the preload image as well, it invoked your 'load' event handler first before executing the next line which is $(this).remove() (explaining the stack overflow), while other browsers might have first finished executing the entire function first, thus removing the preload image, which prevented the infinite loop. (This is just a guess)

A monkey patch to the initial version would be to use .find('img:not(#preload)') instead of just .find('img').

Your patch also prevents the infinite loop because .one() ensures it to only run once.

But ultimately I would refactor the function to the following:

function switchImage(size, objid, prefix, fullimage){

    var $el = $('#' + objid),
        $image = $el.find('img'),
        $preload = $('<img>');

    if(size !== 'full'){
        var image = prefix + size + '/' + size +'_' + fullimage;
    }
    else {
        var image = prefix + size + '/' + fullimage;
    }

    $el.data('type', size);

    $preload
        .on('load', function () {
            $image.attr('src', image);
        })
        .attr('src', image);
}

Also note that the preload image actually does not need to be explicitly appended to the DOM to serve your purpose.

like image 191
Terry Young Avatar answered Oct 16 '22 00:10

Terry Young