我可以控制javascript / jQuery事件触发的顺序吗?

时间:2012-04-12 02:41:52

标签: javascript jquery asp.net webforms

背景

我有一个带有网格的asp.net webform,当用户更新该网格中的文本框时,onchange事件将启动WebMethod调用并更新已更改行的其余部分。当时没有保存任何内容 - 我们只是更新了用户界面。

要提交更改,请单击“保存”按钮。

这几乎在每种情况下都能可靠地运行。然而,有一个非常持久的感觉我应该能够解决,但现在是时候打电话给专家了。

问题情景

我正在使用jQuery来捕获回车键,不幸的是,该事件首先触发,导致页面在回调完成之前提交。该行未正确更新。保存陈旧和令人困惑的数据。

更新

我认为你不能使回车行为取决于回调,因为你可以在不改变行的情况下保存。在这种情况下,如果您没有更改行,它将永远不会保存。

现在,如果有某种方法来检查javascript的内部事务列表,或者可能创建我自己的事情,然后以某种方式管理它,那就行了。但对于一些应该很容易的东西来说,这是一个繁重的举动。因此,除非专家告诉我其他情况,否则我必须认为这是错误的。

尝试

现在我正在使用内置的jQuery事件,我已经有了这个精心设计的setTimeout,它坚持了一个事实,即尝试保存,暂停足够长时间让WebMethod至少被调用,并依赖于回调提交。但事实证明,javascript ansychrony并不像我希望的那样工作,并且onchange事件甚至不会触发,直到代码块完成。这令人惊讶。

我以为我可以使用自己的小对象以正确的顺序对这些事件进行排队,并找到一种巧妙的触发方式,等等。

这一切似乎都是错误的方向。当然这是疯狂的矫枉过正,这是一个常见的问题,我忽略了一个简单的解决方案,因为我没有在24/7的javascript中工作。

右?

代码

这就是我这一刻所做的。这显然不起作用 - 我试图利用jquery的异步性质,但所有这些显然必须在行的onchange事件事件触发之前结束:

$(document).bind("keypress", function (e) {
    if (e.keyCode == 13) {
        handleEnter();
        return false; //apparently I should be using e.preventDefault() here. 
    }
});


function handleEnter() {
    setTimeout(function () {
        if (recalculatingRow) { //recalculatingRow is a bit managed by the onchange code.
            alert('recalculating...');
            return true; //recur
        }

        //$('input[id$="ButtonSave"]').click();
        alert('no longer recalculating. click!');
        return false;
    }, 1000);
}

然后典型的行看起来像这样。请注意,我没有使用jquery来绑定它:

 <input name="ctl00$MainContent$GridOrderItems$ctl02$TextOrderItemDose" type="text" value="200.00" maxlength="7" id="ctl00_MainContent_GridOrderItems_ctl02_TextOrderItemDose" onchange="recalculateOrderItemRow(this);" style="width:50px;" />

我可以发布recalculateOrderItemRow的代码,但它真的很长而且现在的问题是它不会触发,直到after keypress事件结束。

更新Dos 根据{{​​3}}(并且man是一篇很酷的文章),使用setTimeout会导致它变为异步。进一步深入研究setTimeout和jQuery之间的交互,以及普通javascript事件和jQuery事件之间的交互。

5 个答案:

答案 0 :(得分:2)

防止 ENTER 不应该给你带来太多麻烦!确保您的代码中包含以下内容:

$(document).on('keydown', 'input', function(e) {
    if(e.keyCode == 13) {
       e.preventDefault();
    }
});

<强>更新

看起来你希望保存在 ENTER 上,但只有在change上更新UI后才能保存。这是可能的。您可以使用上面提到的Matthew Blancarte旗帜,触发change回调中的保存,并摆脱setTimeout

但我不建议这样做。 您最好完全依靠保存按钮进行保存。如果不这样做,则在保存完成之前,您的用户必须等待两个异步操作完成。因此,您必须阻止UI,或跟踪所有异步操作,根据需要中止一些操作。我认为这不值得,如果保存时间太长, ENTER 会变得不那么直观。

答案 1 :(得分:2)

下面的大量解决方法,有效地占据了我今天和昨天的一半写作,似乎解决了每一种排列。

有趣的是,如果你调用e.preventDefault(),则输入本身不会触发onchange。为什么会这样?在单击保存按钮的默认行为发生之前,实际上不会发生更改。

关于这一点很少有趣。

