web-dev-qa-db-fra.com

Utilisez la saisie semi-automatique HTML5 (datalist) avec l'approche «contient», pas seulement «commence par»

(Je ne le trouve pas, mais là encore, je ne sais pas vraiment comment le rechercher.)

Je veux utiliser <input list=xxx> et <datalist id=xxx> pour obtenir la saisie semi-automatique, MAIS je veux que le navigateur fasse correspondre toutes les options par l'approche "contient", au lieu de "commence par", ce qui semble être standard. Y a-t-il un moyen?

Si ce n'est pas simplement, existe-t-il un moyen de forcer l'affichage des suggestions que je veux afficher, et non celles auxquelles le navigateur correspond? Disons que je tape "foo" et je veux afficher les options "bar" et "baz". Puis-je les imposer à l'utilisateur? Si je remplis simplement la liste de données avec celles-ci (avec JS), le navigateur fera toujours sa vérification "commence par" et les filtrera.

Je veux un contrôle ultime sur la façon dont les options de la liste de données sont affichées. PAS sur son interface utilisateur, sa flexibilité, son accessibilité, etc., donc je ne veux pas le refaire complètement. Ne suggérez même pas un plugin jQuery.

Si je peux contrôler ultimement la validation des éléments de formulaire, pourquoi pas la saisie semi-automatique, non?

edit: Je vois maintenant que Firefox utilise l'approche "contient" ... Ce n'est même pas une norme ?? Une façon de forcer ça? Puis-je changer la façon de Firefox?

edit: J'ai fait ceci pour illustrer ce que j'aimerais: http://jsfiddle.net/rudiedirkx/r3jbfpxw/

31
Rudie

Approche 'contient'

C'est peut-être ce que vous recherchez (partie 1 de votre question).

Il va avec la limitation de "commence par" et change quand une sélection est faite.

'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>

Éditer

Une approche différente de l'affichage du contenu de la recherche, pour clarifier ce qui se passe. Cela fonctionne dans Chrome également. Inspiré par Afficher les étiquettes des listes de données mais soumettre la valeur réelle

   '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

pourtant ce fil est posté il y a environ 2 ans. mais si vous lisez ce fil, vous devrez peut-être vérifier une version plus récente de votre navigateur:

Spécifications actuelles: https://html.spec.whatwg.org/multipage/forms.html#the-list-attribute

Les agents utilisateurs sont encouragés à filtrer les suggestions représentées par l'élément source de suggestions lorsque le nombre de suggestions est important, y compris uniquement les plus pertinentes (par exemple, sur la base de l'entrée de l'utilisateur jusqu'à présent). Aucun seuil précis n'est défini, mais le plafonnement de la liste à quatre à sept valeurs est raisonnable. Si le filtrage est basé sur l'entrée de l'utilisateur, les agents utilisateurs doivent utiliser la sous-chaîne correspondant à la fois l'étiquette et la valeur des suggestions .

Et lorsque cet article a été rédigé, le comportement de Firefox (51) et Chrome (56) avait déjà été modifié pour correspondre aux spécifications.

ce qui signifie que ce que vous voulez devrait fonctionner maintenant.

3
tsh

this fiddle here a craqué ce que vous demandez Mais je ne sais pas comment le faire fonctionner sans cette dépendance car l'interface utilisateur semble un peu étrange et hors de propos lorsqu'elle est utilisée avec Bootstrap.

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

J'ai trouvé cette question parce que je voulais un comportement "commence par", et maintenant tous les navigateurs semblent implémenter "contient". J'ai donc implémenté cette fonction, qui sur Firefox (et probablement d'autres), si elle est appelée depuis le gestionnaire d'événements d'entrée (et éventuellement, depuis le gestionnaire d'événements focusin) fournit un comportement "commence par".

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