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

Can I control the order in which javascript / jQuery events fire?

本文关键字:顺序 事件 控制 javascript jQuery 我可以      更新时间:2023-09-26

Background

我有一个带有网格 asp.net Web 窗体,当用户更新该网格中的文本框时,onchange 事件将启动 WebMethod 调用并更新更改行的其余部分。此时不会保存任何内容 - 我们只是更新 UI。

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

这实际上几乎在每种情况下都能可靠地工作。但是,有一个非常持久的问题,感觉我应该能够解决,但现在是时候召集专家了。

问题场景

我正在使用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;" />

我可以发布用于重新计算OrderItemRow的代码,但它真的很长,现在的问题是它直到按键事件结束才会触发。

更新注意事项根据尼克·菲茨杰拉德(Nick Fitzgerald)的说法(伙计是一篇很酷的文章),使用setTimeout应该会导致它变得异步。进一步挖掘setTimeout和jQuery之间的交互,以及普通javascript事件和jQuery事件之间的交互。

阻止 ENTER 不应该给你带来这么多麻烦!确保你的代码上有这样的东西:

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

更新

看起来您确实想在 ENTER 上保存,但只有在 change 上更新 UI 之后。这是可能的。您可以使用上面建议的马修·布兰卡特(Matthew Blancarte)的标志,从change回调中触发保存,并摆脱setTimeout

但我不建议这样做。您最好仅依靠保存按钮进行保存。如果不这样做,用户必须等待两个异步操作完成,然后才能完成保存。因此,您必须阻止 UI,或跟踪所有异步操作,并根据需要中止一些操作。我认为这是不值得的,如果保存时间过长,ENTER 对用户来说变得不那么直观。

下面可怕的

大量变通方法,有效地花了我今天一整天和昨天的一半时间来写,似乎解决了每一个排列。

有趣的是,如果你调用 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();
    }
}

结果。发送方是调用控件的 ID,我将其填充到 WebMethod 调用中,然后返回。saveOnID 可能并不完美,实际上最好维护一个活动/未回调的 WebMethod 调用计数器,以确保在保存之前一切都结束。呼。

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

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

听起来你不应该异步调用 WebMethod。同步调用它,成功后保存数据。