Изменить асинхронный диалог JQuery на синхронный? - PullRequest
36 голосов
/ 06 августа 2011

В настоящее время я работаю над заменой «alert» / «verify» на диалоговое окно jquery.

Но большинство устаревших кодов написано каким-то асинхронным способом, что затрудняет изменениеЕсть ли способ заставить диалог JQuery работать синхронно? (Не используйте цикл или функцию обратного вызова)

   For example:
   function run()
   { 
      var result = confirm("yes or no");
      alert( result );
      \\more codes here
   }

В этом примере предупреждение и другие коды будут выполняться после пользователяchoice.

Если мы используем jquery dialog var result = $ dialog.open () Он будет продолжать выполнять асинхронное предупреждение.

В настоящее время мое решение заключается в использовании функции обратного вызова.в функции OK | Отмена. Например:

    OK: function ()
   {
       $dialog.close();
       alert("yes");
       //more codes here
    }

Этот метод работает, но трудно сделать все синхронные коды асинхронными, что требует значительных изменений (см. следующий пример). Итак, я ищу синхронный диалог jQuery, возможно ли это?

Например: (Реальные коды намного сложнее, чем в следующем примере)

     function f1()
    {
          if ( confirm("hello") )    f2();
          alert("no");
    } 

    function f2()
    {
          if( confirm("world") )    f3();
          alert("no");
    }

    function f3()
    {
          return confirm("!") ;
    }

Другой пример:

vendorObject.on('some-event', function() {
    if(confirm("Do you really want to do that?")) {
        return true;
    }
    else {
        return false; // cancel the event
    }
});

... здесь объект вендора запускает событие, которое должно быть отменено, если пользователь подтвердит.Событие может быть отменено, только если обработчик события возвращает false - синхронно.

Ответы [ 6 ]

35 голосов
/ 03 декабря 2011

Короткий ответ - нет, вы не сможете сохранить свой код синхронным. И вот почему:

  • Для того, чтобы это было синхронно, исполняемый в данный момент скрипт должен будет дождаться, пока пользователь предоставит ввод, а затем продолжить.
  • Пока существует исполняемый скрипт, пользователь не может взаимодействовать с пользовательским интерфейсом. На самом деле, пользовательский интерфейс даже не обновляется, пока не завершится выполнение скрипта.
  • Если сценарий не может продолжаться до тех пор, пока пользователь не предоставит ввод, и пользователь не сможет предоставить ввод, пока сценарий не будет завершен, самым близким из всех, что вы когда-либо получите, является зависший браузер.

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

$("body").css("backgroundColor", "red");
var x = 1;  // break on this line

Обратите внимание, что фон вашей страницы еще не красный. Он не изменится на красный, пока вы не возобновите выполнение и скрипт не завершит выполнение. Вы также не можете нажимать ссылки на своей странице, когда выполнение сценария приостановлено с помощью отладчика.

Существует исключение из этого правила для alert() и confirm(). Это элементы управления браузера, и они обрабатываются иначе, чем элементы пользовательского интерфейса веб-страницы.

Хорошая новость заключается в том, что преобразование кода не должно быть очень сложным. Предположительно, ваш код в настоящее время выглядит примерно так:

if (confirm("continue?")) {
    // several lines of code if yes
}
else {
    // several lines of code if no
}
// several lines of code finally 

Ваша асинхронная версия может создать функцию ifConfirm(text, yesFn, noFn, finallyFn), и ваш код будет выглядеть примерно так же:

ifConfirm("continue?", function () {
    // several lines of code if yes
},
function () {
    // several lines of code if no
},
function () {
    // several lines of code finally 
});

Редактировать: В ответ на дополнительный пример, который вы добавили в свой вопрос, к сожалению, этот код придется реорганизовать. Просто невозможно иметь синхронные пользовательские диалоги подтверждения. Чтобы использовать настраиваемое диалоговое окно подтверждения в сценарии, где событие необходимо продолжить или отменить, вам просто нужно всегда отменять событие и имитировать событие в обратном вызове yesFn.

Например, ссылка:

$("a[href]").click(function (e) {
    e.preventDefault();
    var link = this.href;
    ifConfirm("Are you sure you want to leave this awesome page?", function () {
        location.href = link;
    });
});

Или, форма:

$("form").submit(function (e) {
    e.preventDefault();
    var form = this;
    ifConfirm("Are you sure you're ready to submit this form?", function () {
        form.submit();
    });
});
8 голосов
/ 06 декабря 2011

