Что такое <datalist> в HTML5 и где его использовать

Всем знакомо автодополнение. Когда вы ищете что-то в интернете, поисковик предлагает подходящие варианты. Когда вы составляете email-сообщение, ваш почтовый клиент предлагает список получателей. Однако для добавления этой функции веб- разработчикам приходится использовать значительное количество JavaScript. Один из новых элементов HTML5, <datalist>, позволяет добавлять функцию автодополнения без использования JavaScript.

В этой статье я расскажу, что такое список вариантов <datalist>, в каких случаях его использование уместно, какие у него есть ограничения и что делать с браузерами, которые его не поддерживают. Давайте начнём.

Создание списка вариантов

Чтобы продемонстрировать работу списка вариантов, для начала добавим в код обычное поле для ввода текста:

<label for="favorite_team">Любимая команда:</label>
<input type="text" name="team" id="favorite_team">

В это поле пользователь должен ввести название свей любимой спортивной команды. По умолчанию пользователь не получает никакой дополнительной помощи при заполнении этого поля. С помощью списка вариантов <datalist> вы можете предложить пользователю список вариантов, из которых он может выбрать подходящий. Чтобы это воплотить, пропишите <datalist> с каждым вариантом в отдельном элементе <option>:

<datalist>
  <option>Бавария</option>
  <option>Бенфика</option>
  <option>Боруссия</option>
  <option>Брюгге</option>
  <!-- и т.д. -->
</datalist>

Чтобы привязать список вариантов к элементу input, добавьте атрибут list для input и атрибут id с таким же значением для <datalist>. Вот пример:

<label for="favorite_team">Любимая команда:</label>
<input type="text" name="team" id="favorite_team" list="team_list">
<datalist id="team_list">
  <option>Бавария</option>
  <option>Бенфика</option>
  <option>Боруссия</option>
  <option>Брюгге</option>
  <!-- и т.д. -->
</datalist>

Обратите внимание на то, что атрибут list элемента input и атрибут id элемента <datalist> содержат одинаковое значение team_list. Это их связывает.

Вот и всё. Чтобы заставить список вариантов работать, JavaScript не нужен. На рисунке 1 показано, что увидит пользователь когда введёт букву Б.

Вид списка вариантов

Рисунок 1. Вид списка вариантов (слева сверху: Internet Explorer 10; справа сверху: Firefox 18; слева снизу: Chrome 24; справа снизу: Opera 12)

Примечание: в Internet Explorer 10 и Opera пользователю не нужно вводить букву, список вариантов отображается сразу, в отличие от Firefox и Chrome.

Элементы <option> могут также принимать атрибут со значением. Это пригодится когда пользователь может не знать какое сокращение используется для нужного варианта. Взгляните на код для поля ввода штата в США:

<label for="state">State:</label>
<input type="text" name="state" id="state" list="state_list">
<datalist id="state_list">
  <option value="AL">Алабама</option>
  <option value="AK">Аляска</option>
  <option value="AZ">Аризона</option>
  <option value="AR">Арканзас</option>
  <!-- и т.д. -->
</datalist>

Пользователь увидит список полных названий штатов, но после того, как он выберет нужный, в поле будет указано сокращение названия, а не полный вариант. Пример этого можно увидеть на рисунке 2.

Выбор из списка варианта <option> с соответствующим значением

Рисунок 2. Выбор из списка варианта <option> с соответствующим значением (Firefox 18)

Атрибут autocomplete

Списки на рисунке 1 и 2 должно быть выглядят знакомыми, так как в браузерах функция автодополнения введена уже давно. Во всех браузерах есть механизм запоминания введенного пользователем текста, который затем используется для автодополнения.

Разработчики могут с помощью атрибута autocomplete контролировать то, как браузер автоматически заполняет пользовательские данные. В следующем примере представлены его возможные значения:

