Автозаполнение браузера в сочетании с маскирующим вводом создает одну из наиболее нестабильных зон при работе с Cleave.js. Основная причина заключается в том, что механизм автозаполнения изменяет значение DOM-элемента напрямую, минуя привычные события пользовательского ввода и внутренние хуки форматирования.
Cleave.js, в свою очередь, рассчитан на перехват
input-событий и немедленное преобразование значения в
форматированный вид. При автозаполнении эти два механизма часто вступают
в конфликт:
onValueChanged могут срабатывать с
неконсистентными данными.Особенно заметно это при работе с полями:
Ключевая проблема автозаполнения — неопределенный момент его срабатывания. Браузер может заполнить поле:
autocomplete;Если Cleave.js инициализируется слишком рано, он может получить пустое значение и не обработать уже вставленные данные.
Типичный проблемный сценарий:
const input = document.querySelector('#card');
const cleave = new Cleave(input, {
creditCard: true,
onValueChanged: function (e) {
console.log(e.target.value);
}
});
Если браузер вставляет значение после этого, Cleave.js может не корректно применить форматирование, либо применить его дважды.
Одним из устойчивых подходов является отложенная инициализация после событий, связанных с автозаполнением.
На практике используются следующие сигналы:
focusDOMContentLoadedloadrequestAnimationFramesetTimeout с минимальной задержкойПример отложенной инициализации:
const input = document.querySelector('#phone');
function initMask() {
new Cleave(input, {
phone: true,
phoneRegionCode: 'KZ'
});
}
window.addEventListener('load', () => {
setTimeout(initMask, 50);
});
Такая задержка позволяет браузеру завершить возможное автозаполнение до вмешательства Cleave.js.
Автозаполнение не всегда вызывает полный набор событий, ожидаемых
библиотекой. Поэтому требуется дополнительная синхронизация через
input.
Распространенная стратегия:
const input = document.querySelector('#date');
const cleave = new Cleave(input, {
date: true,
datePattern: ['d', 'm', 'Y']
});
input.addEventListener('input', () => {
cleave.setRawValue(input.value);
});
Однако такой подход требует осторожности. В некоторых браузерах
input.value уже содержит форматированное значение, и
повторное применение setRawValue приводит к накоплению
артефактов.
Наиболее частый дефект выглядит следующим образом:
4111111111111111;4111 1111 1111 1111;input повторно триггерит форматирование;Для предотвращения этого используется проверка состояния изменения:
let isAutofill = false;
input.addEventListener('animationstart', (e) => {
if (e.animationName === 'onAutoFillStart') {
isAutofill = true;
}
});
input.addEventListener('input', () => {
if (isAutofill) {
isAutofill = false;
return;
}
});
Этот паттерн основан на CSS-анимациях, которые многие браузеры используют для сигнализации autofill.
Некоторые браузеры (особенно Chromium) применяют псевдокласс
:-webkit-autofill. Он позволяет косвенно отследить момент
заполнения.
@keyframes autofillStart {
from {}
to {}
}
input:-webkit-autofill {
animation-name: autofillStart;
animation-duration: 0.01s;
}
input.addEventListener('animationstart', (e) => {
if (e.animationName === 'autofillStart') {
// автозаполнение произошло
}
});
Данный метод используется как триггер для повторной инициализации Cleave.js или для временного отключения форматирования.
В некоторых случаях целесообразно отключать маску на короткий промежуток времени.
Cleave.js не предоставляет прямого флага disable/enable, поэтому применяется пересоздание экземпляра:
let cleaveInstance = null;
function enableMask() {
cleaveInstance = new Cleave(input, {
numericOnly: true,
blocks: [4, 4, 4, 4]
});
}
function disableMask() {
if (cleaveInstance) {
cleaveInstance.destroy();
cleaveInstance = null;
}
}
Сценарий:
rawValue и восстановление исходных данныхОдной из ключевых особенностей Cleave.js является разделение:
value);rawValue).Автозаполнение часто вставляет уже “сырой” текст, но Cleave.js интерпретирует его как форматированный.
Правильная синхронизация:
const cleave = new Cleave(input, {
numeral: true
});
setTimeout(() => {
const value = input.value;
cleave.setRawValue(value);
}, 0);
Использование setTimeout с нулевой задержкой позволяет
дождаться завершения внутреннего обновления DOM после autofill.
Браузеры по-разному интерпретируют autocomplete, и это
влияет на момент и тип заполнения.
Примеры:
<input id="card" autocomplete="cc-number">
<input id="phone" autocomplete="tel">
<input id="date" autocomplete="bday">
Для Cleave.js важно учитывать:
cc-number почти всегда вызывает мгновенное
заполнение;tel может заполняться частично;off не гарантирует отключение autofill.Практика показывает, что autocomplete="off" часто
игнорируется современными браузерами.
После автозаполнения Cleave.js может некорректно вычислить позицию курсора.
Причина:
selectionStart/selectionEnd;Решение — принудительное восстановление позиции:
input.addEventListener('input', () => {
requestAnimationFrame(() => {
const pos = input.value.length;
input.setSelectionRange(pos, pos);
});
});
Автозаполнение не всегда генерирует события. В таких случаях используется наблюдение за DOM:
const observer = new MutationObserver(() => {
if (input.value && cleaveInstance) {
cleaveInstance.setRawValue(input.value);
}
});
observer.observe(input, {
attributes: true,
childList: false,
subtree: false
});
Хотя MutationObserver не всегда ловит изменения
value, в сочетании с input-событиями он
повышает устойчивость.
Автозаполнение часто заполняет сразу набор полей (карта, дата, CVV). Это создаёт каскадное срабатывание Cleave.js на нескольких инстансах одновременно.
Проблемы:
Решение — централизованная инициализация:
const inputs = document.querySelectorAll('.cleave');
window.addEventListener('load', () => {
setTimeout(() => {
inputs.forEach((el) => {
new Cleave(el, {
numericOnly: true
});
});
}, 100);
});
Менеджеры паролей действуют аналогично autofill, но часто:
Для Cleave.js это особенно критично в полях CVV или OTP.
Типовой подход:
setRawValue после изменения.input.addEventListener('focus', () => {
disableMask();
});
input.addEventListener('blur', () => {
setTimeout(enableMask, 100);
});
Устойчивое поведение достигается не одной настройкой, а комбинацией механизмов:
input событий с проверкой источника;setRawValue после autofill;requestAnimationFrame;Такой набор компенсирует отсутствие единого стандарта поведения браузеров и обеспечивает стабильную работу масок ввода в условиях непредсказуемого автозаполнения.