web-dev-qa-db-ja.com

「で始まる」だけでなく、「次を含む」アプローチでHTML5(データリスト)オートコンプリートを使用する

(私はそれを見つけることができませんが、それでもそれを検索する方法が本当にわかりません。)

オートコンプリートを取得するために<input list=xxx><datalist id=xxx>を使用したいのですが、ブラウザは、標準のように「次で始まる」ではなく、「次を含む」アプローチですべてのオプションに一致させたいです。方法はありますか?

単純ではない場合、ブラウザが一致した提案ではなく、表示したい提案を強制表示する方法はありますか? 「foo」と入力していて、オプション「bar」と「baz」を表示したいとします。それらをユーザーに強制できますか? (JSを使用して)データリストにデータを入力するだけの場合でも、ブラウザーは「で始まる」チェックを行い、フィルターで除外します。

データリストオプションの表示方法を最終的に制御したい。 UI、柔軟性、アクセシビリティなどを超えていないので、完全に作り直したくありません。 jQueryプラグインも提案しないでください。

フォーム要素の検証を最終的に制御できる場合は、オートコンプリートを使用してみませんか?

edit:Firefoxが「contains」アプローチを使用していることがわかりました...それは標準ではないのですか?これを強制する方法はありますか? Firefoxの方法を変更できますか?

edit:これは、私が何をしたいかを説明するために作成しました: http://jsfiddle.net/rudiedirkx/r3jbfpxw/

31
Rudie

'contains'アプローチ

多分これはあなたが探しているものです(あなたの質問のパート1)。

「次で始まる」の制限に伴い、選択が行われたときに変更されます。

'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>
14
Nina Scholz

まだこのスレッドは約2年前に投稿されています。ただし、このスレッドを読んでいる場合は、ブラウザの新しいバージョンを確認する必要があるかもしれません。

現在の仕様: https://html.spec.whatwg.org/multipage/forms.html#the-list-attribute

ユーザーエージェントは、最も関連性の高い候補のみを含めて(たとえば、これまでのユーザーの入力に基づいて)、候補の数が多い場合に、候補ソース要素によって表される候補をフィルタリングすることをお勧めします。正確なしきい値は定義されていませんが、リストを4〜7個の値に制限することは妥当です。ユーザーの入力に基づいてフィルタリングする場合、ユーザーエージェントは両方に対して部分文字列の一致を使用する必要があります提案のラベルと値

そして、この投稿が書かれたとき、Firefox(51)とChrome(56)の動作は仕様に一致するようにすでに変更されていました。

これは、opが望むものが今すぐ機能することを意味します。

3
tsh

this fiddle here はあなたが求めているものをクラックしましたが、Bootstrapと一緒に使用するとUIが少し奇妙で場違いに見えるので、この依存関係なしでどのように動作させるかはわかりません。

 elem.autocomplete({
    source: list.children().map(function() {
        return $(this).text();
    }).get()
0
Ananda

「次で始まる」動作が必要だったのでこの質問を見つけましたが、すべてのブラウザが「次を含む」を実装しているようです。したがって、私はこの関数を実装しました。これは、Firefox(およびおそらく他の関数)で、入力イベントハンドラー(およびオプションでfocusinイベントハンドラー)から呼び出された場合、「で始まる」動作を提供します。

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;
  }
}
0
Victoria