ISBN

Структура ISBN опирается на два основных стандарта: ISBN-10 и ISBN-13. Оба варианта представляют собой числовые идентификаторы книг с фиксированным форматом и контрольной цифрой, которая вычисляется по определённому алгоритму. В ISBN-10 последняя позиция может содержать символ X, обозначающий значение 10. В ISBN-13 используется только цифровая последовательность. При работе с пользовательским вводом основная задача маски — обеспечить корректное форматирование, контроль допустимых символов и сохранение структуры с возможными разделителями (дефисами или пробелами).

ISBN-10 состоит из четырёх логических частей:

  • группа регистрации
  • издатель
  • номер издания
  • контрольная цифра (0–9 или X)

Типичный вид: 0-306-40615-X

ISBN-13 расширяет формат до EAN-13 и всегда начинается с префикса 978 или 979:

  • префикс EAN
  • регистрационная группа
  • издатель
  • номер издания
  • контрольная цифра (0–9)

Пример: 978-3-16-148410-0

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

Базовая маска для ISBN-13 в Inputmask

Библиотека Inputmask позволяет задать статическую маску с фиксированными позициями:

import Inputmask from "inputmask";

Inputmask("999-9-99-999999-9").mask("#isbn");

Здесь каждая 9 означает допустимую цифру. Дефисы задаются как литералы и автоматически фиксируются в поле ввода.

Однако такой вариант подходит только для строго фиксированного формата и не учитывает вариативность группировки, которая встречается в реальных ISBN.

Более гибкая маска ISBN-13

Для практических сценариев лучше использовать опцию определения символов и динамических правил:

Inputmask({
  mask: "999-9999-9999-9",
  placeholder: "",
  showMaskOnHover: false,
  showMaskOnFocus: true
}).mask("#isbn");

Такой вариант допускает более равномерное распределение групп, но всё ещё остаётся статическим.

Поддержка ISBN-10 с символом X

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

Inputmask({
  mask: "9-999-99999-9",
  definitions: {
    "9": {
      validator: "[0-9Xx]",
      cardinality: 1,
      casing: "upper"
    }
  }
}).mask("#isbn10");

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

Разделение ISBN-10 и ISBN-13 в одном поле

Часто требуется одно поле, поддерживающее оба формата. В Inputmask это реализуется через массив масок:

Inputmask({
  mask: [
    "999-9-999-99999-9",
    "9-999-99999-9"
  ],
  keepStatic: true
}).mask("#isbn");

Механизм keepStatic управляет выбором наиболее подходящей маски в зависимости от введённых символов. Первая маска обычно соответствует ISBN-13, вторая — ISBN-10.

Автоматическое удаление лишних символов при вставке

При вставке данных из внешних источников часто встречаются пробелы и лишние символы. Inputmask позволяет нормализовать ввод:

Inputmask({
  mask: "999-9999999999",
  autoUnmask: true,
  removeMaskOnSubmit: true,
  onBeforePaste: function (pastedValue) {
    return pastedValue.replace(/[^0-9Xx]/g, "");
  }
}).mask("#isbn");

Функция onBeforePaste очищает строку до применения маски, удаляя все символы, кроме допустимых.

Валидация завершённости ввода

Inputmask предоставляет событие oncomplete, которое срабатывает при полном заполнении маски:

Inputmask({
  mask: "999-9-999-99999-9",
  oncomplete: function () {
    console.log("ISBN заполнен полностью");
  },
  onincomplete: function () {
    console.log("ISBN введён не полностью");
  }
}).mask("#isbn");

Это позволяет отделить частичный ввод от завершённого значения без дополнительной логики проверки длины.

Контроль ввода через isValid

Для программной проверки используется метод isValid:

const im = new Inputmask("999-9-999-99999-9");
im.mask("#isbn");

const element = document.querySelector("#isbn");

function checkISBN() {
  return im.isValid(element.value);
}

