【问题标题】:Is there a way to stop all other events in a dojo onBlur event handler?有没有办法停止 dojo onBlur 事件处理程序中的所有其他事件?
【发布时间】:2012-06-04 16:12:17
【问题描述】:

也许我走错了路……

设置: 我有一个相当复杂的完整的 dojofied Web 应用程序。这个问题的重要部分是在dijit.layout.BorderContainer 的中心区域有一个较长的表格,在其他区域有导航树和一些操作按钮。

我想做的事: 如果用户确实输入了数据但没有保存,如果他要离开表单,他们应该会收到一条警告消息(如果他离开,点击“新元素”按钮,...)。为了获得更好的用户体验,我想提供一个带有“保存”、“无论如何离开”、“取消”选项的模式对话框。 可能的想法是使用表单的 onBlur 事件,停止所有其他事件(很可能是某个其他小部件上的 onClick),检查更改,如果有更改,则显示对话框,否则让其他事件继续。 我不想为所有非表单活动元素添加 checkChanges 方法!

对于第一次测试,我只是试图阻止事件...

这行得通

<div id="formArea" dojoType="dijit.form.Form" encoding="multipart/form-data" action=""  class="ContentPane" region="center">
   <script type="dojo/connect" event="onBlur" >
      alert("I don't think so");
    </script>
 </div>

...但它很难看,我不能轻易继续

这不是

<div id="formArea" dojoType="dijit.form.Form" encoding="multipart/form-data" action=""  class="ContentPane" region="center">
   <script type="dojo/connect" event="onBlur" args="e">
      console.log("blur"); // ok
      e.preventDefault();//event.stopt(e)//return false //<--neither of these
    </script>
 </div>

问题是,如果我点击表单外的按钮,onBlur 会触发,但我无法停止按钮上的onClick。 我知道 onBlur 不提供事件对象 - 所以 e.something 不能真正工作...... 有什么办法可以捕捉到另一个元素上的onClick

【问题讨论】:

  • 问题是浏览器可能有N个事件在运行。上面的 onBlur 只会阻止对其中之一的操作,即当前的 onBlur 事件。

标签: javascript dojo dom-events


【解决方案1】:

Pause button event listener(s) 在表单的onBlur 中(如果未保存数据)。

查看实际操作:http://jsfiddle.net/phusick/A5DHf/

你有一些按钮事件监听器,通过on.pausable(node, event, callback)而不是on()注册它们:

var b1Handler = on.pausable(button1Node, "click", function() {
    console.log("b1.onClick");        
});

var b2Handler = on.pausable(button2Node, "click", function() {
    console.log("b2.onClick");        
});

将处理程序收集到一个数组中:

var handlersToPause = [b1Handler, b2Handler];

添加onBlur事件监听器:

on(form, "blur", function(e) {

    if (this.isDirty()) {
        // if data are not saved, pause button event listeners
        handlersToPause.forEach(function(handler) {
            handler.pause();
        });
        // display modal dialog here     
    }            
});

添加例如onFocus事件监听器恢复按钮事件监听器:

on(form, "focus", function(e) {
    handlersToPause.forEach(function(handler) {
        handler.resume();            
    });
});

请注意,handler.pause() 正在暂停 onclick 侦听器,而不是事件。 onclick 事件在Event queue 中等待,因此在onblur 的执行时间内无法访问。

我会制定一些更强大的解决方案,但这很快就能回答您的问题。不管怎样,看看dojo/aspect 和它的around advice 来调用你的checkChanges,而不需要改变所有非表单的活动元素。

【讨论】:

  • 感谢您的想法。 on()dojo/aspect 对我来说是新的,我绝对需要看看它们。不过,目前我们仍然坚持使用 dojo 1.6:/
  • 您可以使用 Dojo 1.6 或多或少做同样的事情,只需使用 dojo.connect 而不是 dojo/ondojo/aspect
  • 是的,有暂停的事情^^但是,使用无控制器的所有匿名架构最终会受到伤害..问题是,如果所有浏览器都调用 onblur和 onclick 同步 - 刚刚在 FF 中测试了你的小提琴,在这里它可以正常工作。在您的小提琴中所做的更改,每次表单关注时都会保存值,并且 isDirty 会根据当前值检查这些值jsfiddle.net/seeds/A5DHf/16
【解决方案2】:

只有 confirm('question?') 会像这样“死锁”你的页面事件。

我已经做了一个类似的设置,但我解决这个问题的方式(除非用户在地址栏中输入 url 并按回车键)是每当单击导航树时弹出对话框,将用户发送到新视图。考虑:

----------------------------------------
| Nav 1  |   Asset1 ( view controller )  |
| Nav 2  |   Asset2 ( hidden )           |
----------------------------------------

Nav 1 是默认的加载视图,资产 1 已加载,包含“设置页面”表单或类似表单,并且可以更改。诀窍是,Asset1 和 Asset2 是从 AbstractAsset 派生的,而 AbstractAsset 又是一个简单的 ContentPane 扩展。

在 AbstractAsset 中是一个名为 isDirty 的“必须覆盖功能”函数

var Viewcontroller = declare("AbstractAsset", [dijit.ContentPane], {
       isDirty: function() { return false; }
});


declare("Asset1", [Viewcontroller], {

startup: function() {
  // sets up form
  ...
  // and references inputfields to 'this'
  this.inputfields = this.form.getChildren();

  // and saves (clones) the state of each field
  var self = this;
  this.inputfields.forEach(function(inputWidget) {
     self.states[inputWidget.id] = inputWidget.get("value");
  });
},
isDirty: function() {
  var self = this;
  var dirty = false;
  this.form.getChildren().some(input) {
     if(self.states[input.id] != input.get("value")) {
        dirty = true;
        return false; // breaks .some loop
     }
     return true;
  });
  return dirty;
}

})

然后,每次导航点击都必须调用当前可见的视图控制器的 isDirty 函数才能继续。假设用户单击导航树 (dijit.Tree) 行节点 Nav 2

var navigation = dojo.declare("NavigationController", [dijit.Tree], {
 currentView : null,
 onLoad: function() {
   // start Asset1 in viewNode by default
   this.currentView = new Asset1({ }, this.viewNode); 
 },
 onClick : function() {
    if(this.currentView.isDirty()) alert("I Dont Think So");
    else {
       this.loadFunction(this.model.selection.getSelected());
    }
 }

});

这是实现卸载检查的一般思路,您需要通过“主应用程序控制器”挂钩任何 onClick 事件以确定应该发生什么。 Check this application 用作 cms 导航控制器,page.js:587 用于 isDirty 示例

【讨论】:

  • 感谢您的想法!我需要考虑一下。与 phusicks 的回答一样,我有点难过我仍然必须触摸其他(非形式)元素,但似乎这是唯一的方法
  • 恐怕 m8 :) 在一个 DOM 中触发的事件对另一个 DOM 触发的事件是隐藏的
  • 两个答案之间的艰难选择,我认为这更接近我可以在我们的架构中使用的内容。
猜你喜欢
  • 2022-06-18
  • 1970-01-01
  • 1970-01-01
  • 2014-07-22
  • 2018-03-16
  • 1970-01-01
  • 1970-01-01
  • 2012-08-05
  • 1970-01-01
相关资源
最近更新 更多