Статьи

Поиск решения для выбора даты для Bootstrap

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

Вы можете увидеть последние данные о поддержке браузером для ввода даты на я могу использовать .

В дополнение к основному полю date-input существуют и другие типы полей, связанные со временем:

  • Поля datetime позволяют вводить даты и время в формате UTC .
  • datetime-local представляет дату и время без часового пояса
  • time
  • month представляет месяц и год
  • week представляет номер недели и год

Я создал тестовую страницу для всех перечисленных выше типов ввода даты с помощью Modernizr , и я протестировал ее во многих современных мобильных и настольных браузерах, использующих Browserstack . Например, это страница результатов при просмотре в Chrome (красные поля не поддерживаются):

Дата теста

Вы можете проверить это прямо в демоверсии здесь:

Этот тест приносит нам следующую информацию:

  • Ни один браузер не поддерживает datetime ввода datetime .
  • Браузеры, которые поддерживают datetime-local также поддерживают time и month , в то время как тип week не так хорошо поддерживается.
  • Все браузеры, кажется, игнорируют атрибут lang . То есть виджеты ввода браузера всегда используют системные настройки для формата даты и времени, поэтому невозможно отобразить виджет ввода с языком, отличным от родного.

Теперь мы знаем, что мы должны обрабатывать не только date или date / время, но и формат даты (если нам нужно, чтобы она отличалась от системных настроек пользователя) и проблемы UTC / local. Кроме того, мы должны принять во внимание разницу между отображаемым форматом и возвращаемым при отправке формы.

Выбор резервного сценария

Существует множество JavaScript-решений для улучшения поддержки ввода данных во всех браузерах, и, конечно, у каждого из них есть свои плюсы и минусы. Мой самый последний проект был проектом на основе Bootstrap, поэтому я сначала искал сценарии, основанные на этой платформе. Я нашел несколько интересных решений:

Но эти проекты (и другие, которые я не перечислил) имеют две основные проблемы:

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

Поскольку мне нужно отобразить дату в итальянском формате (она будет одинаковой для большинства локализаций), это создает дополнительную проблему: браузеры, которые поддерживают ввод даты, возвращают значения даты в стандартном формате ISO 8601 / RFC 3339 (гггг-мм-дд ) в то время как каждый скрипт, который я проверял, использует один и тот же формат для отображения и сохранения.

Таким образом, у меня были значения, сохраненные из Chrome, Opera и большинства мобильных браузеров в формате yyyy-mm-dd , и значения из браузеров, которые используют резервный сценарий как dd/mm/yyyy — большой беспорядок!

А как насчет jQuery UI?

Прежде чем перейти к пользовательскому решению (я намеренно отказался от возможности управления двумя форматами в серверной части), я начал рассматривать jQuery UI Datepicker как возможное решение.

Я изначально не рассматривал jQuery UI, потому что он поставляется с некоторыми предопределенными темами, которые не интегрированы с Bootstrap (но это не было большой проблемой). С другой стороны, DatePicker пользовательского интерфейса jQuery — это хорошо протестированная и поддерживаемая библиотека, и ее опция altField представляет именно то, что я искал. Кроме того, сценарии Bootstrap требуют jQuery, как и пользовательский интерфейс jQuery.

Пользовательский интерфейс jQuery можно загрузить со страницы проекта , но, поскольку я собираюсь смешивать файлы пользовательского интерфейса jQuery с файлами Bootstrap, я думаю, что наилучшим способом является его загрузка (или клонирование и т. д.) из репозитория GitHub .

Использование файлов разработки позволит мне проще комбинировать CSS-файлы с Bootstrap. Необходимые файлы (из основной папки пользовательского интерфейса jQuery):

  • JS:
    • ui/core.js
    • ui/datepicker.js
    • файл локализации из папки ui/i18n
  • CSS:
    • themes/base/core.css
    • themes/base/datepicker.css
    • themes/base/theme.css .

Условная загрузка

