使用 HTML5(数据列表)自动完成 'contains' 方法,而不仅仅是 'starts with'

Use HTML5 (datalist) autocomplete with 'contains' approach, not just 'starts with'

(我找不到,不过又不知道怎么找。)

我想使用 <input list=xxx><datalist id=xxx> 来获得自动完成功能,但我希望浏览器通过 'contains' 方法匹配所有选项,而不是 'starts with',这似乎是标准的。有办法吗?

如果不是简单的话,有没有办法强制显示我想显示的建议,而不是浏览器匹配的建议?假设我正在输入 "foo",我想显示选项 "bar" 和 "baz"。我可以将这些强加给用户吗?如果我只是用那些(用 JS)填充数据列表,浏览器仍然会进行 'starts with' 检查,并将它们过滤掉。

我想最终控制数据列表选项的显示方式。不是因为它的 UI、灵活性、可访问性等,所以我不想完全重制它。甚至不要建议 jQuery 插件。

如果我可以最终控制表单元素验证,为什么不自动完成,对吗?

编辑: 我现在看到 Firefox 确实使用 'contains' 方法...这甚至不是标准?有什么办法可以强制这个吗?我可以改变 Firefox 的方式吗?

编辑:我做这个是为了说明我想要什么:http://jsfiddle.net/rudiedirkx/r3jbfpxw/

'contains'接近

也许这就是您要查找的内容(问题的第 1 部分)。

受"starts with"的限制,选择时会发生变化。

'use strict';
function updateList(that) {
    if (!that) {
        return;
    }
    var lastValue = that.lastValue,
        value = that.value,
        array = [],
        pos = value.indexOf('|'),
        start = that.selectionStart,
        end = that.selectionEnd,
        options;

    if (that.options) {
        options = that.options;
    } else {
        options = Object.keys(that.list.options).map(function (option) {
            return that.list.options[option].value;
        });
        that.options = options;
    }

    if (lastValue !== value) {
        that.list.innerHTML = options.filter(function (a) {
            return ~a.toLowerCase().indexOf(value.toLowerCase());
        }).map(function (a) {
            return '<option value="' + value + '|' + a + '">' + a + '</option>';
        }).join();
        updateInput(that);
        that.lastValue = value;
    }
}

function updateInput(that) {
    if (!that) {
        return;
    }
    var value = that.value,
        pos = value.indexOf('|'),
        start = that.selectionStart,
        end = that.selectionEnd;

    if (~pos) {
        value = value.slice(pos + 1);
    }
    that.value = value;
    that.setSelectionRange(start, end);
}

document.getElementsByTagName('input').browser.addEventListener('keyup', function (e) {
    updateList(this);
});
document.getElementsByTagName('input').browser.addEventListener('input', function (e) {
    updateInput(this);
});
<input list="browsers" name="browser" id="browser" onkeyup="updateList();" oninput="updateInput();">
<datalist id="browsers">
    <option value="Internet Explorer">
    <option value="Firefox">
    <option value="Chrome">
    <option value="Opera">
    <option value="Safari">
</datalist>

编辑

一种显示搜索内容的不同方法,以明确会发生什么。这也适用于 Chrome。灵感来自

   'use strict';
var datalist = {
        r: ['ralph', 'ronny', 'rudie'],
        ru: ['rudie', 'rutte', 'rudiedirkx'],
        rud: ['rudie', 'rudiedirkx'],
        rudi: ['rudie'],
        rudo: ['rudolf'],
        foo: [
            { value: 42, text: 'The answer' },
            { value: 1337, text: 'Elite' },
            { value: 69, text: 'Dirty' },
            { value: 3.14, text: 'Pi' }
        ]
    },
    SEPARATOR = ' > ';

function updateList(that) {
    var lastValue = that.lastValue,
        value = that.value,
        array,
        key,
        pos = value.indexOf('|'),
        start = that.selectionStart,
        end = that.selectionEnd;

    if (lastValue !== value) {
        if (value !== '') {
            if (value in datalist) {
                key = value;
            } else {
                Object.keys(datalist).some(function (a) {
                    return ~a.toLowerCase().indexOf(value.toLowerCase()) && (key = a);
                });
            }
        }
        that.list.innerHTML = key ? datalist[key].map(function (a) {
            return '<option data-value="' + (a.value || a) + '">' + value + (value === key ? '' : SEPARATOR + key) + SEPARATOR + (a.text || a) + '</option>';
        }).join() : '';
        updateInput(that);
        that.lastValue = value;
    }
}

function updateInput(that) {
    var value = that.value,
        pos = value.lastIndexOf(SEPARATOR),
        start = that.selectionStart,
        end = that.selectionEnd;

    if (~pos) {
        value = value.slice(pos + SEPARATOR.length);
    }
    Object.keys(that.list.options).some(function (option) {
        var o = that.list.options[option],
            p = o.text.lastIndexOf(SEPARATOR);
        if (o.text.slice(p + SEPARATOR.length) === value) {
            value = o.getAttribute('data-value');
            return true;
        }
    });
    that.value = value;
    that.setSelectionRange(start, end);
}

document.getElementsByTagName('input').xx.addEventListener('keyup', function (e) {
    updateList(this);
});
document.getElementsByTagName('input').xx.addEventListener('input', function (e) {
    updateInput(this);
});
<input list="xxx" name="xx" id="xx">
<datalist id="xxx" type="text"></datalist>

这个fiddle here已经破解了你想要的 但是我不确定如何在没有这种依赖性的情况下使其工作,因为 UI 与 Bootstrap.

一起使用时看起来有点奇怪且不合适
 elem.autocomplete({
    source: list.children().map(function() {
        return $(this).text();
    }).get()

然而这个帖子是 post 大约 2 年前编辑的。但如果您正在阅读此主题,您可能需要检查您的浏览器的更新版本:

当前规格:https://html.spec.whatwg.org/multipage/forms.html#the-list-attribute

User agents are encouraged to filter the suggestions represented by the suggestions source element when the number of suggestions is large, including only the most relevant ones (e.g. based on the user's input so far). No precise threshold is defined, but capping the list at four to seven values is reasonable. If filtering based on the user's input, user agents should use substring matching against both the suggestions' label and value.

并且在编写此 post 时,Firefox (51) 和 Chrome (56) 的行为已经更改以符合规范。

这意味着 op 想要的现在应该可以工作了。

我发现这个问题是因为我想要 "starts with" 行为,现在所有的浏览器似乎都实现了 "contains"。所以我实现了这个函数,如果从输入事件处理程序(以及可选地,从 focusin 事件处理程序)调用,它在 Firefox(可能还有其他)上提供 "starts with" 行为。

let wrdlimit = prefix =>
{ let elm = mydatalist.firstElementChild;
  while( elm )
  { if( elm.value.startsWith( prefix ))
    { elm.removeAttribute('disabled');
    } else
    { elm.setAttribute('disabled', true );
    }
    elm = elm.nextElementSibling;
  }
}