Кастомные формы на Тильде: валидация, маски, условные поля

Стандартные формы на Тильде работают, но часто не хватает маски для телефона, проверки ИНН или условного показа полей. Разберём, как добавить всё это на чистом JavaScript — без сторонних библиотек или с минимальными зависимостями.

Маска для телефона: IMask.js

Начнём с самого частого запроса — маска ввода телефона. Можно реализовать на ванильном JS, но IMask.js весит 7 КБ и экономит часы отладки.

Подключаем библиотеку через блок T123 (HTML-код) перед закрывающим </body>:

<script src="https://unpkg.com/imask@7.6.0/dist/imask.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
  var phoneInput = document.querySelector('input[name="Phone"]');
  if (!phoneInput) return;

  IMask(phoneInput, {
    mask: '+{7} (000) 000-00-00',
    lazy: false,
    placeholderChar: '_'
  });
});
</script>

Маска не даст ввести буквы, автоматически подставит скобки и дефисы. Атрибут lazy: false показывает маску сразу при фокусе.

Маска на ванильном JavaScript

Если не хотите тянуть библиотеку — вот минимальная реализация:

document.addEventListener('DOMContentLoaded', function() {
  var input = document.querySelector('input[name="Phone"]');
  if (!input) return;

  input.addEventListener('input', function(e) {
    var x = e.target.value.replace(/\D/g, '');
    if (x.length === 0) { e.target.value = ''; return; }
    if (x[0] === '8') x = '7' + x.slice(1);
    if (x[0] !== '7') x = '7' + x;

    var formatted = '+7';
    if (x.length > 1) formatted += ' (' + x.slice(1, 4);
    if (x.length > 4) formatted += ') ' + x.slice(4, 7);
    if (x.length > 7) formatted += '-' + x.slice(7, 9);
    if (x.length > 9) formatted += '-' + x.slice(9, 11);
    e.target.value = formatted;
  });

  input.addEventListener('focus', function() {
    if (!input.value) input.value = '+7';
  });

  input.addEventListener('blur', function() {
    if (input.value === '+7') input.value = '';
  });
});

Кастомная валидация

Тильда проверяет только обязательность полей и формат email. Добавим свои правила — проверку длины телефона, ИНН, формата даты.

document.addEventListener('DOMContentLoaded', function() {
  var form = document.querySelector('.t-form');
  if (!form) return;

  var validators = {
    'Phone': function(value) {
      var digits = value.replace(/\D/g, '');
      if (digits.length !== 11) return 'Введите 11 цифр номера';
      return null;
    },
    'INN': function(value) {
      if (!/^\d{10}$|^\d{12}$/.test(value)) return 'ИНН: 10 или 12 цифр';
      return null;
    },
    'Date': function(value) {
      if (!/^\d{2}\.\d{2}\.\d{4}$/.test(value)) return 'Формат: ДД.ММ.ГГГГ';
      var parts = value.split('.');
      var d = new Date(parts[2], parts[1] - 1, parts[0]);
      if (isNaN(d.getTime())) return 'Некорректная дата';
      return null;
    }
  };

  var submitBtn = form.querySelector('.t-submit');
  if (!submitBtn) return;

  submitBtn.addEventListener('click', function(e) {
    var hasErrors = false;

    Object.keys(validators).forEach(function(fieldName) {
      var input = form.querySelector('input[name="' + fieldName + '"]');
      if (!input || !input.value) return;

      var error = validators[fieldName](input.value);
      var existingError = input.parentNode.querySelector('.custom-error');
      if (existingError) existingError.remove();

      if (error) {
        hasErrors = true;
        var errorEl = document.createElement('div');
        errorEl.className = 'custom-error';
        errorEl.textContent = error;
        errorEl.style.cssText = 'color:#ff4444;font-size:12px;margin-top:4px;';
        input.parentNode.appendChild(errorEl);
        input.style.borderColor = '#ff4444';
      } else {
        input.style.borderColor = '';
      }
    });

    if (hasErrors) {
      e.preventDefault();
      e.stopPropagation();
    }
  }, true);
});