Такой подход позволяет валидировать значение вне контекста DOM-событий.

Динамическое поведение маски

При необходимости можно изменять маску в зависимости от длины ввода:

Inputmask({
  mask: function () {
    return this.value.replace(/\D/g, "").length > 10
      ? "999-9-999-99999-9"
      : "9-999-99999-9";
  }
}).mask("#isbn");

Функциональная маска позволяет адаптировать поведение поля без пересоздания экземпляра Inputmask.

Работа с группировкой и визуальным форматированием

ISBN допускает разные варианты отображения дефисов, но структурно остаётся неизменным. Для управления визуальной частью используется комбинация greedy и clearIncomplete:

Inputmask({
  mask: "999-9999999999-9",
  greedy: false,
  clearIncomplete: true
}).mask("#isbn");

greedy: false уменьшает агрессивное заполнение маски, а clearIncomplete очищает незавершённые значения при потере фокуса.

Обработка пользовательских ошибок ввода

Для улучшения устойчивости формы применяется обработка события onincomplete и ручная коррекция:

Inputmask({
  mask: "999-9-999-99999-9",
  onincomplete: function () {
    this.value = this.value.trim();
  }
}).mask("#isbn");

Такой механизм полезен при интеграции с формами, где требуется минимизация некорректных отправок.

Использование regex-маски для ISBN

Inputmask поддерживает регулярные выражения, что позволяет описывать формат более гибко:

Inputmask({
  regex: "97(8|9)-[0-9]{1,5}-[0-9]{1,7}-[0-9]{1,7}-[0-9Xx]{1}"
}).mask("#isbn");

Этот вариант ближе к спецификации ISBN, но требует более строгого контроля длины сегментов на стороне логики приложения.

Нормализация значения перед сохранением

Для хранения в базе данных часто требуется убрать форматирование:

Inputmask({
  mask: "999-9-999-99999-9",
  autoUnmask: true
}).mask("#isbn");

В результате input.value возвращает только цифры без дефисов, что упрощает индексацию и поиск.

Объединение маски и внешней валидации

Маска отвечает за структуру ввода, но не заменяет проверку контрольной суммы ISBN. Обычно используется комбинированный подход:

  • Inputmask ограничивает формат
  • отдельная функция проверяет контрольную цифру
function validateISBN13(value) {
  const digits = value.replace(/\D/g, "");
  let sum = 0;

  for (let i = 0; i < 12; i++) {
    sum += (i % 2 === 0 ? 1 : 3) * parseInt(digits[i], 10);
  }

  const check = (10 - (sum % 10)) % 10;
  return check === parseInt(digits[12], 10);
}

Такой подход разделяет ответственность: Inputmask управляет вводом, алгоритм — математической корректностью.

Синхронизация маски с состоянием формы

При работе с реактивными интерфейсами важно учитывать обновление значения извне:

const im = new Inputmask("999-9-999-99999-9");
im.mask("#isbn");

document.querySelector("#setValue").addEventListener("click", () => {
  im.setValue("9783161484100");
});

Метод setValue корректно применяет форматирование при программной установке значения, сохраняя структуру маски.

Поведение при очистке поля

Очистка значения может быть полностью управляемой:

Inputmask({
  mask: "999-9-999-99999-9",
  clearMaskOnLostFocus: true
}).mask("#isbn");

При потере фокуса поле очищается, если введённое значение не завершено, что снижает риск отправки неполных данных.

Особенности интеграции с серверной валидацией

Серверная часть обычно ожидает ISBN без форматирования. Поэтому часто используется комбинация:

  • autoUnmask: true
  • removeMaskOnSubmit: true
Inputmask({
  mask: "999-9-999-99999-9",
  autoUnmask: true,
  removeMaskOnSubmit: true
}).mask("#isbn");

Это обеспечивает передачу «чистого» значения без необходимости дополнительной обработки на сервере.