<form>
  <!--
  Если атрибут autocomplete не указан, он наследует значение родительского 
  элемента в форме. Если и он не указан, значение атрибута autocomplete в 
  форме по умолчанию — on. Так как ни для input, ни для form этот 
  атрибут не указан, используется состояние «on».
  -->
  <input type="text" name="firstName">
  <!--
  Состояние «on» значит, что браузер может запоминать значения для дальнейшего 
  использования, а также предлагать пользователю сохранённые значения.
  -->
  <input type="text" name="address" autocomplete="on">
  <!--
  Состояние «off» приказывает браузеру не сохранять введённые значения для этого 
  input и не предлагать ранее сохранённые значения. Его стоит использовать, 
  когда данные несут секретный характер или значение больше никогда не будет использоваться. 
  -->
  <input type="text" name="secret" autocomplete="off">
</form>

Так в чём же разница между атрибутом autocomplete и списком вариантов <datalist>? Атрибут autocomplete указывает браузеру, нужно ли предлагать пользователю варианты заполнения полей исходя из данных, введенных им ранее, и нужно ли сохранять введённое значение для дальнейшего заполнения. Список вариантов <datalist> — это список вариантов для заполнения поля, составленный разработчиком, который отображается всегда в том же виде независимо от данных, которые пользователь вводил ранее.

Помните однако, что атрибут autocomplete со значением off не даст списку вариантов отображаться в Opera. Вот пример:

<!--
Этот список вариантов не будет отображаться в Opera, потому что для autocomplete задано 
значение off. Он будет отображаться во всех других браузерах, поддерживающих datalist.
-->
<input type="text" list="pets" autocomplete="off">
<datalist id="pets">
  <option>Кот</option>
  <option>Собака</option>
  <option>Хомяк</option>
  <option>Черепаха</option>
</datalist>

Другие типы полей ввода

Хотя автодополнение обычно ассоциируется с вводом текста, списки вариантов можно смело использовать и для некоторых новых типов input, добавленных в HTML5. Например для range, который позволяет создавать форму с ползунком. Соединив её со списком вариантов datalist можно добавить разделительные точки в форме.

Например, следующий input предлагает пользователю сделать пожертвование в размере от $5 до $200.

<label for="donation">Размер пожертвования (USD):</label>
<input type="range" name="donation" id="donation" list="donation_list"
  step="5" min="5" max="200">
<datalist id="donation_list">
  <option>25</option>
  <option>50</option>
  <option>100</option>
  <option>200</option>
</datalist>

На рисунках 3 и 4 показано, как выглядит форма с ползунком в Chrome 24 и Internet Explorer 10 соответственно.

Форма-слайдер со списком вариантов в Chrome 24

Рисунок 3. форма с ползунком со списком вариантов (в Chrome 24)

Форма-слайдер со списком вариантов в Internet Explorer 10

Рисунок 4. форма с ползунком со списком вариантов (в Internet Explorer 10)

Как видите, в обоих браузерах для каждого option нашего списка datalist отображена отчётливая отметка. Кроме того, в Chrome чувствуется прилипание ползунка к указанным разработчиком значениям по мере того как пользователь его передвигает.

К сожалению, в данный момент формы с ползунком, созданные с помощью input с типом range поддерживаются только в Internet Explorer и Chrome. На рисунке 5 показана поддержка использования <datalist> для распространённых типов input в современных браузерах.

Поддержка <datalist> для типов input в формах

Рисунок 5. Поддержка <datalist> для типов input в формах

Когда использовать списки вариантов

Так как у списков <datalist> нет встроенного механизма, требующего, чтобы пользователь выбрал один из предложенных вариантов, <datalist> хорошо подходят для полей ввода которые принимают любое значение. Как в примере с любимой командой, приведенном выше. Хоть список с вариантами названий команд и был предложен, пользователь мог ввести любое значение.

С другой стороны, пример с американскими штатами не подходит так как количество валидных значений, которые пользователь может ввести, строго ограничено. В таком случае лучше использовать элемент select, тогда выбор из списка будет обязательным.

Исключением будут случаи, когда количество валидных значений огромно. Например, взгляните на распространённый выпадающий список для выбора страны, изображённый на рисунке 6.