Ключевой момент — третий аргумент true в addEventListener. Это фаза захвата: наш обработчик сработает раньше обработчика Тильды, и мы успеем заблокировать отправку.

Условные поля: показ/скрытие по выбору

Классическая задача: пользователь выбирает «Юридическое лицо» — появляются поля ИНН, КПП, название компании. Выбирает «Физическое лицо» — поля скрыты.

document.addEventListener('DOMContentLoaded', function() {
  // Находим select или radio по имени поля
  var typeSelect = document.querySelector('select[name="ClientType"]');
  // Группы полей, которые нужно показывать/скрывать
  var companyFields = document.querySelectorAll(
    '[data-input-lid="1234567890"], [data-input-lid="1234567891"]'
  );

  function toggleFields() {
    var isCompany = typeSelect.value === 'Юридическое лицо';
    companyFields.forEach(function(field) {
      var wrapper = field.closest('.t-input-group');
      if (!wrapper) return;
      wrapper.style.display = isCompany ? '' : 'none';
      // Снимаем обязательность для скрытых полей
      var input = wrapper.querySelector('input');
      if (input) {
        if (isCompany) {
          input.setAttribute('required', '');
        } else {
          input.removeAttribute('required');
          input.value = '';
        }
      }
    });
  }

  if (typeSelect) {
    typeSelect.addEventListener('change', toggleFields);
    toggleFields(); // начальное состояние
  }
});

Чтобы найти data-input-lid нужного поля — откройте страницу, кликните правой кнопкой по полю → «Исследовать элемент». В атрибутах контейнера увидите ID.

Условные поля через радиокнопки

Если вместо select используются радиокнопки:

document.addEventListener('DOMContentLoaded', function() {
  var radioButtons = document.querySelectorAll('input[name="DeliveryType"]');
  var addressBlock = document.querySelector('.t-input-group_address');
  var pickupBlock = document.querySelector('.t-input-group_pickup');

  function handleChange() {
    var selected = document.querySelector('input[name="DeliveryType"]:checked');
    if (!selected) return;

    if (selected.value === 'Доставка') {
      addressBlock.style.display = '';
      pickupBlock.style.display = 'none';
    } else {
      addressBlock.style.display = 'none';
      pickupBlock.style.display = '';
    }
  }

  radioButtons.forEach(function(radio) {
    radio.addEventListener('change', handleChange);
  });
  handleChange();
});

Многошаговая форма (wizard)

Разбиваем длинную форму на шаги. Каждый шаг — это блок, который показывается или скрывается. Между шагами — кнопки «Назад» и «Далее».

document.addEventListener('DOMContentLoaded', function() {
  var form = document.querySelector('.t-form');
  if (!form) return;

  var steps = form.querySelectorAll('.js-form-step');
  var currentStep = 0;

  // Создаём навигацию
  var nav = document.createElement('div');
  nav.style.cssText = 'display:flex;gap:12px;margin-top:20px;justify-content:space-between;';
  nav.innerHTML =
    '<button type="button" class="js-step-prev" style="padding:10px 24px;background:#e0e0e0;border:none;border-radius:6px;cursor:pointer;font-size:14px;">Назад</button>' +
    '<span class="js-step-counter" style="line-height:40px;font-size:14px;color:#888;"></span>' +
    '<button type="button" class="js-step-next" style="padding:10px 24px;background:#222;color:#fff;border:none;border-radius:6px;cursor:pointer;font-size:14px;">Далее</button>';
  form.appendChild(nav);

  var prevBtn = nav.querySelector('.js-step-prev');
  var nextBtn = nav.querySelector('.js-step-next');
  var counter = nav.querySelector('.js-step-counter');
  var submitBtn = form.querySelector('.t-submit');

  function showStep(index) {
    steps.forEach(function(step, i) {
      step.style.display = i === index ? '' : 'none';
    });
    prevBtn.style.visibility = index === 0 ? 'hidden' : 'visible';
    nextBtn.style.display = index === steps.length - 1 ? 'none' : '';
    submitBtn.style.display = index === steps.length - 1 ? '' : 'none';
    counter.textContent = 'Шаг ' + (index + 1) + ' из ' + steps.length;
    currentStep = index;
  }

  function validateStep(index) {
    var stepEl = steps[index];
    var inputs = stepEl.querySelectorAll('input[required], select[required], textarea[required]');
    var valid = true;
    inputs.forEach(function(inp) {
      if (!inp.value.trim()) {
        inp.style.borderColor = '#ff4444';
        valid = false;
      } else {
        inp.style.borderColor = '';
      }
    });
    return valid;
  }

  nextBtn.addEventListener('click', function() {
    if (validateStep(currentStep) && currentStep < steps.length - 1) {
      showStep(currentStep + 1);
    }
  });

  prevBtn.addEventListener('click', function() {
    if (currentStep > 0) showStep(currentStep - 1);
  });

  showStep(0);
});

