关于 javascript:如何在输入更改事件之前获取 Bootstrap 预先输入的点击结果

How to get Bootstrap typeahead click result before the input change event

我正在尝试使用引导程序预先输入作为用户的灵活选择。因此,如果该项目是已知的,他们会选择它,否则会打开一个对话框,允许他们输入新项目。

我通过观察输入上的更改事件来执行此操作,如果输入 val 不在源数组中(对于预先输入),则向用户显示"添加项目"对话框。问题是,如果用户单击其中一个选项,则会在预先输入有机会设置 val 之前发送更改事件。这是因为点击会导致文本模糊。

我想检查活动元素以解决此问题,因此在更改事件中查看 document.activeElement 并查看它是否是预先输入的选项之一,这不起作用并返回了整个正文元素。

这是代码的精简版:

HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
    <h3 id="myModalLabel">Unknown Contact
 
 
    <p>The contact you have entered is unknown. Press cancel to enter a different contact or fill out the details below to create a new contact</p>
    <label>Name</label>
    <input id="modal_contact"  type="text" placeholder="dealership contact" value=""/>
    <label>Email</label>
    <input id="modal_contact_email" type="text" placeholder="dealership contact" value=""/>
 
 
    <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
    <button id="save" class="btn btn-primary">Save changes</button>

Javascript

1
2
3
4
5
6
7
8
9
10
11
12
13
var contacts = ['pete','geoff'];

$('.typeahead').typeahead({source:contacts, updater : function(item){
    console.log('updater fired'); //fires after first change event
    return item;
}});

$('input').change(function(ev){
    console.log($(ev.target).val());

    if ($.inArray($(ev.target).val(), contacts) < 0)
        $('#contactModal').modal();
})

还有一个 JSFiddle 版本 http://jsfiddle.net/k39vM/

有谁知道我如何测试用户是否点击了提前输入以找出是否导致更改事件?


我之前的建议实际上没有奏效,因为正如您所提到的,有一个有点意外的 blur 事件在菜单上的 click 事件之前触发了 change。我认为它与 Typeahead.prototype.select 方法中的 this.hide() 调用有关。

但是,经过一些反复试验,我确实认为我可能已经找到了解决方法。导致问题的实际上并不是 select 方法中的 this.hide() 调用。了解用户可以通过两种方式触发选择有助于理解有希望的解决方法(我仅在 Chrome 中测试过):使用键盘,例如按 Enter,或单击项目。结果,知道点击是两者的问题,我注意到 mousedover 事件在用户将鼠标悬停在下拉菜单上时被维护。

因此,可以在 change 事件中手动忽略奇怪的行为。为了简化确定实际导致下一个 change 事件的原因的过程,我使用了一个名为"selected"的不同(自定义)事件来表示用户更改了值,而不是标准的 change。不幸的是,当 mousedover 属性为 true:

时,您仍然必须手动忽略 change 事件

简化的HTML(我使用了p标签,因为我发现Chrome在调试JSFiddle的JavaScript时有问题,与控制台结合导致不好的时间):

1
2
3
<input class="typeahead" type="text" placeholder="contact" value="Peter skillet"></input>
<input type="text"></input>
<p id="text" />

JavaScript(文本可以用 console.log 代替,有兴趣的可以用):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var contacts = ['pete', 'geoffrey'];

var $text = $("#text");
var typeahead = $('.typeahead').typeahead({source: contacts}).data('typeahead');

typeahead.select = function () {
    var val = this.$menu.find('.active').attr('data-value');
    this.$element.val(this.updater(val))
        .trigger('selected'); // <- unique event
    return this.hide()
};

$('input.typeahead').on('change', function (e) {
    var $target = $(e.target);
    if (typeahead.mousedover) {
        $text.html($text.html() +"mousedover [ignored selection]<br />");
    }
    else if ($.inArray($target.val(), contacts) < 0) {
        $text.html($text.html() +"show add<br/>");
    }
}).on('selected', function () {
    $text.html($text.html() +"selected<br />");
});

作为参考,这是默认的 select 方法:

1
2
3
4
5
6
7
select: function () {
  var val = this.$menu.find('.active').attr('data-value')
  this.$element
    .val(this.updater(val))
    .change()
  return this.hide()
}


该问题的一个更简单的解决方案是在您的点击事件中包含以下内容:

1
2
3
var typeahead = $(this).data("typeahead");
if (typeahead && this.value != typeahead.$menu.find('.active').attr('data-value'))
  return;

如果附加,第一行将检索预先输入。第二个将确保当前值与所选值匹配。

您还可以在 if 中检查 typeahead.mousedover 以允许无效(意味着不是预输入选项)输入在控件失去焦点而不进行选择时仍触发默认更改行为。