4

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

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

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

这是代码的精简版本:

html

<div id="contactModal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
  <div class="modal-header">
    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
    <h3 id="myModalLabel">Unknown Contact</h3>
  </div>
  <div class="modal-body">
    <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=""/>
  </div>
  <div class="modal-footer">
    <button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
    <button id="save" class="btn btn-primary">Save changes</button>
  </div>
</div>

Javascript

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/

有谁知道我如何测试用户是否点击了一个提前输入来确定这是否导致了更改事件?

4

2 回答 2

4

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

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

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

简化的 HTML(我使用了这个p标签,因为我发现 Chrome 在调试 JSFiddle 的 JavaScript 时遇到了麻烦,并且与控制台结合使用会导致糟糕的时间):

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

console.logJavaScript(对于那些感兴趣的人来说,文本可以替换为舒适):

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方法:

select: function () {
  var val = this.$menu.find('.active').attr('data-value')
  this.$element
    .val(this.updater(val))
    .change()
  return this.hide()
}
于 2013-04-19T02:50:29.830 回答
3

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

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

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

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

于 2014-05-28T18:56:42.320 回答