Чтобы это работало, оберните группы полей в Zero-блоки или используйте T123 для создания обёрток с классом js-form-step.

Стилизация ошибок валидации

Добавьте CSS через блок T123 или «Настройки сайта → Head»:

.custom-error {
  color: #e74c3c;
  font-size: 12px;
  margin-top: 4px;
  animation: fadeIn 0.3s ease;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(-4px); }
  to { opacity: 1; transform: translateY(0); }
}

.t-input-group input:focus {
  border-color: #222 !important;
  transition: border-color 0.2s;
}

/* Прогресс-бар для многошаговой формы */
.step-progress {
  display: flex;
  gap: 8px;
  margin-bottom: 24px;
}

.step-progress__item {
  flex: 1;
  height: 4px;
  background: #e0e0e0;
  border-radius: 2px;
  transition: background 0.3s;
}

.step-progress__item.active {
  background: #222;
}

Маска даты без библиотек

function dateMask(input) {
  input.addEventListener('input', function(e) {
    var v = e.target.value.replace(/\D/g, '');
    if (v.length > 8) v = v.slice(0, 8);
    var result = '';
    if (v.length > 0) result = v.slice(0, 2);
    if (v.length > 2) result += '.' + v.slice(2, 4);
    if (v.length > 4) result += '.' + v.slice(4, 8);
    e.target.value = result;
  });
  input.setAttribute('placeholder', 'ДД.ММ.ГГГГ');
  input.setAttribute('maxlength', '10');
}

var dateInput = document.querySelector('input[name="Date"]');
if (dateInput) dateMask(dateInput);

Итого: чек-лист

Задача Решение Библиотека
Маска телефона IMask.js или ванильный JS IMask (7 КБ) / нет
Маска даты Ванильный JS Нет
Валидация ИНН, email, телефона Кастомные правила на submit Нет
Условные поля show/hide по change Нет
Многошаговая форма JS wizard с валидацией шагов Нет

Все скрипты добавляются через блок T123 (Вставка кода) или через раздел «Head / Footer» в настройках страницы. Не забудьте обернуть код в DOMContentLoaded — иначе скрипт может выполниться до рендера формы.

Есть идея? Реализуем

Разрабатываем проекты, которые решают задачи бизнеса — от лендинга до сложного сервиса. Расскажите о своей задаче, подберём решение.

Написать в Telegram

29.03.2026

Нужна консультация?

Оставьте свои контактные данные, или свяжитесь с нами удобным для вас способом

Привет! Меня зовут Багира. Пишите, я все передам хозяевам!

Привет! Меня зовут Багира. Пишите, я все передам хозяевам!

Нажимая кнопку «Принять», вы соглашаетесь на сбор cookie. Мы используем их для обеспечения функционирования веб-сайта, аналитики действий и улучшения качества обслуживания. Если Вы не хотите, чтобы эти данные обрабатывались, отключите cookie в настройках браузера или прекратите использовать сайт.
Принять