Функция item в библиотеке Awesomplete отвечает за
создание HTML-элемента пункта списка автодополнения. Именно она
определяет, каким образом каждая подсказка будет отображаться внутри
выпадающего списка.
По умолчанию библиотека генерирует элемент <li>,
содержащий текст найденного совпадения с выделением совпадающих символов
через тег <mark>. Однако стандартное поведение
подходит не для всех сценариев. Во многих проектах требуется:
Для решения этих задач используется переопределение функции
item.
itemВнутри библиотеки функция имеет примерно следующий вид:
Awesomplete.ITEM = function (text, input) {
return Awesomplete.$.create("li", {
innerHTML: text.replace(
new RegExp(Awesomplete.REGEX_ESCAPE(input.trim()), "gi"),
"<mark>$&</mark>"
),
"aria-selected": "false"
});
};
Функция:
<li>;Собственная функция item передаётся через конфигурацию
экземпляра.
const awesomplete = new Awesomplete(input, {
item: function(text, input) {
const li = document.createElement("li");
li.textContent = text;
return li;
}
});
textСодержимое элемента списка.
item: function(text, input) {
console.log(text);
}
При использовании простого массива строк:
[
"JavaScript",
"TypeScript",
"Python"
]
значением text будет строка.
inputТекущее значение поля ввода.
item: function(text, input) {
console.log(input);
}
Используется для:
Наиболее распространённый сценарий — изменение структуры элемента.
new Awesomplete(input, {
item: function(text) {
const li = document.createElement("li");
li.innerHTML = `
<strong>${text}</strong>
`;
return li;
}
});
Часто требуется показывать дополнительную информацию.
const list = [
{
label: "JavaScript",
description: "Язык программирования"
},
{
label: "HTML",
description: "Язык разметки"
}
];
new Awesomplete(input, {
list,
item: function(item) {
const li = document.createElement("li");
li.innerHTML = `
<div class="title">${item.label}</div>
<div class="description">
${item.description}
</div>
`;
return li;
}
});
Если список содержит объекты, функция item получает
полный объект.
[
{
label: "VS Code",
type: "Редактор"
}
]
Это позволяет формировать сложные шаблоны.
item: function(item) {
const li = document.createElement("li");
li.innerHTML = `
<span>${item.label}</span>
<small>${item.type}</small>
`;
return li;
}
item: function(item) {
const li = document.createElement("li");
li.innerHTML = `
<div class="autocomplete-item">
<span class="icon">?</span>
<span>${item.label}</span>
</div>
`;
return li;
}
item: function(item) {
const li = document.createElement("li");
li.innerHTML = `
<div class="row">
<svg width="16" height="16">
<circle
cx="8"
cy="8"
r="6"
fill="green"
/>
</svg>
<span>${item.label}</span>
</div>
`;
return li;
}
Функция item может возвращать полноценные карточки.
item: function(item) {
const li = document.createElement("li");
li.classList.add("card-item");
li.innerHTML = `
<div class="card-title">
${item.name}
</div>
<div class="card-meta">
${item.category}
</div>
<div class="card-price">
${item.price}
</div>
`;
return li;
}
При переопределении item стандартная подсветка исчезает.
Её приходится реализовывать самостоятельно.
replaceitem: function(text, input) {
const li = document.createElement("li");
const regex = new RegExp(input, "gi");
li.innerHTML = text.replace(regex, match => {
return `<mark>${match}</mark>`;
});
return li;
}
Без экранирования регулярное выражение может работать некорректно.
Проблемный пример:
input = "c++"
Правильный подход:
function escapeRegex(value) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
Использование:
item: function(text, input) {
const li = document.createElement("li");
const regex = new RegExp(
escapeRegex(input),
"gi"
);
li.innerHTML = text.replace(regex, "<mark>$&</mark>");
return li;
}
item: function(user) {
const li = document.createElement("li");
li.innerHTML = `
<div class="user-item">
<img
src="${user.avatar}"
alt=""
>
<span>${user.name}</span>
</div>
`;
return li;
}
Иногда необходимо сохранять служебные данные.
item: function(item) {
const li = document.createElement("li");
li.dataset.id = item.id;
li.dataset.type = item.type;
li.textContent = item.label;
return li;
}
item: function(item) {
const li = document.createElement("li");
li.classList.add("autocomplete-item");
if (item.active) {
li.classList.add("active");
}
li.textContent = item.label;
return li;
}
item: function(item) {
const li = document.createElement("li");
li.style.color = item.color;
li.textContent = item.label;
return li;
}
item: function(item) {
const li = document.createElement("li");
li.innerHTML = `
<div class="category">
${item.category}
</div>
<div class="title">
${item.label}
</div>
`;
return li;
}
item: function(item) {
const li = document.createElement("li");
if (item.separator) {
li.classList.add("separator");
li.textContent = item.title;
return li;
}
li.textContent = item.label;
return li;
}
Для крупных шаблонов код удобно выносить отдельно.
function renderItem(item) {
return `
<div class="title">
${item.label}
</div>
<div class="meta">
${item.type}
</div>
`;
}
Использование:
item: function(item) {
const li = document.createElement("li");
li.innerHTML = renderItem(item);
return li;
}
DocumentFragmentПри сложной структуре можно избегать большого количества
innerHTML.
item: function(item) {
const li = document.createElement("li");
const title = document.createElement("div");
title.textContent = item.label;
const meta = document.createElement("small");
meta.textContent = item.type;
li.append(title);
li.append(meta);
return li;
}
Сложная функция item может замедлять отображение
списка.
На производительность влияют:
Плохо:
item: function(text, input) {
const regex = new RegExp(input, "gi");
}
Лучше:
const cache = {};
item: function(text, input) {
if (!cache[input]) {
cache[input] = new RegExp(input, "gi");
}
const regex = cache[input];
}
innerHTMLБольшие HTML-строки увеличивают нагрузку.
Более эффективный подход:
item: function(item) {
const li = document.createElement("li");
const span = document.createElement("span");
span.textContent = item.label;
li.append(span);
return li;
}
Использование innerHTML может привести к
XSS-уязвимостям.
Опасный пример:
li.innerHTML = item.label;
Если данные приходят с сервера:
<script>alert(1)</script>
код выполнится.
li.textContent = item.label;
Если HTML необходим:
function sanitize(value) {
return value
.replace(/</g, "<")
.replace(/>/g, ">");
}
Использование:
li.innerHTML = sanitize(item.label);
Для доступности желательно сохранять необходимые атрибуты.
item: function(item) {
const li = document.createElement("li");
li.setAttribute(
"aria-selected",
"false"
);
li.textContent = item.label;
return li;
}
item: function(item) {
const li = document.createElement("li");
if (item.loading) {
li.textContent = "Загрузка...";
li.classList.add("loading");
return li;
}
li.textContent = item.label;
return li;
}
item: function(item) {
const li = document.createElement("li");
const badge = document.createElement("span");
badge.className = "badge";
badge.textContent = item.type;
li.append(item.label);
li.append(badge);
return li;
}
item: function(item) {
const li = document.createElement("li");
li.classList.add("fade-item");
li.textContent = item.label;
return li;
}
.fade-item {
animation: fade .2s ease;
}
@keyframes fade {
from {
opacity: 0;
transform: translateY(4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
new Awesomplete(input, {
list: users,
item: function(user, input) {
const li = document.createElement("li");
const regex = new RegExp(
escapeRegex(input),
"gi"
);
const highlighted = user.name.replace(
regex,
"<mark>$&</mark>"
);
li.className = "user-row";
li.innerHTML = `
<img
class="avatar"
src="${user.avatar}"
>
<div class="content">
<div class="name">
${highlighted}
</div>
<div class="email">
${user.email}
</div>
</div>
`;
li.setAttribute(
"aria-selected",
"false"
);
return li;
}
});
item: function(text) {
const li = document.createElement("li");
}
Без return элемент не появится.
Неправильно:
item: function(text) {
return `<li>${text}</li>`;
}
Правильно:
item: function(text) {
const li = document.createElement("li");
li.textContent = text;
return li;
}
После переопределения item встроенная подсветка больше
не используется автоматически.
aria-selectedБез атрибута могут возникать проблемы доступности.
Слишком сложная разметка ухудшает производительность при большом количестве элементов.