Как избежать утечки памяти в jQuery Mobile из замыканий - PullRequest
2 голосов
/ 24 января 2012

Я пытаюсь создать плагин jQuery для собственного использования, который можно использовать для настройки обработчика для мобильных диалогов jQuery с помощью одной команды: $('#dialog').setup_dialog({ callback: callback_function });

Однако мой обработчик имеет довольноочевидная утечка памяти из-за замыканий в нем:

$.fn.setup_dialog = function( options ) {
    var settings = $.extend({
        callback : 0
    }, options);
    var that = this;

    return this.live('pagebeforeshow', function(event, ui) {
        console.log("outside");
        $('form', that).submit( function(e) {
            var $inputs = $('form :input', that); // get all form inputs

            var values = {};
            $inputs.each(function() {
                values[this.name] = $(this).val();
            });

            that.dialog('close');

            if ( settings.callback && typeof(settings.callback) === "function" ) {
                $('#'+ui.prevPage[0].id).live('pagebeforeshow', function() {
                    settings.callback(values, that);
                    console.log("inside");
                });
            }

            return e.preventDefault();
        });
    });
}; /* setup_dialog */

Если вы запустите приведенный выше код, вы увидите, что «изнутри» и «снаружи» напечатаны сначала один раз, затем три раза (дважды с одной отправки),затем шесть раз (три раза от одной отправки) и т. д.

Целью кода является присоединение обработчика событий к диалоговому окну jQuery Mobile, когда он появляется, который перехватит отправку формы, соберет всю формузначения, а затем передать их в функцию обратного вызова, которая изменит исходную страницу (которая запустила диалоговое окно).

Обратите внимание, что из-за того, что jQuery Mobile использует AJAX для переключения между страницами, мне нужно использовать .liveсвязать события (.bind или .one не будет работать).

Есть идеи, как мне избежать накопления событий (и, возможно, немного очистить код)?

Ответы [ 2 ]

0 голосов
/ 25 января 2012

Решение выглядит следующим образом:

$.fn.setup_dialog = function( options ) {
    var settings = $.extend({
        callback : 0
    }, options);
    var that = this; var values = {}; var submitted = 0;

    this.on('pageinit', function(event, ui) {
        $('form', that).submit(function(e) {
            var $inputs = $('form :input', that); // get all form inputs
            submitted = 1;

            $inputs.each(function() {
                values[this.name] = $(this).val();
            });

            that.dialog('close');

            return e.preventDefault();
        });

        $('.cancel-button', that).click(function() {
            submitted = 0;
        });
    });

    this.on('pagebeforehide', function(event, ui) {
        if ( submitted && settings.callback && typeof(settings.callback) === "function" ) {
            settings.callback(values, that);
        }
    });
}; /* setup_dialog */

Было несколько способов сделать это, включая создание замыкания и вызов $('#'+ui.prevPage[0].id).die().live('pagebeforeshow', function() {

Не уверен, что это лучшее решениехотя, так что я хотел бы услышать некоторые лучшие идеи.

0 голосов
/ 25 января 2012

Вы связываете обработчики событий внутри других обработчиков событий.И внешние обработчики событий не являются уникальными событиями (они происходят более одного раза), что означает, что ваши внутренние обработчики событий связаны более одного раза.

Хорошим решением является удаление обработчика события submit изpagebeforeshow обработчик событий и поместите его в обработчик событий pagecreate или pageinit (они уникальны и будут запускаться только один раз в диалоге).

Если вы хотите сохранить текущую логику, вы можете простоотсоединяйте обработчики событий при их запуске, поэтому вы не будете складывать несколько обработчиков событий, которые делают одно и то же:

$.fn.setup_dialog = function( options ) {
    var settings = $.extend({
        callback : 0
    }, options);
    var that = this;

    return this.live('pagebeforeshow', function(event, ui) {
        console.log("outside");
        $('form', that).submit( function(e) {
            var $inputs = $('form :input', that); // get all form inputs

            var values = {};
            $inputs.each(function() {
                values[this.name] = $(this).val();
            });

            that.dialog('close');

            if ( settings.callback && typeof(settings.callback) === "function" ) {
                $('#'+ui.prevPage[0].id).live('pagebeforeshow', function() {
                    settings.callback(values, that);
                    console.log("inside");

                    $(this).die('pagebeforeshow');//NEW, unbind the pagebeforeshow event handler for this element
                                                  //since it will be bound the next time the dialog is shown anyway
                });
            }

            $(this).die('submit');//NEW, unbind the submit event handler for this form 
                                  //since it will be bound the next time the dialog is shown anyway

            return e.preventDefault();
        });
    });
}; /* setup_dialog */

Вот документация для .die(): http://api.jquery.com/die/

...