Select2 4.0.0 AJAX - 使用 Tab 选择突出显示的选项
Select2 4.0.0 AJAX - Choose highlighted option with Tab
我正在通过 AJAX 获取用户 ID 和名称并使用 Select2 搜索它们,但我的用户已请求能够从预输入下拉列表中 select按 Tab,有效地将其视为按 Enter。这是我的 select2 声明:
$("#user-select").select2({
ajax: {
url: "/api/User",
method: "get",
data: function (params) {
return {
search: params.term
};
},
beforeSend: function () {
$(".loading-results").text("Loading...");
},
processResults: function (data) {
return {
results: data
};
},
cache: true
},
allowClear: true,
placeholder: "Enter a User ID or Name",
templateResult: function (data) {
return "(" + data.id + ") " + data.name;
},
templateSelection: function (data) {
return "(" + data.id + ") " + data.name;
}
".select2-search__field" 似乎是下拉列表可见时的焦点元素,并且突出显示的元素获得 class "select2-results__option--highlighted".
我尝试了一些解决方案,但似乎没有任何效果,尤其是因为此元素会在下拉菜单打开时出现和消失。不幸的是,我在尝试时丢失了代码,但它们主要包括当 Tab 在焦点输入上被击中时执行 preventDefault,然后在突出显示的元素上触发点击事件或在输入上触发回车键。
我也试过调整 selectOnClose 选项,但总的来说这似乎有问题,当我 运行 通常使用它时会导致无限循环,更不用说尝试根据什么键覆盖它了正在按下。
[编辑]
selected 解决方案有效,但不考虑指定的 templateResult,而是显示“() undefined”。因此,我对其进行了调整,将突出显示的答案添加为覆盖 Select 的 selected 选项,然后在 Select.
上调用更改事件
...(与初始 select2 相同)
}).on('select2:close', function (evt) {
var context = $(evt.target);
$(document).on('keydown.select2', function (e) {
if (e.which === 9) { // tab
var highlighted = context.data('select2').$dropdown.find('.select2-results__option--highlighted');
if (highlighted) {
var data = highlighted.data('data');
var id = data.id;
var display = data.name;
$("#user-select").html("<option value='" + id + "' selected='selected'>" + display + "</option>");
$("#user-select").change();
}
else {
context.val("").change();
}
}
});
我也一直在努力寻找解决这个问题的方法。
主要问题是 select2 事件没有提供任何关于按下哪个键的信息。
所以我想出了这个 hack 来访问 select2 上下文中的 keydown 事件。
我一直在尽我最大的能力测试它,它似乎工作得很好。
selectElement
.select2({ options ... })
.on('select2:close', function(evt) {
var context = $(evt.target);
$(document).on('keydown.select2', function(e) {
if (e.which === 9) { // tab
var highlighted = context
.data('select2')
.$dropdown
.find('.select2-results__option--highlighted');
if (highlighted) {
var id = highlighted.data('data').id;
context.val(id).trigger('change');
}
}
});
// unbind the event again to avoid binding multiple times
setTimeout(function() {
$(document).off('keydown.select2');
}, 1);
});
对于任何想要使用 tab-select 与 multi-select 一起工作的人来说,这对我有用:
$("#selected_ids").select2({ multiple: true }).on('select2:open', function(e) { selectOnTab(e) });
function selectOnTab(event){
var $selected_id_field = $(event.target);
$(".select2-search__field").on('keydown', function (e) {
if (e.which === 9) {
var highlighted = $('.select2-results__option--highlighted');
if (highlighted) {
var data = highlighted.data('data');
var vals = $selected_id_field.val();
if (vals === null){
vals = [];
}
vals.push(data.id)
$selected_id_field.val(vals).trigger("change")
}
}
});
}
目前这将我限制在每页一个字段,但它正在完成工作。
感谢 MikeOShay 和 Sniffdk 对此进行深入研究。
目前有一个未解决的问题可以为我们解决这个问题:
selectOnClose 功能似乎在 4.0.3 中稳定,并且更简单的解决方案:
$("#user-select").select2({
...
selectOnClose: true
});
模板的使用可能会干扰此功能,我没有使用模板,所以我没有测试过。
你可以简单的改变Select2控件的来源,只有一行:
else if (key === KEYS.ENTER)
else if (key === KEYS.ENTER || key ===
KEYS.TAB)
来自这里:
this.on('keypress', function (evt) {
var key = evt.which;
if (self.isOpen()) {
if (key === KEYS.ESC || key === KEYS.TAB ||
(key === KEYS.UP && evt.altKey)) {
self.close();
evt.preventDefault();
} else if (key === KEYS.ENTER) {
self.trigger('results:select', {});
evt.preventDefault();
对此
this.on('keypress', function (evt) {
var key = evt.which;
if (self.isOpen()) {
if (key === KEYS.ESC || (key === KEYS.UP && evt.altKey)) {
self.close();
evt.preventDefault();
} else if (key === KEYS.ENTER || key === KEYS.TAB) {
self.trigger('results:select', {});
evt.preventDefault();
可以在源文件src/js/select2/core.js或编译版本中进行更改。当我应用此更改时,我修改了 src/js/select2/core.js 并执行了 gruntfile.js 以再次编译 select2 库。此解决方案不是解决方法,而是 select2 的不错功能。
在尝试了所有这些解决方案之后,这个解决方案似乎能捕捉到最多的案例并且对我来说效果最好。请注意,我正在使用 select2 4.0.3 但不喜欢 selectOnClose,如果您有多个 select2 框和多个它会造成严重破坏!
var fixSelect2MissingTab = function (event) {
var $selected_id_field = $(event.target);
var selectHighlighted = function (e) {
if (e.which === 9) {
var highlighted = $selected_id_field.data('select2').$dropdown.find('.select2-results__option--highlighted');
if (highlighted) {
var data = highlighted.data('data');
if (data) {
var vals = $selected_id_field.val();
if (vals === null) {
vals = [];
}
if (vals.constructor === Array) {
vals.push(data.id);
} else {
vals = data.id;
}
$selected_id_field.val(vals).trigger("change");
}
}
}
};
$('.select2-search__field').on('keydown', selectHighlighted);
}
$(document).on('select2:open', 'select', function (e) { fixSelect2MissingTab(e) });
$(document).on('select2:close', 'select', function (e) {
//unbind to prevent multiple
setTimeout(function () {
$('.select2-search__field').off('keydown');
}, 10);
});
这个解决方案的好处是它是通用的,可以应用到框架代码中,因为它甚至可以用于动态添加的 select2 框。
我在 vue 中使用 select2 版本 4.0.6-rc.1,这是我为确保绑定安全所做的工作:
selectElement
.select2({ options ... })
.on("select2:close", function(evt) {
var context = $(evt.target);
$(document).on("keydown.select2", function(e) {
if (e.which === 9) {
var highlighted = context
.data("select2")
.$dropdown.find(".select2-results__option--highlighted");
if (highlighted) {
$.fn.select2.amd.require(["select2/utils"], function(Utils) {
var data = Utils.__cache[highlighted.data().select2Id].data;
var $select2 = context.data('select2');
$select2.trigger("select", {data: data});
});
}
}
});
setTimeout(function() {
$(document).off("keydown.select2");
}, 1);
});
对我来说,关键是 Utils 助手,它是库的一部分,从当前元素的缓存中检索列表,然后用 select 强制新值。
祝你好运! :)
类似于@Semen Shekhovtsov 的解决方案,但如果您希望 TAB 实际跳转到下一个字段并生成 selection(更像是一个普通输入 select将)。
将 KEYS.ENTER 和 KEYS.TAB 分离到它们自己的 else if 块中,并省略 evt.preventDefaults()。
按照下面的 core.js 或 select2.full.js(如果你不想重新编译它)。
if (key === KEYS.ESC || (key === KEYS.UP && evt.altKey)) {
self.close();
evt.preventDefault();
} else if (key === KEYS.ENTER){
self.trigger('results:select', {});
evt.preventDefault();
} else if (key === KEYS.TAB){
self.trigger('results:select', {});
// leave out the prevent default if you want it to go to the next form field after selection
//evt.preventDefault();
}
我混合了这里提出的一些解决方案,others 在元素获得焦点时打开 select2 下拉菜单。
我还希望 TAB 键可以使 selection 立即聚焦到下一个 select2 字段。 SHIFT-TAB 反而会聚焦前一个 select2 字段。
这是我的最终代码(select2 4.0.5,在 FF 和 Chrome 上测试)。我假设您的 select 字段具有 "select2" class:
$('.select2').select2().on('select2:close', function (e) {
var target = $(e.target);
$(document).on('keydown.select2', function(e) {
if (e.which === 9) { // tab
var highlighted = target
.data('select2')
.$dropdown
.find('.select2-results__option--highlighted');
if (highlighted) {
// select the option
var id = highlighted.data('data').id;
target.val(id);
target.trigger('change');
// focus the next (or the previous) field with "select2" class
var set = $('.select2');
var current_index = set.index(target);
var next_index = current_index + 1;
if (e.shiftKey) {
next_index = current_index - 1;
}
var next = set.eq(next_index)
next.focus();
}
}
});
// unbind the event again to avoid binding multiple times
setTimeout(function() {
$(document).off('keydown.select2');
}, 1);
});
// on focus, open the menu
$(document).on('focus', '.select2-selection.select2-selection--single', function (e) {
$(this).closest(".select2-container").siblings('select:enabled').select2('open');
});
我发现 Sniffdk 接受的答案不再适用于最新的 jquery 和 select2 库。它给了我一个 Uncaught TypeError: Cannot read property 'id' of undefined
.
我提出了以下有效的解决方案(对于单选 select2 下拉菜单):
function pickSelect2OptionOnTab() {
let $select;
let optionSelected;
let select2Closing = false;
$('select').on('select2:closing', function(event) {
select2Closing = true;
$select = $(event.target);
optionSelected = $('.select2-results__option--highlighted').text();
setTimeout(function() {
select2Closing = false;
}, 1);
});
$(document).bind('keydown', function(event) {
if (event.key === 'Tab' && select2Closing) {
const val = $select.find('option').filter(function() {
return $(this).text() === optionSelected;
}).first().prop('value');
$select.val(val);
$select.trigger('change');
}
});
}
我正在通过 AJAX 获取用户 ID 和名称并使用 Select2 搜索它们,但我的用户已请求能够从预输入下拉列表中 select按 Tab,有效地将其视为按 Enter。这是我的 select2 声明:
$("#user-select").select2({
ajax: {
url: "/api/User",
method: "get",
data: function (params) {
return {
search: params.term
};
},
beforeSend: function () {
$(".loading-results").text("Loading...");
},
processResults: function (data) {
return {
results: data
};
},
cache: true
},
allowClear: true,
placeholder: "Enter a User ID or Name",
templateResult: function (data) {
return "(" + data.id + ") " + data.name;
},
templateSelection: function (data) {
return "(" + data.id + ") " + data.name;
}
".select2-search__field" 似乎是下拉列表可见时的焦点元素,并且突出显示的元素获得 class "select2-results__option--highlighted".
我尝试了一些解决方案,但似乎没有任何效果,尤其是因为此元素会在下拉菜单打开时出现和消失。不幸的是,我在尝试时丢失了代码,但它们主要包括当 Tab 在焦点输入上被击中时执行 preventDefault,然后在突出显示的元素上触发点击事件或在输入上触发回车键。
我也试过调整 selectOnClose 选项,但总的来说这似乎有问题,当我 运行 通常使用它时会导致无限循环,更不用说尝试根据什么键覆盖它了正在按下。
[编辑]
selected 解决方案有效,但不考虑指定的 templateResult,而是显示“() undefined”。因此,我对其进行了调整,将突出显示的答案添加为覆盖 Select 的 selected 选项,然后在 Select.
...(与初始 select2 相同)
}).on('select2:close', function (evt) {
var context = $(evt.target);
$(document).on('keydown.select2', function (e) {
if (e.which === 9) { // tab
var highlighted = context.data('select2').$dropdown.find('.select2-results__option--highlighted');
if (highlighted) {
var data = highlighted.data('data');
var id = data.id;
var display = data.name;
$("#user-select").html("<option value='" + id + "' selected='selected'>" + display + "</option>");
$("#user-select").change();
}
else {
context.val("").change();
}
}
});
我也一直在努力寻找解决这个问题的方法。
主要问题是 select2 事件没有提供任何关于按下哪个键的信息。
所以我想出了这个 hack 来访问 select2 上下文中的 keydown 事件。
我一直在尽我最大的能力测试它,它似乎工作得很好。
selectElement
.select2({ options ... })
.on('select2:close', function(evt) {
var context = $(evt.target);
$(document).on('keydown.select2', function(e) {
if (e.which === 9) { // tab
var highlighted = context
.data('select2')
.$dropdown
.find('.select2-results__option--highlighted');
if (highlighted) {
var id = highlighted.data('data').id;
context.val(id).trigger('change');
}
}
});
// unbind the event again to avoid binding multiple times
setTimeout(function() {
$(document).off('keydown.select2');
}, 1);
});
对于任何想要使用 tab-select 与 multi-select 一起工作的人来说,这对我有用:
$("#selected_ids").select2({ multiple: true }).on('select2:open', function(e) { selectOnTab(e) });
function selectOnTab(event){
var $selected_id_field = $(event.target);
$(".select2-search__field").on('keydown', function (e) {
if (e.which === 9) {
var highlighted = $('.select2-results__option--highlighted');
if (highlighted) {
var data = highlighted.data('data');
var vals = $selected_id_field.val();
if (vals === null){
vals = [];
}
vals.push(data.id)
$selected_id_field.val(vals).trigger("change")
}
}
});
}
目前这将我限制在每页一个字段,但它正在完成工作。
感谢 MikeOShay 和 Sniffdk 对此进行深入研究。
目前有一个未解决的问题可以为我们解决这个问题:
selectOnClose 功能似乎在 4.0.3 中稳定,并且更简单的解决方案:
$("#user-select").select2({
...
selectOnClose: true
});
模板的使用可能会干扰此功能,我没有使用模板,所以我没有测试过。
你可以简单的改变Select2控件的来源,只有一行:
else if (key === KEYS.ENTER)
else if (key === KEYS.ENTER || key === KEYS.TAB)
来自这里:
this.on('keypress', function (evt) {
var key = evt.which;
if (self.isOpen()) {
if (key === KEYS.ESC || key === KEYS.TAB ||
(key === KEYS.UP && evt.altKey)) {
self.close();
evt.preventDefault();
} else if (key === KEYS.ENTER) {
self.trigger('results:select', {});
evt.preventDefault();
对此
this.on('keypress', function (evt) {
var key = evt.which;
if (self.isOpen()) {
if (key === KEYS.ESC || (key === KEYS.UP && evt.altKey)) {
self.close();
evt.preventDefault();
} else if (key === KEYS.ENTER || key === KEYS.TAB) {
self.trigger('results:select', {});
evt.preventDefault();
可以在源文件src/js/select2/core.js或编译版本中进行更改。当我应用此更改时,我修改了 src/js/select2/core.js 并执行了 gruntfile.js 以再次编译 select2 库。此解决方案不是解决方法,而是 select2 的不错功能。
在尝试了所有这些解决方案之后,这个解决方案似乎能捕捉到最多的案例并且对我来说效果最好。请注意,我正在使用 select2 4.0.3 但不喜欢 selectOnClose,如果您有多个 select2 框和多个它会造成严重破坏!
var fixSelect2MissingTab = function (event) {
var $selected_id_field = $(event.target);
var selectHighlighted = function (e) {
if (e.which === 9) {
var highlighted = $selected_id_field.data('select2').$dropdown.find('.select2-results__option--highlighted');
if (highlighted) {
var data = highlighted.data('data');
if (data) {
var vals = $selected_id_field.val();
if (vals === null) {
vals = [];
}
if (vals.constructor === Array) {
vals.push(data.id);
} else {
vals = data.id;
}
$selected_id_field.val(vals).trigger("change");
}
}
}
};
$('.select2-search__field').on('keydown', selectHighlighted);
}
$(document).on('select2:open', 'select', function (e) { fixSelect2MissingTab(e) });
$(document).on('select2:close', 'select', function (e) {
//unbind to prevent multiple
setTimeout(function () {
$('.select2-search__field').off('keydown');
}, 10);
});
这个解决方案的好处是它是通用的,可以应用到框架代码中,因为它甚至可以用于动态添加的 select2 框。
我在 vue 中使用 select2 版本 4.0.6-rc.1,这是我为确保绑定安全所做的工作:
selectElement
.select2({ options ... })
.on("select2:close", function(evt) {
var context = $(evt.target);
$(document).on("keydown.select2", function(e) {
if (e.which === 9) {
var highlighted = context
.data("select2")
.$dropdown.find(".select2-results__option--highlighted");
if (highlighted) {
$.fn.select2.amd.require(["select2/utils"], function(Utils) {
var data = Utils.__cache[highlighted.data().select2Id].data;
var $select2 = context.data('select2');
$select2.trigger("select", {data: data});
});
}
}
});
setTimeout(function() {
$(document).off("keydown.select2");
}, 1);
});
对我来说,关键是 Utils 助手,它是库的一部分,从当前元素的缓存中检索列表,然后用 select 强制新值。
祝你好运! :)
类似于@Semen Shekhovtsov 的解决方案,但如果您希望 TAB 实际跳转到下一个字段并生成 selection(更像是一个普通输入 select将)。 将 KEYS.ENTER 和 KEYS.TAB 分离到它们自己的 else if 块中,并省略 evt.preventDefaults()。 按照下面的 core.js 或 select2.full.js(如果你不想重新编译它)。
if (key === KEYS.ESC || (key === KEYS.UP && evt.altKey)) {
self.close();
evt.preventDefault();
} else if (key === KEYS.ENTER){
self.trigger('results:select', {});
evt.preventDefault();
} else if (key === KEYS.TAB){
self.trigger('results:select', {});
// leave out the prevent default if you want it to go to the next form field after selection
//evt.preventDefault();
}
我混合了这里提出的一些解决方案,others 在元素获得焦点时打开 select2 下拉菜单。
我还希望 TAB 键可以使 selection 立即聚焦到下一个 select2 字段。 SHIFT-TAB 反而会聚焦前一个 select2 字段。
这是我的最终代码(select2 4.0.5,在 FF 和 Chrome 上测试)。我假设您的 select 字段具有 "select2" class:
$('.select2').select2().on('select2:close', function (e) {
var target = $(e.target);
$(document).on('keydown.select2', function(e) {
if (e.which === 9) { // tab
var highlighted = target
.data('select2')
.$dropdown
.find('.select2-results__option--highlighted');
if (highlighted) {
// select the option
var id = highlighted.data('data').id;
target.val(id);
target.trigger('change');
// focus the next (or the previous) field with "select2" class
var set = $('.select2');
var current_index = set.index(target);
var next_index = current_index + 1;
if (e.shiftKey) {
next_index = current_index - 1;
}
var next = set.eq(next_index)
next.focus();
}
}
});
// unbind the event again to avoid binding multiple times
setTimeout(function() {
$(document).off('keydown.select2');
}, 1);
});
// on focus, open the menu
$(document).on('focus', '.select2-selection.select2-selection--single', function (e) {
$(this).closest(".select2-container").siblings('select:enabled').select2('open');
});
我发现 Sniffdk 接受的答案不再适用于最新的 jquery 和 select2 库。它给了我一个 Uncaught TypeError: Cannot read property 'id' of undefined
.
我提出了以下有效的解决方案(对于单选 select2 下拉菜单):
function pickSelect2OptionOnTab() {
let $select;
let optionSelected;
let select2Closing = false;
$('select').on('select2:closing', function(event) {
select2Closing = true;
$select = $(event.target);
optionSelected = $('.select2-results__option--highlighted').text();
setTimeout(function() {
select2Closing = false;
}, 1);
});
$(document).bind('keydown', function(event) {
if (event.key === 'Tab' && select2Closing) {
const val = $select.find('option').filter(function() {
return $(this).text() === optionSelected;
}).first().prop('value');
$select.val(val);
$select.trigger('change');
}
});
}