Встроенная логика подбора подсказок в Awesomplete опирается на два ключевых этапа обработки списка: фильтрацию и сортировку. Именно на уровне сортировки формируется конечный порядок отображения элементов, и именно здесь реализуется приоритизация результатов, определяющая, какие варианты оказываются выше в выпадающем списке.
Базовое поведение библиотеки ориентировано на простое правило: совпадения, начинающиеся с введённой строки, должны иметь более высокий приоритет, чем совпадения внутри строки. Однако в реальных интерфейсах этого недостаточно. Требуются дополнительные механизмы: учёт частоты использования, ручные веса, бизнес-логика, контекст ввода и даже внешние источники данных.
По умолчанию Awesomplete использует внутреннюю функцию сортировки, которая сравнивает позиции вхождения строки запроса в элемент списка.
Логика выглядит упрощённо следующим образом:
Условная реализация стандартного подхода может быть представлена так:
Awesomplete.prototype.sort = function(a, b) {
return (a.indexOf(this.input.value) > -1 ? 0 : 1)
- (b.indexOf(this.input.value) > -1 ? 0 : 1);
};
Хотя реальная реализация более аккуратна, ключевая идея сохраняется: позиция совпадения влияет на порядок.
Awesomplete предоставляет возможность заменить сортировку через
параметр sort. Это основной инструмент управления
приоритетами.
new Awesomplete(input, {
list: ["javascript", "java", "python", "php"],
sort: function(a, b) {
return customScore(a) - customScore(b);
}
});
Функция сортировки должна возвращать:
a выше bb выше aЭто позволяет полностью контролировать порядок выдачи.
Наиболее распространённая стратегия — учитывать индекс вхождения строки запроса.
function positionScore(item, query) {
const index = item.toLowerCase().indexOf(query.toLowerCase());
return index === -1 ? 1000 : index;
}
Использование:
new Awesomplete(input, {
list: items,
sort: function(a, b) {
const q = this.input.value.toLowerCase();
return positionScore(a, q) - positionScore(b, q);
}
});
Чем меньше индекс, тем выше приоритет.
В интерфейсах автодополнения часто требуется выделять полные совпадения как абсолютно приоритетные.
Добавляется дополнительное правило:
function score(item, query) {
item = item.toLowerCase();
query = query.toLowerCase();
if (item === query) return 0;
if (item.startsWith(query)) return 1;
const pos = item.indexOf(query);
return pos === -1 ? 1000 : 10 + pos;
}
Сортировка:
sort: (a, b) => score(a, this.input.value) - score(b, this.input.value)
Такой подход делает поведение предсказуемым: наиболее релевантные элементы всегда оказываются наверху.
Когда список данных содержит бизнес-важность, простая текстовая релевантность становится недостаточной. Тогда вводится система весов.
Пример структуры данных:
const list = [
{ label: "JavaScript", weight: 10 },
{ label: "Java", weight: 8 },
{ label: "Python", weight: 9 }
];
Сортировка учитывает два фактора: релевантность и вес.
function score(item, query) {
const text = item.label.toLowerCase();
const q = query.toLowerCase();
let relevance = text.indexOf(q);
if (relevance === -1) relevance = 1000;
const weightPenalty = 10 - item.weight;
return relevance + weightPenalty;
}
Итоговый порядок начинает учитывать не только текст, но и приоритет домена данных.
На практике используется многокомпонентная модель оценки:
Пример расширенной функции:
function advancedScore(item, query) {
const text = item.label.toLowerCase();
const q = query.toLowerCase();
let score = 0;
if (text === q) score -= 100;
else if (text.startsWith(q)) score -= 50;
const pos = text.indexOf(q);
score += pos === -1 ? 1000 : pos;
score += text.length * 0.01;
if (item.weight) {
score -= item.weight * 2;
}
return score;
}
Такой подход позволяет гибко управлять выдачей даже в сложных интерфейсах.
Приоритизация часто сталкивается с проблемой нестабильного порядка, когда элементы с одинаковым score “прыгают” при каждом вводе.
Для решения добавляется вторичный критерий сортировки:
sort: function(a, b) {
const q = this.input.value;
const sa = score(a, q);
const sb = score(b, q);
if (sa === sb) {
return a.localeCompare(b);
}
return sa - sb;
}
Это обеспечивает детерминированный порядок отображения.
В некоторых сценариях приоритет задаётся уже порядком массива. Например, когда backend возвращает отсортированный список.
Awesomplete позволяет отключить сортировку:
new Awesomplete(input, {
list: items,
sort: false
});
В этом режиме библиотека сохраняет исходный порядок элементов после фильтрации. Это важно при серверной ранжировке, где клиентская логика должна быть минимальной.
Иногда более эффективно не переопределять сортировку, а заранее перестроить список.
Пример: добавление префиксных элементов в начало массива:
function reorderList(list, query) {
const q = query.toLowerCase();
const startsWith = [];
const contains = [];
list.forEach(item => {
const text = item.toLowerCase();
if (text.startsWith(q)) {
startsWith.push(item);
} else {
contains.push(item);
}
});
return [...startsWith, ...contains];
}
Затем передача в Awesomplete:
input.addEventListener("input", function() {
awesomplete.list = reorderList(originalList, this.value);
});
Этот подход снижает нагрузку на sort и упрощает логику
библиотеки.
Когда элементы списка — не строки, а объекты, сортировка усложняется.
const list = [
{ label: "React", category: "frontend", popularity: 10 },
{ label: "Node.js", category: "backend", popularity: 9 }
];
Приоритет может зависеть от контекста:
Пример контекстной логики:
function contextualScore(item, query) {
let score = advancedScore(item, query);
if (query.startsWith("re") && item.category === "frontend") {
score -= 20;
}
if (query.startsWith("node") && item.category === "backend") {
score -= 20;
}
score -= item.popularity;
return score;
}
Важно различать два этапа:
Awesomplete позволяет переопределять оба, но приоритизация должна
оставаться в sort. Перегрузка фильтра логикой ранжирования
приводит к ухудшению читаемости и снижению гибкости.
Правильная модель:
Типичная промышленная схема работы с Awesomplete выглядит следующим образом:
Такой подход обеспечивает управляемое поведение автодополнения при любых объёмах данных и любых правилах доменной логики.