Перед загрузкой скрипта обратного вызова нам необходимо проверить поддержку браузера. Простой способ сделать это — использовать функцию обнаружения с Modernizr, как и в моем предыдущем тесте, используя загрузчик ресурсов для загрузки резервных сценариев только при необходимости:

 Modernizr.load({ test: Modernizr.inputtypes.date, nope: [ 'jquery-ui-master/ui/core.js', 'jquery-ui-master/ui/datepicker.js', 'jquery-ui-master/ui/i18n/datepicker-it.js', 'jquery-ui-master/themes/base/core.css', 'jquery-ui-master/themes/base/datepicker.css', 'jquery-ui-master/themes/base/theme.css' ], callback: function () { // do something if test fails } }); 

Сначала мы проверяем поддержку inputtypes.date , при необходимости загружая файлы JavaScript и CSS для inputtypes.date .

Я специально держал все файлы отдельно для ясности, но это хорошая идея объединить их в процессе сборки для производства.

Примечание: имейте в виду, что yepnope.js (компонент загрузки Modernizr) устарел , и будущие версии Modernizr не будут включать его.

После nope скриптов nope и css мы должны сказать нашим браузерам (тем, которые не Modernizr.inputtypes.date тест Modernizr.inputtypes.date ) сделать что-то, поэтому мы должны добавить функцию обратного вызова:

 callback:function (url) { if (url === 'jquery-ui-master/ui/i18n/datepicker-it.js') { var idx = 0; $('input[type="date"]').each( function() { var _this = $(this), prefix = 'alt' + String(idx++) + '_', _val = _this.val(); _this .attr('placeholder', 'gg/mm/aaaa') .attr('autocomplete', 'off') .prop('readonly', true) .after('<input type="hidden" id="' + prefix + _this.attr('id') + '" name="' + _this.attr('name') + '" value="' + _val + '">') .removeAttr('name') .val('') .datepicker({ altField: '#'+ prefix + _this.attr('id'), dateFormat: 'dd/mm/yy', altFormat: 'yy-mm-dd' }); // managing existing date values if (_val) { _this.datepicker('setDate', $.datepicker.parseDate('yy-mm-dd', _val) ); } }); } } 

Как работает код:

  • Используя аргумент url , мы сначала проверяем, все ли скрипты были загружены, проверяя последний из них.
  • Для каждого поля input[type="date"] создается скрытое поле с unique-id .
  • Атрибут имени поля даты удаляется и добавляется в скрытое поле.
  • Виджет проинструктирован использовать скрытое поле как altField для хранения даты в формате ISO, независимо от используемой нами локализации.
  • Дата и скрытое поле форматов даты установлены.
  • Любое существующее значение ISO удаляется и переназначается в формате DatePicker.
  • Добавляется заполнитель с шаблоном вставляемой даты (в соответствии с нашим локальным форматом даты).
  • autocomplete отключено, поскольку в некоторых случаях это может привести к путанице при использовании виджета.
  • атрибут readonly добавляется в поля. Это, конечно, не лучший вариант с точки зрения UX, но он решает большую проблему: если содержимое поля даты стирается вручную (без запуска виджета), поле alt не обновляется, и только readonly предотвращает такое поведение.

Вы можете увидеть результат, показанный на скриншоте ниже (я изменил altField со hidden на text в серой altField чтобы вы могли видеть изменения).

Вот страница результатов просмотра с Firefox …

Основная тема выбора даты в Firefox

… и с Chrome и IOS (которые используют свой родной виджет):

Выбор даты в Chrome и IOS

А вот демо, чтобы попробовать это самостоятельно:

Обратите внимание, что в этой демонстрации я загрузил скрипты из CDN, поэтому есть некоторые отличия от кода, показанного ранее.

Чтобы завершить обратный вызов, я добавил некоторый код для управления любыми min или max атрибутами (которые вы можете увидеть на вкладке «JS» демонстрации CodePen).

Интеграция с начальной загрузкой

Этот предыдущий шаг не решает проблему интеграции Bootstrap. Вы можете увидеть, как во всех примерах показана основная тема пользовательского интерфейса jQuery, загруженная через файл theme.css . Если вы удалите его, виджет появится без какого-либо форматирования:

Datepicker нет темы

Поэтому мы должны заменить этот файл интегрированным с Bootstrap.

Я думаю, что есть два хороших способа сделать это:

  • Создайте новый theme.less (или .scss ). Это лучший способ, если вам нужно использовать больше виджетов jQuery UI, так как theme.css совместно используется всеми ними (в этом случае посмотрите документацию по CSS jQuery UI CSS Framework )
  • Создайте тему только для DatePicker, игнорируя другие виджеты. Вы должны учитывать это, если вам нужен только виджет Datepicker. Даже если этот способ более эмпирический , он действительно быстрее, чем другой (и это решение мы рассмотрим сейчас).

Это наши цели:

  • Напишите новый файл datepicker.less используя переменные проекта Bootstrap для цвета, размеров и т. Д. Это свяжет ваш Datepicker с настройкой фреймворка, что позволит нам создать более скоординированный дизайн.
  • Используйте значок шрифта (в данном случае это глифы по умолчанию в Bootstrap) вместо оригинальных изображений jQuery UI.

Чтобы создать файл, сначала нужно импортировать некоторые настройки из Bootstrap. Загрузив его (и, конечно, пользовательский интерфейс jQuery), создайте новый файл less и импортируйте variables и glyphicons :

 @import (reference) 'path/to/bootstrap/variables.less'; @import (reference) 'path/to/bootstrap/glyphicons.less'; 

Опорная reference позволяет нам «импортировать внешние файлы без добавления импортированных стилей в скомпилированный вывод, если на них нет ссылок» (см. Меньше параметров импорта ).

Затем вы должны построить файл с помощью переменных Bootstrap, как в примере ниже:

 .ui-datepicker .ui-datepicker-header { background-color: @panel-default-heading-bg; border-radius: @border-radius-base; font-family: @font-family-sans-serif; color: @panel-default-text; font-weight: bold; } 

Точно так же замена изображений значков на глифы очень проста:

 .ui-datepicker .ui-icon { .glyphicon(); visibility: hidden; text-indent: 0; &:before { visibility: visible; } } .ui-datepicker .ui-icon.ui-icon-circle-triangle-w { .glyphicon-chevron-left(); } .ui-datepicker .ui-icon.ui-icon-circle-triangle-e { .glyphicon-chevron-right(); } 

Вы можете найти полный файл less в моем репозитории GitHub для этого решения .

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

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

Вот скриншот результата:

Выбор даты начальной загрузки

О времени

К сожалению, jQuery UI Datepicker не управляет полями datetime . Но я нашел очень простое решение в дополнении Timepicker Трента Ричардсона . Он ведет себя так же, как стандартный виджет datepicker.less и использует наш файл datepicker.less без дополнительной работы.

Что дальше?

Есть несколько вещей, которые необходимо учитывать при реализации кросс-браузерного поля ввода даты / времени, и здесь невозможно охватить все вопросы. Постепенное увеличение поддержки браузерами типов ввода в конечном итоге сделает обсуждаемые здесь уловки (я надеюсь) устаревшими, и это станет намного проще.

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