Стандартный выпадающий список выбора страны

Рисунок 6. Стандартный выпадающий список выбора страны

Этот список неудобен для пользователей, так как им необходимо пролистать сотни вариантов, чтобы найти нужный. В этом случае функция автодополнения удобнее, так как пользователь получает возможность быстро отфильтровать список, введя одну-две буквы.

Вот как можно создать форму выбора страны с помощью <datalist>:

<label for="country">Страна:</label>
<input type="text" id="country" list="country_list">
<datalist id="country_list">
  <option value="AF">Афганистан</option>
  <option value="AX">Аландские острова</option>
  <option value="AL">Албания</option>
  <option value="DZ">Алжир</option>
  <!-- и остальные -->
</datalist>

На рисунке 7 показано, что пользователь увидит введя букву У.

Использование списка вариантов <datalist> для создания формы ввода страны

Рисунок 7. Использование списка вариантов <datalist> для создания формы ввода страны

Принудительный ввод значения

Хотя изначально <datalist> и не позволяет сделать выбор варианта из предложенных обязательным, можно легко добавить проверку, которая его таким сделает. В следующем коде валидация добавлена с помощью API проверки ограничений валидации:

// Поиск всех input-элементов в DOM, которые привязаны к datalist с помощью атрибута list.
var inputs = document.querySelectorAll('input[list]');
for (var i = 0; i < inputs.length; i++) {
  // Когда значение input изменяется…
  inputs[i].addEventListener('change', function() {
    var optionFound = false,
      datalist = this.list;
    // Определение, существует ли option с текущим значением input.
    for (var j = 0; j < datalist.options.length; j++) {
        if (this.value == datalist.options[j].value) {
            optionFound = true;
            break;
        }
    }
    // используйте функцию setCustomValidity API проверки ограничений валидации
    // чтобы обеспечить ответ пользователю, если нужное значение в datalist отсутствует
    if (optionFound) {
      this.setCustomValidity('');
    } else {
      this.setCustomValidity('Please select a valid value.');
    }
  });
}

API проверки ограничений валидации встроено во все браузеры, поддерживающие списки вариантов, так что если datalist работает, валидация также должна работать. Таким образом, когда пользователь попробует отправить на сервер форму с input, к которому подключён datalist (не выбрав ни один из предложенных вариантов), он получит ошибку, изображённую на рисунке 8.

Ошибка API проверки ограничений валидации

Рисунок 8. Ошибка API проверки ограничений валидации (Internet Explorer 10)

Важно однако отметить, что при использовании API проверки ограничений валидации валидация на стороне сервера также необходима. Пользователи, использующие старые браузеры, не будут иметь доступ к API проверки ограничений валидации и злоумышленники получают возможность с лёгкостью ломать скрипты на стороне клиента.

Хотя этот подход и работает в современных браузерах, он делает пользовательский интерфейс непригодным к эксплуатации в устаревших браузерах. Пользователь получает указание выбрать валидное значение из предложенных вариантов, которых не видит, если его браузер не поддерживает datalist. Так что если вы планируете использовать обсуждаемый подход, необходимо продумать интерфейс, который будет работать во всех браузерах. Этого можно достичь проверяя поддерживается ли datalist браузером и при необходимости, используя подходящий полифил.

Поддержка datalist браузерами

На момент написания статьи списки вариантов для текстовых полей поддерживаются в Internet Explorer 10, Firefox 4+, Chrome 20+, и Opera, что, к сожалению, не охватывает значительную аудиторию пользователей.

В отличие от многих других нововведений HTML5, в большинстве случаев для браузеров, которые не поддерживают datalist, особые действия не требуются. По умолчанию список вариантов — это всего лишь предложения от разработчика; следовательно, пользователю, чей браузер не поддерживает datalist, просто придётся заполнить текстовое поле без предложенных вариантов.

Однако, для комфорта пользователей, пользующихся браузерами, которые не поддерживают datalist, можно использовать некоторые резервные решения.