Я не совсем уверен, что мотивирует не использовать обратные вызовы, поэтому трудно судить, какое решение может удовлетворить ваши требования, но другой способ отложить выполнение - через «отложенный» объект jQuery.

http://api.jquery.com/category/deferred-object/

Вы можете настроить функцию, открывающую диалог jquery, и добавлять код, который «ждет» закрытия диалога.В конечном итоге это работает почти так же, как обратный вызов в случае, если вы выложили, но вот пример:

    function confirm () {
        var defer = $.Deferred(); 
        $('<div>Do you want to continue?</div>').dialog({
                autoOpen: true,
                close: function () { 
                    $(this).dialog('destroy');
                },
                position: ['left', 'top'],
                title: 'Continue?',
                buttons: {
                    "Yes": function() {
                        defer.resolve("yes"); //on Yes click, end deferred state successfully with yes value
                        $( this ).dialog( "close" );
                    },
                    "No": function() {
                        defer.resolve("no"); //on No click end deferred successfully with no value
                        $( this ).dialog( "close" );
                    }
                }
            });
        return defer.promise(); //important to return the deferred promise
    }

    $(document).ready(function () {
        $('#prod_btn').click(function () {
            confirm().then(function (answer) {//then will run if Yes or No is clicked
                alert('run all my code on '  + answer);

            });
        });

    });

Здесь он работает в jsFiddle: http://jsfiddle.net/FJMuJ/

3 голосов
/ 01 декабря 2011

Нет, вы не можете ничего синхронизировать в Javascript (на самом деле предупреждение нарушает правила).Javascript построен с «однопоточным, асинхронным» в ядре.

Что вы можете сделать, это отключить функциональность базовой страницы (лайтбокса), чтобы ни одно событие не вызывалось со страницы, пока вы не отключитене предпринимайте никаких действий в диалоговом окне, будь то ОК или Отмена.Думаю, это не поможет вам заставить ваш код синхронизации работать.Вы должны переписать.

1 голос
/ 01 декабря 2011

Вот несколько идей: вам нужно заблокировать асинхронное событие, чтобы оно выглядело как синхронизация. Вот несколько ссылок:

Очередь асинхронных вызовов

Mobl

Повествовательный код JavaScript

Надеюсь, это поможет вам дальше!

0 голосов
/ 01 сентября 2015

Чтобы ответить на более конкретный вопрос Дэвида Уайтмена, вот как я реализую «отложенный» постбэк для события ClickButton Click.По сути, я просто запрещаю поведение по умолчанию и запускаю обратную передачу вручную, когда доступна обратная связь с пользователем.

function MyClientClickHandler(event, confirmationMessage, yesText, noText) {
    // My LinkButtons are created dynamically, so I compute the caller's ID
    var elementName = event.srcElement.id.replace(/_/g, '$');
    // I don't want the event to be fired immediately because I want to add a parameter based on user's input
    event.preventDefault();
    $('<p>' + confirmationMessage + '</p>').dialog({
        buttons: [
        {
            text: yesText,
            click: function () {
                $(this).dialog("close");
                // Now I'm ready to fire the postback
                __doPostBack(elementName, 'Y');
            }
        },
        {
            text: noText,
            click: function () {
                $(this).dialog("close");
                // In my case, I need a postback when the user presses "no" as well
                __doPostBack(elementName, 'N');
            }
        }
        ]
    });
}
0 голосов
/ 02 декабря 2011

Я действительно не понимаю, почему вы против использования обратных вызовов Jquery для достижения поведения в вашем примере. Вам определенно придется переписать некоторый код, но что-то вроде:

    function f1() { 
    $( "#testdiv" ).dialog({
        modal: true,
        buttons: {
            "OK": function() {
                $( this ).dialog( "close" );
                f2();
            },
            Cancel: function() {
                $( this ).dialog( "close" );
                alert('no');
            }
        }
    });
}
function f2() {
      $( "#testdiv2" ).dialog({
        modal: true,
        buttons: {
            "OK": function() {
                $( this ).dialog( "close" );
                f3();
            },
            Cancel: function() {
                $( this ).dialog( "close" );
                alert('no');
            }
        }
    });
}
function f3() {
    $( "#testdiv3" ).dialog({
        modal: true,
        buttons: {
            "OK": function() {
                $( this ).dialog( "close" );
            },
            Cancel: function() {
                $( this ).dialog( "close" );
            }
        }
    });
}

    <div id="testdiv" title="Hello"/>
    <div id="testdiv2" title="World"/>
    <div id="testdiv3" title="!"/>
...