内容有无变化OnBlur即时更新引起的问题与解决

文本框中onblur事件,实现用户输入完成后,光标离开文本框实现即时更新。
但是,用户只要用鼠标点击文本框离开后,它均会执行更新操作,文本框内容是否有变化都会进行。

这是一种不好的性能影响。

先示例,然后看看Insus.NET怎样去解决它的。
数据还是使用下面随笔中的数据表,存储过程等。为了能看到效果,在数据表中添加一个字段,UpdatedDate来记录更新时间。
修改表:

ALTER TABLE [dbo].[ZTestData]
ADD [UpdatedDate] DATETIME NOT NULL DEFAULT(GETDATE())
GO
View Code

 

修改更新的存储过程:

ALTER PROCEDURE [dbo].[usp_ZTestData_UpdateBeiZhu]
@ID INT,
@BeiZhu NVARCHAR(30)
AS
BEGIN
    UPDATE [dbo].[ZTestData]
    SET
        [BeiZhu] = @BeiZhu,
        [UpdatedDate] = GETDATE()
    WHERE 
        [ID] = @ID
END
GO
View Code

 

修改获取记录的存储过程:

ALTER PROCEDURE [dbo].[usp_ZTestData_SelectAll]
AS
BEGIN
    SELECT 
        [ID],
        [IMT_pz],
        [IMT_gx],
        [BeiZhu],
        [UpdatedDate]
    FROM [dbo].[ZTestData]
END
GO
View Code

 

下面是修改App_Code下的ZTestData.cs类:
2026-04-29_16-40-45

 

此篇随笔,演示示例,由GridView改为html table呈现,
2026-04-29_16-45-20

 

<form id="form1" runat="server">
    <div>
        <table>
            <thead>
                <tr>
                    <td>ID</td>
                    <td>Remarks</td>
                    <td>Update Time</td>
                </tr>
            </thead>
            <tbody >
                <tr>
                    <td>
                        <%# Eval("ID") %>
                        <input id="HiddenID" type="hidden" value=' <%# Eval("ID") %>' />
                    </td>
                    <td>
                        <input id="TextBeiZhu" type="text" value='<%# Eval("BeiZhu") %>' onblur="update(event);" />
                    </td>
                    <td>
                        <%# Eval("UpdatedDate") %>
                    </td>
                </tr>
            </tbody>
            <tfoot>
            </tfoot>
        </table>
    </div>
    <asp:Button ID="ButtonRefreshData" runat="server" Text="RefreshData" Style="display:none" OnClick="ButtonRefreshData_Click" />
</form>
View Code

 

cs后端对html table数据绑定,
2026-04-29_16-53-27

 

public object DataItem { get; set; }

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
        Data_Binding();
    }

}

private void Data_Binding()
{
    ZTestDataEntity ztde = new ZTestDataEntity();
    DataView dv = ztde.Get_DataTable_GetData().DefaultView;
    dv.RowFilter = "ID = 2"; //指定记录主键,只是为了演示。
    DataItem = dv.ToTable().DefaultView[0];
    DataBind();
}
View Code

宣告一个对象object(第1个红色感叹号)。
第2处注意的地方,ztde.Get_DataTable_GetData()返回的是DataTable。

把它转为DefaultView后,可以对从数据库得到数据进行RowFilter过滤。过滤条件你可以自定。
还有一行是dv.ToTable().DefaultView[0];
给它一个0索引。受到过滤条件影响,有时也许返回不一定是1条记录。

还有一个隐藏服务控件Button按钮OnClick事件,
2026-04-29_17-08-37

 


最后是html input的text的 onblur="update(event);"事件,
照搬随笔最初参考随笔的链接的写法(不过仍有一些细节修改而变化):
2026-04-29_17-16-00

 

 <script src="Scripts/axios.min.js"></script>
 <script type="text/javascript">

     //onblur事件
     function update(event) {
         updateBeizhuByTextBox(event.target)
             .catch(console.warn);
     }

     function updateBeizhuByTextBox(txtboxElement) {
         return new Promise((resolve, reject) => {

             let url = '/api/MyTest/UpdateBeiZhu';
             var postData = {
                 ID: HiddenID.value,
                 BeiZhu: txtboxElement.value.trim()
             };

             axios.post(url, JSON.stringify(postData), {
                 headers: { 'Content-Type': 'application/json; charset=utf-8' }
             })
                 .then(response => {
                     if (!response.data.Status) {
                         alert(response.data.Message);
                         reject(response.data.Message);
                     } else {
                         alert(response.data.Message);
                         resolve(response.data);
                     }
                 })
                 .catch(error => {
                     console.error(error);
                     reject(error);
                 })
                 .finally(() => {
                     refreshData();
                 });
         });
     }

     function refreshData() {
         document.getElementById('<%= ButtonRefreshData.ClientID %>').click();
     }


 </script>