Резервная замена для HTML-контента

Одно из решений, популяризованное Джереми Китом (Jeremy Keith), состоит в использовании того факта, что в браузерах, не поддерживающих элемент datalist, всё равно отображаются дочерние элементы. Дальше показано, как можно изменить пример со списком стран, чтобы добавить резервное решение, используя <select>.

<label for="country">Страна:</label>
<datalist id="country_list">
  <select name="country">
    <option value="AF">Афганистан</option>
    <option value="AX">Аландские острова</option>
    <option value="AL">Албания</option>
    <option value="DZ">Алжир</option>
    <option value="AS">Американское Самоа</option>
    <!-- другие -->
  </select>
  Если подходящий вариант отсутствует, пожалуйста, укажите его:
</datalist>
<input type="text" name=”country” id=”country” list="country_list">

Вид интерфейса в браузере не изменится, но теперь пользователи, браузеры которых не поддерживают datalist, увидят элемент, в котором можно выбрать название страны из предложенных вариантов и текстовое поле, в которое можно ввести любое значение. Это можно увидеть на рисунке 9.

Резервное решение для списков вариантов с использованием <select>

Рисунок 9. Резервное решение для списков вариантов с использованием <select> (Internet Explorer 9)

Использование полифилов

Единственный недостаток <select> — это то, что он не предлагает функцию автодополнения как datalist. Если для вашего списка вариантов необходимо автодополнение, воспользуйтесь вторым резервным решением с использованием полифила.

Сначала нужно определить, поддерживает ли браузер datalist. Такую проверку предлагает популярная библиотека для определения возможностей браузера Modernizr, как можно видеть дальше:

if (Modernizr.input.list) {
    // Браузер поддерживает атрибут list и списки datalist
  } else {
    // Браузер не поддерживает ни атрибут list, ни списки datalist
  }
}

Используя этот подход, можно с помощью полифила обеспечить работу списка вариантов в браузерах, которые не поддерживают datalist. Для списка вариантов есть несколько полифилов, но я отдаю предпочтение виджету автодополнения jQuery. В следующем коде продемонстрирована реализация полифила:

var datalist,
  listAttribute,
  options = [];
// Если браузер не поддерживает атрибут list…
if (!Modernizr.input.list) {
  // Для каждого текстового input с атрибутом list…
  $('input[type="text"][list]').each(function() {
    // Поиск id элемента списка вариантов для input
    // Используя его, ищем datalist, который соответствует input
    listAttribute = $(this).attr('list');
    datalist = $('#' + listAttribute);
    // Если для input есть соответствующий элемент datalist…
    if (datalist.length > 0) {
      options = [];
      // Создаём список вариантов для виджета автодополнения.
      datalist.find('option').each(function() {
        options.push({ label: this.innerHTML, value: this.value });
      });
      // Трансформируем input в jQuery-виджет автодополнения.
      $(this).autocomplete({ source: options });
    }
  });
  // Удаление всех списков вариантов из DOM, чтобы они не отображались.
  $('datalist').remove();
}

На рисунке 10 показано, как выглядит пример со списком стран в Safari, когда используется полифил автодополнения на основе jQuery.

Список вариантов названий стран с использованием виджета автозавершения на основе jQuery в качестве полифила

Рисунок 10. Список вариантов названий стран с использованием виджета автодополнения на основе jQuery в качестве полифила (Safari)

Вы, возможно, заметили, что по умолчанию jQuery-виджет автодополнения предлагает варианты, содержащие введённые символы в любом расположении, в то время как datalist предлагает только те варианты, в которых первые символы совпадают с введёнными пользователем. В отличие от datalist, в виджете автодополнения можно настроить принцип выдачи вариантов на своё усмотрение.

Следующий пример демонстрирует, как можно настроить функцию автодополнения, которая предлагает варианты, в которых первые символы совпадают с введёнными:

<input type="text" id="autocomplete">
<script>
var options = ['Барсук', 'Летучая мышь', 'Кот', 'Гепард', 'Крокодил', 'Олень', 'Осёл',
  'Слон', 'Рыба', 'Лягушка', 'Жираф', 'Горилла'];