//Used in handleEnter and GridOrderItems.js to handle a deferred an attempt to save by hitting enter (see handleEnter).
var isSaving = false; 
var saveOnID = '';

//When one of the fields that trigger WebMethods get focus, we put the value in here
//so we can determine whether the field is dirty in handleEnter.
var originalVal = 0;

//These fields trigger callbacks. On focus, we need to save their state so we can
//determine if they're dirty in handleEnter().
$('[id$=TextOrderItemDose], [id$=TextOrderItemUnits]').live("focus", function() {
    originalVal = this.value;
});

$(document).bind("keypress", function (e) {
    if (e.keyCode == 13) { //enter pressed.
        e.preventDefault();
        handleEnter();
    }
});

//Problem:
//In the products grid, TextOrderItemDose and TextOrderItemUnits both have js in their onchange events
//that trigger webmethod calls and use the results to update the row. Prsssing enter is supposed to 
//save the form, but if you do it right after changing one of those text fields, the row doesn't always
//get updated due to the async nature of js's events. That leads to stale data being saved.  
//Solution:
//First we capture Enter and prevent its default behaviors. From there, we check to see if one of our
//special boxes has focus. If so, we do some contortions to figure out if it's dirty, and use isSaving
//and saveOnID to defer the save operation until the callback returns. 
//Otherwise, we save as normal.
function handleEnter() {
    var focusedElement = $("[id$=TextOrderItemDose]:focus, [id$=TextOrderItemUnits]:focus")

    //did we press enter with a field that triggers a callback selected?
    if (isCallbackElement(focusedElement) && isElementDirty(focusedElement)) { 
        //Set details so that the callback can know that we're saving.
        isSaving = true;
        saveOnID = focusedElement.attr('id');

        //Trigger blur to cause the callback, if there was a change. Then bring the focus right back.
        focusedElement.trigger("change");
        focusedElement.focus();
    } else {
        forceSave();
    }
}

function isCallbackElement(element) {
    return (element.length == 1);
}

function isElementDirty(element) {
    if (element.length != 1) 
        return false;

    return (element.val() != originalVal);
}

function forceSave() {
    isSaving = false;
    saveOnID = '';

    $('input[id$="ButtonSave"]').click();
}

在文本框的更改事件中调用它:

function recalculateOrderItemRow(textbox) {
    //I'm hiding a lot of code that gathers and validates form data. There is a ton and it's not interesting.

    //Call the WebMethod on the server to calculate the row. This will trigger a callback when complete.
    PageMethods.RecalculateOrderItemRow($(textbox).attr('id'),
                                   orderItemDose,
                                   ProductItemSize,
                                   orderItemUnits,
                                   orderItemUnitPrice,
                                   onRecalculateOrderItemRowComplete);

}

然后,在WebMethod回调代码的末尾,我们将更新的表单值拉出来,使用jquery.caret将插入符号放在需要的位置,然后检查是否需要强制保存:

function onRecalculateOrderItemRowComplete(result) {
    var sender, row;

    sender = $('input[id="' + result.Sender + '"]');
    row = $(sender).closest('tr');

    row.find('input[id$="TextOrderItemDose"]').val(result.Dose);
    row.find('input[id$="TextOrderItemUnits"]').val(result.Units);
    row.find('span[id$="SpanTotalPrice"]').html(formatCurrency(result.TotalPrice));

    calculateGrandTotalPrice();
    $(document.activeElement).select();

    if (isSaving && saveOnID == result.Sender) {
        forceSave();
    }
}

result.Sender是调用控件的ID,我将其填入WebMethod调用然后返回。 saveOnID可能并不完美,实际上甚至可以更好地维护一个active / uncallback-ed WebMethod调用的计数器,以确保在保存之前所有内容都已完成。呼。

答案 2 :(得分:1)

你可以发布你的javascript吗?听起来你走在正确的轨道上。在进行AJAX调用之前,我会更改我的OnChange事件以增加变量。我将调用变量inProcess并将其初始化为零。当AJAX调用返回时,我会将inProcess更新为当前值减1。在Enter键事件上,我将检查inProcess是否等于零。如果没有,您可以警告用户或设置超时以稍微重试。

答案 3 :(得分:1)

您可以在onChange事件中取消绑定Enter键捕获,然后在回调函数结束时重新绑定它。如果您发布一些代码,我可以给出更具体的答案。

答案 4 :(得分:0)

听起来你不应该异步调用WebMethod。同步调用它,并在成功时保存您的数据。