View Code


在#95行代码是为刷新呈现的数表而已。

看看效果(关注Update time栏位时间变化):
update_data_byonblur

 


从效果中看到,内容无变化,它更新了。
如何解决它呢?Insus.NET想到2种方法:
从数据端入手,写个INSTEAD OF UPDATE触发器,
2026-04-30_09-02-56

 

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:        leo yeung
-- Create date: 2026-04-30
-- Description:    更新备注前验证是否有变化
-- =============================================
CREATE TRIGGER [dbo].[tri_ZTestData_UpdateBeiZhu]
ON  [dbo].[ZTestData]
INSTEAD OF UPDATE 
AS 
BEGIN    
    SET NOCOUNT ON;

    IF NOT EXISTS
    (
        SELECT 1 FROM inserted i 
        INNER JOIN deleted d ON (i.[ID] = d.[ID] AND i.[BeiZhu] = d.[BeiZhu]
    ))
    BEGIN        
        UPDATE target
        SET
            [IMT_pz] = i.[IMT_pz],
            [IMT_gx] = i.[IMT_gx],
            [BeiZhu] = i.[BeiZhu],
            [UpdatedDate] =i.[UpdatedDate]
        FROM [dbo].[ZTestData] target
        INNER JOIN inserted i ON target.[ID] = i.[ID];        
    END
    ELSE
    BEGIN
        RAISERROR(N'备注内容无任何变化,更新略过。',16,1)
    END
END
GO
View Code

 

如果不想写触发器,那就改更新存储过程,
2026-04-30_09-22-13

 

ALTER PROCEDURE [dbo].[usp_ZTestData_UpdateBeiZhu]
    @ID INT,
    @BeiZhu NVARCHAR(30)
AS
BEGIN
    IF NOT EXISTS
    (
        SELECT 1 FROM [dbo].[ZTestData]
        WHERE 
            [ID] = @ID
            AND[BeiZhu] = @BeiZhu
    )
    BEGIN    
        UPDATE [dbo].[ZTestData]
        SET
            [BeiZhu] = @BeiZhu,
            [UpdatedDate] = GETDATE()
        WHERE 
            [ID] = @ID
    END
    ELSE
    BEGIN
        RAISERROR(N'备注内容无任何变化,更新略过。',16,1)
        RETURN
    END
END
GO
View Code

 

上面数据库改正,它只是让更新是否继续。性能还是不容乐观,因为网络连接还是要连,数据sql语句还是要执行一遍。

不防改前端,参考如下,
2026-04-30_10-33-44

 

 window.lastSavedData = window.lastSavedData || new Map();

 function updateBeizhuByTextBox(txtboxElement) {
     return new Promise((resolve, reject) => {       

         var uniqueKey = txtboxElement.id + '_' + HiddenID.value.trim();                
         var newValue = txtboxElement.value.trim();

         // 检查是否与上次成功保存的值相同
         var lastValue = window.lastSavedData.get(uniqueKey);
         if (lastValue === newValue) {
             console.log("内容未改变,无需保存", uniqueKey, newValue);
             return resolve("内容未改变");
         }

         let url = '/api/IMT/UpdateBeiZhu';
         var postData = {
             ID: HiddenID.value,
             BeiZhu: newValue
         };

         axios.post(url, JSON.stringify(postData), {
             headers: { 'Content-Type': 'application/json; charset=utf-8' }
         })
             .then(response => {
                 if (response.data.Status !== true && response.data.Status !== "true") {
                     alert(response.data.Message);
                     reject(response.data.Message);
                 } else {
                     alert(response.data.Message);
                     window.lastSavedData.set(uniqueKey, newValue);
                     console.log("已记录", uniqueKey, newValue);
                     resolve(response.data);
                 }
             })
             .catch(error => {
                 console.error(error);
                 alert("保存失败,请重试!", true);
                 reject(error);
             })
             .finally(() => {
                 //
             });
     });
 }       
View Code

 

这样一改,可以防止同一文本框第2次更新时,不会去呼叫Web API把数据上传至数据库进行更新。
虽然不是很完善,已经做出很大改善了。

是否有更好方案?期待......
 

posted @ 2026-04-30 10:53  Insus.NET  阅读(28)  评论(0)    收藏  举报