$('#autocomplete').autocomplete({
  // в req будет помещён объект со свойством, которое содержит значение, 
  // помещённое в текстовое поле input. responseFn должно быть вызвано с вариантами, 
  // которые должны быть показаны пользователю.
  source: function (req, responseFn) {
    // Кодировка имеющих значение для RegExp символов в введённом слове,  
    // например ".", или "^"
    var term = $.ui.autocomplete.escapeRegex(req.term),
      // Символ '^' в RegExp используется для сопоставления с началом строки
      // 'i' говорит RegExp, что сопоставление должно проводиться с учётом регистра.
      matcher = new RegExp('^' + term, 'i'),
      // Закольцовывает варианты и выбирает только те, что совпадают с RegExp.
      matches = $.grep(options, function (item) {
        return matcher.test(item);
      });
    // Возвращение совпавших вариантов.
    responseFn(matches);
  }
});
</script>

Старые версии Internet Explorer

Чтобы любой полифил для элемента datalist заработал в старых версиях Internet Explorer, нужны ещё две вещи.

Элементы option

Первое: Internet Explorer 9 и старше требует чтобы все элементы option были непосредственными дочерними элементами для select. Если это условие не соблюдено, браузер их не понимает, и они не будут распознаны полифилом.

К счастью, это легко обойти. Используя условные комментарии, можно обернуть элементы option в элемент select только для этих версий Internet Explorer. Как в этом примере:

<datalist>
  <!--[if IE]><select><!--<![endif]-->
  <option>Апельсин</option>
  <option>Банан</option>
  <option>Вишня</option>
  <!- другие -->
  <!--[if IE]><select><!--<![endif]-->
</datalist>

Теперь Internet Explorer 9 и старше будет понимать элементы option так, как надо. На Internet Explorer 10 это не повлияет, так как условные комментарии удалены из его анализатора. Все другие браузеры также игнорируют комментарии.

HTML5 Shiv

В Internet Explorer 8 и старше неизвестные элементы не могут содержать дочерние элементы. Таким образом, даже если элементы option списка вариантов datalist помещены в элемент select, браузер всё равно их не распознает.

К счастью, для этой проблемы также есть простое решение. Популярная библиотека HTML5 Shiv позволяет элементам, неизвестным для Internet Explorer 6–8, содержать дочерние элементы. Эта библиотека по умолчанию встроена в Modernizr; Убедитесь, что напротив html5shiv стоит отметка, когда вы его скачиваете.

Ограничения списка вариантов

Хотя datalist идеально подходит для добавления списка вариантов для текстовых полей, ему не хватает настроек, необходимых для многих современных веб-приложений. Например, для datalist нельзя сделать следующее:

Если вам нужно что-либо из перечисленного, вам придётся поискать более сложное решение для автодополнения. Виджет автодополнения jQuery даёт все перечисленные возможности и больше. Он также поддерживается всеми браузерами вплоть до Internet Explorer 7 без необходимости в полифиле. Больше информации об этом виджете автодополнения можно найти в демо и документации по API (Виджет автодополнения работает только для текстовых полей ввода, его нельзя применить для более узкоспециализированных типов input таких как range и color)

Подводя итог

datalist даёт нам быстрое и естественное решение для вывода предложений по завершению текста, который пользователь начал вводить. Поскольку это всего лишь предложения, во многих случаях не обязательно предоставлять резервное решение для браузеров, которые не поддерживают datalist; пользователи таких браузеров просто не увидят эти предложения.

В тех случаях, когда вы хотите отображать список вариантов для всех пользователей, можно использовать проверку на поддержку браузером и подключать резервное решение в виде полифила.

Список вариантов datalist является отличным инструментом для вывода вариантов автодополнения, однако его функциональность ограничена. Если вам нужно более сложное решение, вам следует рассмотреть возможность использования виджета автодополнения из библиотеки jQuery.