2

我有一个下拉菜单(产品类型):

在此处输入图像描述

由 Spring MVC 代码生成(Google MDL 绒毛去除)

<form:select path="productType" required="required">
    <form:option value="" label="" />
    <form:options items="${availableProductTypesForAdd}" />
</form:select>
<label for="productType">Product Type</label>

${availableProductTypesForAdd}控制器加载到模型中,如下所示:

HashMap<String, String> availableProductTypesForAdd = new HashMap<>();

availableProductTypesForAdd.put("191", "191 - Table");
availableProductTypesForAdd.put("201", "201 - Chair");
availableProductTypesForAdd.put("230", "230 - Desk");
availableProductTypesForAdd.put("232", "232 - Monitor");

model.put("availableProductTypesForAdd", availableProductTypesForAdd);

如果你打印${availableProductTypesForAdd}到屏幕上,它看起来像:

{191=191 - Table, 201=201 - Chair, 230=230 - Desk, 232=232 - Monitor}

填写表格的用户知道以下两件事之一:(1) 产品密钥(191、201 等)或 (2) 产品名称(桌子、椅子等)。

  • 部分用户不熟悉按键,需要按产品名称进行搜索。
  • 一些用户熟悉按键并且更喜欢更快捷的按键方式。

我想让这两个都可以通过键盘选择下拉菜单。即,当用户点击该字段时,他们可以在键盘上键入“201”或“椅子”,它将在列表中选择正确的项目。

目前,该列表只能通过类型代码(191、201 等)搜索,因为显示的字符串以代码开头。

我如何实现这一目标?

4

1 回答 1

2

Easiest and likely most preferable would be to use some replacement of standard select as it's not much customizable.

You can try 'Chosen' jQuery plugin. This will require external js and css. However, seems to fully satisfy you requirements without pain and workarounds.

Simply 4 extra lines in your jsp:

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>

<form:form id="form" method="post" modelAttribute="form" action="/submit2">
    <form:select path="productType" required="required" >
        <form:option value="" label="" />
        <form:options items="${availableProductTypesForAdd}" />
    </form:select>
    <label for="productType">Product Type</label>
    <input type="submit" name="action" value="Submit" />
</form:form>

<script src="https://harvesthq.github.io/chosen/docsupport/jquery-3.2.1.min.js"></script>
<script src="https://harvesthq.github.io/chosen/chosen.jquery.js"></script>
<link rel="stylesheet" href="https://harvesthq.github.io/chosen/chosen.css">
<script>$(function(){ $('select').chosen(); })</script>

and here you are: (it works like a charm in more or less fresh chrome, safari, and FF on OSX)

'Choosen' in action

$(function(){
    $('select').chosen();
});
<link href="https://harvesthq.github.io/chosen/chosen.css" rel="stylesheet"/>
<script src="https://harvesthq.github.io/chosen/docsupport/jquery-3.2.1.min.js"></script>
<script src="https://harvesthq.github.io/chosen/chosen.jquery.js"></script>

<form id="form" action="/submit2" method="post">
    <select id="productType" name="productType" required="required">
        <option value=""></option>
        <option value="232">232 - Monitor</option><option value="310">310 - Bad</option><option value="201">201 - Chair</option><option value="555">555 - Sofa</option><option value="191">191 - Table</option><option value="999">999 - Bookcase</option><option value="618">618 - Armchair</option><option value="230">230 - Desk</option>
    </select>
    <label for="productType">Product Type</label>
    <input type="submit" name="action" value="Submit">
</form>


Also you can take a look at HTML Data List element

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib uri = "http://java.sun.com/jsp/jstl/core" prefix = "c" %>

<form:form id="form" method="post" modelAttribute="form" action="/submit">
    <form:input path="productType" list="productTypes" required="required" autocomplete="off" />
        <datalist id="productTypes">
            <c:forEach items="${availableProductTypesForAdd}" var="productType">
                <option value="${productType.key}">${productType.value}</option>
            </c:forEach>
        </datalist>
    <label for="productType">Product Type</label>
    <button type="submit">Ok</button>
