backbone.js 点击和模糊事件
我在主干中的模糊和点击事件方面遇到了一些麻烦。我有一个视图(下面的代码),它创建一个带有按钮的小搜索条目 div。我弹出这个 div 并将焦点放在输入字段上。如果有人点击关闭(模糊),我会通知父视图关闭此视图。如果他们点击按钮,我将启动搜索。
模糊行为工作正常,但是当我单击按钮时,我也会收到模糊事件,并且无法获取单击事件。我的结构正确吗?
顺便说一句,其他一些帖子提出了一些建议,例如向 div 添加计时器,以防在单击事件触发之前关闭它。我可以完全注释掉关闭,但仍然只能得到模糊事件。这些是否按照先到先得的原则一次只发射一个?
PB_SearchEntryView = Backbone.View.extend({
template: _.template("<div id='searchEntry' class='searchEntry'><input id='part'></input><button id='findit'>Search</button></div>"),
events: {
"click button": "find",
"blur #part": "close"
},
initialize: function(args) {
this.dad = args.dad;
},
render: function(){
$(this.el).html(this.template());
return this;
},
close: function(event){ this.dad.close(); },
find: function() {
alert("Find!");
}
});
I'm having some trouble with blur and click events in backbone. I have a view (code below) that creates a little search entry div with a button. I pop open this div and put focus on the entry field. If someone clicks off (blur) I notify a parent view to close this one. If they click on the button I'll initiate a search.
The blur behavior works fine, however when I click on the button I also get a blur event and can't get the click event. Have I got this structured right?
BTW, some other posts have suggested things like adding timers to the div in case its being closed before the click event fires. I can comment out the close completely and still only get the blur event. Do these only fire one at a time on some kind of first-com-first-served basis?
PB_SearchEntryView = Backbone.View.extend({
template: _.template("<div id='searchEntry' class='searchEntry'><input id='part'></input><button id='findit'>Search</button></div>"),
events: {
"click button": "find",
"blur #part": "close"
},
initialize: function(args) {
this.dad = args.dad;
},
render: function(){
$(this.el).html(this.template());
return this;
},
close: function(event){ this.dad.close(); },
find: function() {
alert("Find!");
}
});
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我不确定问题是什么,但这是 jsbin 代码。
I am not sure what the problem was, but here is the jsbin code.
尽管这个问题已经有 11 年了,但使用当前版本的 Backbone、Underscore 和 jQuery 仍然有可能遇到这种情况,并且解决方案也仍然相同。
让我们从在可运行代码片段中重现问题开始。下面,我复制了问题中的代码并添加了一些模拟代码。要重现,请单击“运行代码片段”,然后通过单击内部手动聚焦文本输入,然后单击按钮。您将看到输入字段和按钮再次消失,并且
alert('Find!')
行不会运行:理由如下。用户的操作通常会触发许多不同类型的事件。在这种情况下,单击按钮可能会触发以下所有事件:
元素上的
blur
。元素上的
change
(仅当用户在单击按钮之前输入内容时;换句话说,如果blur
之前有一个input
事件)。单击
上述顺序是标准化的,因为该顺序对于实现用户友好的交互表单最有用。特别是,
上的
blur
和change
在click
code> 事件在步骤 7 中被触发,我们的PB_SearchEntryView
实例的find
方法仍然不会被调用,因为事件绑定在步骤 5 中被撤消。)因此,如果我们仍然想要
find
方法在处理blur
后被调用,我们需要防止同时调用remove
方法。一种简单有效的方法是延迟对this.dad.close
的调用。合适的延迟约为50毫秒;这足够长以确保首先触发奖励材料
该问题还询问代码的结构是否正确。下面是该代码段的第三个版本,其中我做了三项我认为会有所改进的更改:
PB_SearchEntryView
) 不再了解其父视图。它不是调用父级的方法,而是简单地触发一个事件。由应用程序的其他组件来侦听该事件并处理它。这减少了组件的耦合,通常使应用程序更易于测试和维护。 (对于长距离通信的松耦合,我建议使用 backbone.radio。)使用上面的代码片段有一个轻微的问题,至少在 Safari 中是这样:如果我通过更改返回键来提交表单,
find
方法仍然会被调用,但表单也会被删除。在这种情况下,我怀疑 Safari 在关闭警报后触发了新的blur
事件。解决这个问题就足以解决另一个问题了!Despite the question being 11 years old, it is still possible to run into this situation with current versions of Backbone, Underscore and jQuery, and the solution is also still the same.
Let us start with a reproduction of the problem in a runnable snippet. Below, I copied the code from the question and added some mock code. To reproduce, please click "Run code snippet", then manually focus the text input by clicking inside it, and then click the button. You will see that the input field and the button disappear again and the
alert('Find!')
line does not run:The reason is as follows. An action by the user will often trigger many different types of events. In this case, clicking the button may be expected to trigger all of the following events:
blur
on the<input>
element.change
on the<input>
element (only if the user was typing into it just before clicking the button; in other words, if theblur
was preceded by aninput
event).mousedown
on the<button>
element.mouseup
on the<button>
element.click
on the<button>
element.submit
on a parent<form>
element, if there is one (because we did not set the thetype
attribute of the<button>
and it defaults tosubmit
).The above order is standardized, because this order is most useful for implementing user-friendly interactive forms. In particular,
blur
andchange
on the<input>
trigger before any events on the<button>
, because those events are potentially good opportunities to validate the user's input prior to taking further form processing steps. Likewise, the<button>
events trigger before thesubmit
of the entire<form>
, because you might want to do final whole-form validation or other preprocessing before finally actually submitting the form.Handlers for these events are immediately invoked by the browser, and the call stack is allowed to unwind completely before the next user event is triggered. This is JavaScript's famous event loop at play. Hence, the following things happen when we click the button in the problematic snippet above:
blur
event fires.close
method of ourPB_SearchEntryView
instance is invoked.close
method of ourMockDadView
is invoked.remove
method of ourPB_SearchEntryView
instance is invoked.PB_SearchEntryView
instance is removed from theDOM
and its event bindings are deleted (this is the default behavior of theremove
prototype method ofBackbone.View
).click
event on the<button>
is never triggered, because the element has been removed from the DOM in step 5.click
event was triggered in step 7, thefind
method of ourPB_SearchEntryView
instance would still not be invoked, because the event binding was undone in step 5.)Hence, if we still want the
find
method to be invoked after theblur
has been handled, we need to prevent theremove
method from being invoked in the meanwhile. A straightforward and valid way to do this, is to delay the call tothis.dad.close
. A suitable delay is about 50 milliseconds; this is long enough to ensure that theclick
event of the<button>
is triggered first and short enough that the user does not notice it. We can even cancel the delayed call in order to keep the search view open when the button is clicked. In the snippet below, I changed theclose
andfind
methods to illustrate how this can be done and added comments to explain the mechanics:Bonus material
The question also asked whether the code was structured right. Below is a third version of the snippet, in which I made three changes that I think would be improvements:
PB_SearchEntryView
) no longer knows about its parent view. Instead of invoking a method on the parent, it simply triggers an event. It is up to other components of the application to listen for that event and handle it. This reduces coupling of components and generally makes applications easier to test and maintain. (For loose coupling of long-distance communication, I recommend using backbone.radio.)click
event on a<button>
, listen for asubmit
event on a<form>
. This is more semantic and appropriate when submitting a form is exactly what we are doing. This also enables the user to trigger the same event handler by pressing the return key while typing into the<input>
element. This does require that the<button type=submit>
is inside a<form>
element, which is why we set thetagName
of the view toform
.There is a slight catch with using the above snippet, at least in Safari: if I submit the form by pretting the return key, the
find
method is still invoked, but the form is also removed. I suspect Safari is triggering a newblur
event after closing the alert in this case. Solving that would be enough material for another question!