jorblume
jorblume

Reputation: 607

Firefox, javascript and iFrame performance with Jquery

I'm having a bit of a jquery javascript performance issue, specifically related to Firefox.

We have a set of vimeo embeds, and the ids are pulled in via a json file. On each click, a new video is displayed. After the video is played, the container is removed and the title cloud is put back in. After a certain number of rounds, Firefox performance seriously degrades and you get the "unresponsive script" error. This isn't happening on any other browsers. Furthermore, the profiler in FF doesn't seem to point to a root cause of the slowdown.

I believe this is caused by poor iframe performance and how FF handles iframes, but I'm not entirely sure about this. Nothing else I'm doing is anything too, mostly just stock jquery functions like empty(), remove(), prepend(), etc.

I have implemented a click counter which will just refresh the page after a certain amount of click throughs. This resolved the problem, but it's a hacky solution which I seriously dislike. I would love some ideas on the root cause of this and any advice on how to solve it.

Here's the link to the site and the specific portion mentioned: http://www.wongdoody.com/mangles

This isn't all the code, but this is the part that gets called every click.

Also, I have tried just swapping out the src="" in the iframe, but performance still degrades.

EDIT: I can confirm this is not a memory leak, I used about:memory and with addons disabled in safe mode I'm getting decent memory usage:

359.11 MB ── private 361.25 MB ── resident 725.54 MB ── vsize

Something in the vimeo embed is slowing down the javascript engine, but it's not a memory leak. Also, this is confirmed by the fact that I can resolve the issue by just refreshing the page. If it was a memory leak I would have to close FF altogether.

function getIframeContent(vid) {
    mangle_vid_id = vid;
    return '<div class="vimeoContainerflex"><div class="vimeoContainer"><iframe class="vimeo" style="z-index:1;" width="100%" height="100%" frameborder="0" allowfullscreen="" mozallowfullscreen=""                  webkitallowfullscreen="" src="//player.vimeo.com/video/' + mangle_vid_id + '?api=1&title=0&color=89ff18&amp;byline=0&amp;portrait=0&amp;autoplay=1"></iframe></div></div>';
}

function show_titles() {
    $('.mangle-btn').hide();
    $('.vimeoContainerflex').remove();
    $('span.mangle').hide();
    if ($('#mangle-titles').length < 1) {
        $('#wongdoody').prepend(wd_titles_content);
    }

    $('#arrow').show();

    if (clicks > 12) {
        location.reload();
    }

    $('#mangle-titles span').click(function() {
        clicks = clicks + 1;
        $('#mangle-wrapper').remove();
        var vidID = $(this).attr('data-id');
        if ($('.vimeoContainer').length < 1) {
            if (vidID == "home") {
                $('#wongdoody').prepend(getIframeContent(getRandom()));
            } else {
                $('#wongdoody').prepend(getIframeContent(vidID));
            }
        }
        $('#arrow').hide();
        vimeoAPI();
    });

    $('#mangle-titles span').not('noscale').each(function() {
        var _this = $(this);
        var classname = _this.attr('class');
        var scaleNum = classname.substr(classname.length - 2);
        var upscale = parseInt(scaleNum);
        var addition = upscale + 5;
        var string = addition.toString();

        _this.hover(
            function() {
                _this.addClass('scale' + string);
            },
            function() {
                _this.removeClass('scale' + string);
            }
        );
    });
}

function vimeoAPI() {
    var player = $('iframe');
    var url = window.location.protocol + player.attr('src').split('?')[0];
    var status = $('.status');

    // Listen for messages from the player
    if (window.addEventListener) {
        window.addEventListener('message', onMessageReceived, false);
    } else {
        window.attachEvent('onmessage', onMessageReceived, false);
    }

    // Handle messages received from the player
    function onMessageReceived(e) {
        var data = JSON.parse(e.data);

        switch (data.event) {
            case 'ready':
                onReady();
                break;
            case 'finish':
                onFinish();
                break;
        }
    }

    // Helper function for sending a message to the player
    function post(action, value) {
        var data = {
            method: action
        };

        if (value) {
            data.value = value;
        }

        var message = JSON.stringify(data);
        if (player[0].contentWindow != null) player[0].contentWindow.postMessage(data, url);
    }

    function onReady() {
        post('addEventListener', 'finish');
    }

    function onFinish() {
        setTimeout(show_titles, 500);
    }
}

Upvotes: 2

Views: 543

Answers (3)

acontell
acontell

Reputation: 6932

You're overloading window with eventListeners. Each time a user clicks a video, you're attaching an event to window that fires every time you're receiving a message.

You can easily check this by adding console.log("Fire!"), for instance, at the beginning of onMessageReceived. You'll see that this function gets triggered an awful number of times after the user has performed some clicks on videos.

That surely has an impact on performance.

Hope this helps.

Upvotes: 1

mhu
mhu

Reputation: 18051

Part of you're problem may be the fact that you keep adding more and more click-handlers to the spans. After each movie ends the onFinish function calls show_titles again, which attaches a new (=additional) click-handler to the $('#mangle-titles span') spans. jQuery does not remove previously attached handlers.

Try splitting the show_titles function into two. init_titles should be called only once:

function init_titles() {
    if ($('#mangle-titles').length < 1) {
        $('#wongdoody').prepend(wd_titles_content);
    }

    $('#mangle-titles span').click(function() {
        $('#mangle-wrapper').remove();
        var vidID = $(this).attr('data-id');
        if ($('.vimeoContainer').length < 1) {
            if (vidID == "home") {
                $('#wongdoody').prepend(getIframeContent(getRandom()));
            } else {
                $('#wongdoody').prepend(getIframeContent(vidID));
            }
        }
        $('#arrow').hide();
        vimeoAPI();
    });

    $('#mangle-titles span').not('noscale').each(function() {
        var _this = $(this);
        var classname = _this.attr('class');
        var scaleNum = classname.substr(classname.length - 2);
        var upscale = parseInt(scaleNum);
        var addition = upscale + 5;
        var string = addition.toString();

        _this.hover(
            function() {
                _this.addClass('scale' + string);
            },
            function() {
                _this.removeClass('scale' + string);
            }
        );
    });
}

function show_titles() {
    $('.mangle-btn').hide();
    $('.vimeoContainerflex').remove();
    $('span.mangle').hide();
    $('#arrow').show();
}

Upvotes: 2

Jean
Jean

Reputation: 680

I'd recommend trying to re-use the iframe instead of wiping and re-adding. Failing that, I think you may be out of luck. Your method of closing the iFrame is fine; your browser that it's running in is not.

Upvotes: 1

Related Questions