</form:form>

search anywhere

You can even remove keys from options as it shows and searches in both value and text

HashMap<String, String> availableProductTypesForAdd = new HashMap<>();
availableProductTypesForAdd.put("191", "Table");
availableProductTypesForAdd.put("201", "Chair");
availableProductTypesForAdd.put("230", "Desk");
availableProductTypesForAdd.put("232", "Monitor");

search anywhere search anywhere

However, it is not as strict as select and allows to input anything not limited by options (it's by nature kind of suggestions, that's why autocomplete is off)

But if you are fine with some tiny js, you can add some extra logic to cover this gap, here is one of the options (cleans out input if it does not match one of the options):

<script>
(function() {
    function verify() {
        var input = document.getElementById("productType");
        var o = [].map.call(input.list.options, opt => opt.value);
        var opts = o.reduce((result, v) => (result[v.toUpperCase()] = v, result), {});
        var value = input.value.toUpperCase();
        if (!opts[value]) {
            input.value = '';
            return false;
        }
    }
    document.getElementById("productType").addEventListener("focusout", verify);
    document.getElementById("form").addEventListener("focusout", verify);
})();
</script>

Basically you can slightly improve default . But only if you are fine with this search not fully working when menu is opened (because that menu is not accessible from js because it's OS specific and seems to not even be a part of html).

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>

<form:form id="form" method="post" modelAttribute="form" action="/submit2">
    <form:select path="productType" required="required" >
        <form:option value="" label="" />
        <form:options items="${availableProductTypesForAdd}" />
    </form:select>
    <label for="productType">Product Type</label>
    <input type="submit" name="action" value="Submit" />
</form:form>

<script>
(function() {
    var lastKeysForSearchNumber = 7; // tune # of keys entered to keep
    var lastKeys = [];
    var select = document.querySelector('[name=productType]');
    function search(e) {
        if (e.key.match(/^\w$/i)) {
            lastKeys.push(e.key);
            if (lastKeys.length > lastKeysForSearchNumber) {
                lastKeys.shift();
            }
            select.value = bestMatch(select.options, lastKeys.join(''));
            e.preventDefault();
        }
    }
    function bestMatch(options, input) {
        for (var i = 0; i < input.length; i++) {
            for (var j = 0; j < options.length; j++) {
                if (options[j].text.match(new RegExp(input.substring(i), 'i'))) { // contains ignore case
                    return options[j].value;
                }
            }
        }
        return '';
    }
    select.addEventListener("keydown", search);
})();
</script>

The idea here is to store last 7 (or whatever) keys and check every option if it contains as much as possible.

enter image description here

This could require some tuning and/or some adaptation for specific browsers (IE?) Here is snipped for that, try it out:

(function() {
    var lastKeysSorSearchNumber = 7;
    var lastKeys = [];
    var select = document.querySelector('[name=productType]');
    function search(e) {
        if (e.key.match(/^\w$/i)) {
            lastKeys.push(e.key);
            if (lastKeys.length > lastKeysSorSearchNumber) {
                lastKeys.shift();
            }
            select.value = bestMatch(select.options, lastKeys.join(''));
            e.preventDefault();
        }
    }
    function bestMatch(options, input) {
        for (var i = 0; i < input.length; i++) {
            for (var j = 0; j < options.length; j++) {
                if (options[j].text.match(new RegExp(input.substring(i), 'i'))) {
                    return options[j].value;
                }
            }
        }
        return '';
    }
    select.addEventListener("keydown", search);
})();
<form id="form" action="/submit2" method="post">
    <select id="productType" name="productType" required="required">
        <option value=""></option>
        <option value="310">310 - Bad</option><option value="201">201 - Chair</option><option value="2324">2324 - Monitor</option><option value="555">555 - Sofa</option><option value="191">191 - Table</option><option value="999">999 - Bookcase</option><option value="618">618 - Armchair</option><option value="230">230 - Desk</option>
    </select>
    <label for="productType">Product Type</label>
    <input type="submit" name="action" value="Submit">
</form>

于 2020-03-06T02:11:33.867 回答