为什么jquery.blockUI吞下onClick事件?

时间:2019-07-19 16:06:26

标签: javascript backbone.js marionette jquery-blockui

摘要:使用jquery.blockUI似乎会隐藏/吞咽/遮罩按钮单击事件。

技术史

  1. 主干和木偶
  2. backbone.radio
  3. 下划线
  4. jquery
  5. jquery.blockUI

(所有最新版本)


应用

该应用程序由文本输入和按钮组成。

enter image description here

关于主干/牵线木马的术语,有

  1. 具有2个区域的顶视图
  2. 作为文本输入的容器视图
  3. 具有按钮的页脚视图
  4. 容器视图由模型支持。
  5. 页脚中有一个按钮,单击该按钮将发送一个border.radio事件。
  6. 该事件在顶视图中显示。

enter image description here

当用户离开文本输入时,将调用API(服务器/后端)。在该示例中,Promise / setTimeout用于模拟呼叫。

在该示例中,按钮调用console.log。


代码

这是JSFiddle Example on JSFiddle,在Javascript代码下方

// ------------------------------------------------------------------
var Model = Backbone.Model.extend({

  defaults: {
    "SearchCriteria": {
      "Min": { "value": "abc123", "ReadOnly": true }
    }
  },


  async callBackend() {

    //$.blockUI(); //<----- uncomment this and the button click is swallowed

    await new Promise(resolve => setTimeout(resolve, 3000));

    $.unblockUI();
  }

});
// ------------------------------------------------------------------

// ------------------------------------------------------------------
var ContainerView = Marionette.View.extend({
  template: _.template('<div><label>Container</label></div><div><input id = "min" name = "min" type = "text"/></div>'),

  events: {
    'change': 'onChangeData',
  },

  async onChangeData(data) {
    console.log('start onChangeData');    
    await this.model.callBackend();
    this.render();
    console.log('end onChangeData');    
  }

});
// ------------------------------------------------------------------

// ------------------------------------------------------------------
var FooterView = Marionette.View.extend({
  template: _.template('<div><button class="btn-footer-test">Footer</button></div>'),

  events: {
    "click .btn-footer-test": () => {
      console.log('click test ...');
      Backbone.Radio.channel("maske").trigger("select:test");
    }
  },

});
// ------------------------------------------------------------------

// ------------------------------------------------------------------
var TopView = Marionette.View.extend({
  template: _.template("<div id='container'></div><div id='footer'></div>"),

  regions: {
    container: '#container',
    footer: '#footer'
  },

  events: {
    'change': 'onChangeData',
  },

  initialize() {
    this.listenTo(Backbone.Radio.channel("maske"), "select:test", this.onTest, this);
  },

  onRender() {
    this.showChildView('container', new ContainerView({
      model: new Model()
    }));
    this.showChildView('footer', new FooterView());
  },

  onChangeData(data) {
  },

  onTest() {
    //NOT called if jquery.blockUI present ******

    console.log('onTest');
  }
});
// ------------------------------------------------------------------

$(document).ready(function () {
  console.log('Start');
  const topView = new TopView();

  topView.render();
  $('body').append(topView.$el);
});

使用

用户像这样使用应用程序。用户

  1. 更改文本输入
  2. 并直接单击按钮(无需先跳出字段!)

预期行为

  1. 对文本输入的更改将触发更改事件。
    1. jquery.blockUI
    2. 异步通话
    3. jquery unblockUI
  2. 按钮的点击事件已执行

实际行为

存在jquery.blockUI函数时,不执行按钮的click事件。注释jquery.blockUI会发生按钮单击事件,但是会在等待返回之前。


问题

  1. 我在做什么错?
  2. 为什么点击事件会被吞噬?

1 个答案:

答案 0 :(得分:1)

  

我在做什么错了?

您的期望是错误的。 JavaScript中没有隐式机制可以在异步事件完成后一个序列化异步事件。您(开发人员)负责异步事件的同步。

  

为什么吞下了点击事件?

Click事件触发when a mousedown and mouseup event occur on the same element。这不是你的情况。事件的顺序如下:

  1. mousedown上的<button>
  2. change上的<input>;导致通过<div>显示显示blockUI覆盖
  3. mouseup覆盖<div>
  4. click上触发mousedownmouseup的元素<body>的最接近的公共父元素

从技术上讲,更改输入后似乎无法单击按钮,因为叠加层显示在mouseup之前,但是有一种方法。如果在显示叠加层时单击并按住鼠标按钮,然后再释放它,则将在按钮上触发click事件,但这仍然不是您想要的。

尝试使用此代码段。它记录每个mousedownmouseupclickchange事件。它在change上注册了异步<input>事件处理程序,然后在第一秒钟不执行任何操作,然后显示一个覆盖图,然后休眠3秒钟,最后隐藏该覆盖图。您可以按住按钮鼠标的时间来观察各种行为。

更改输入文字并快速单击按钮

  

button.mousedown
  input.change
  button.mouseup
  按钮。单击

更改输入文本,单击按钮并保持1秒钟,然后松开

  

button.mousedown
  input.change
   div.mouseup
   body.click

更改输入文本,单击按钮并保持4秒钟(直到覆盖消失),然后释放

  

button.mousedown
  input.change
  button.mouseup
  按钮。单击

$(document).on('mousedown mouseup click change', e => {
  console.log(e.target.tagName.toLowerCase() + '.' + e.type);
});

$('input').change(async _ => {
  await new Promise(resolve => setTimeout(resolve, 1000));
  const $div = $('<div style="position:fixed; left:0; top:0; right:0; bottom:0; background-color:rgba(0,0,0,0.5); z-index:999">').appendTo('body');
  await new Promise(resolve => setTimeout(resolve, 3000));
  $div.remove();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input type="text" placeholder="type something, then click the button" style="width:250px">
<button>Button</button>

您将很难解决这个问题,因为在正常情况下,不会在按钮上触发click事件,因此建议您重新考虑用户界面。 为什么在使用异步/承诺时甚至会阻止用户界面?发明它是为了避免阻塞。