HTML
Поле типа HTML предназначено для хранения форматированного текста с поддержкой Rich Text Editor.
Основные характеристики
- Тип данных: HTML-разметка
- Редактор: WYSIWYG (What You See Is What You Get)
- Максимальный размер: До 1 МБ
- Санитизация: Автоматическая очистка от опасного кода
- Форматирование: Поддержка стилей, списков, таблиц, изображений
Возможности редактора
Форматирование текста
- Жирный, курсив, подчеркнутый,
зачеркнутый - Заголовки (H1-H6)
- Цвет текста и фона
- Размер шрифта
- Выравнивание (по левому краю, по центру, по правому краю, по ширине)
Списки
- Маркированные списки
- Нумерованные списки
- Вложенные списки
- Чек-листы
Вставка элементов
- Изображения
- Ссылки
- Таблицы
- Видео (YouTube, Vimeo)
- Блоки кода
- Цитаты
- Горизонтальные линии
Настройки поля
При создании поля типа "HTML" доступны следующие настройки:
- Название поля: Отображаемое имя поля
- Системное имя: Уникальный идентификатор для API
- Описание: Подсказка для пользователей
- Обязательное поле: Требовать заполнения
- Максимальная длина: Ограничение размера контента
- Разрешенные теги: Список допустимых HTML-тегов
- Разрешенные атрибуты: Список допустимых атрибутов
- Автосанитизация: Автоматическая очистка от опасного кода
Примеры использования
Описание товара
{
"name": "description",
"type": "html",
"required": true
}
Контент статьи
{
"name": "content",
"type": "html",
"required": true,
"maxLength": 1000000
}
Комментарий
{
"name": "comment",
"type": "html",
"allowedTags": ["p", "br", "strong", "em", "a", "ul", "ol", "li"]
}
Работа через API
Создание записи
const record = await emd.database.collection('articles').create({
title: 'Моя статья',
content: `
<h1>Заголовок статьи</h1>
<p>Это первый абзац с <strong>жирным текстом</strong> и <em>курсивом</em>.</p>
<ul>
<li>Первый пункт</li>
<li>Второй пункт</li>
</ul>
<p>Ссылка на <a href="https://example.com">сайт</a>.</p>
`
});
Получение HTML
const record = await emd.database.collection('articles').get(recordId);
console.log(record.content);
// {
// html: "<h1>Заголовок</h1><p>Текст...</p>",
// text: "Заголовок\nТекст...",
// length: 1234,
// wordCount: 156
// }
Отображение HTML
const record = await emd.database.collection('articles').get(recordId);
// Безопасное отображение (уже санитизировано)
document.getElementById('content').innerHTML = record.content.html;
Обновление HTML
await emd.database.collection('articles').update(recordId, {
content: `
<h1>Обновленный заголовок</h1>
<p>Новый контент статьи.</p>
`
});
Санитизация HTML
Автоматическая очистка
HTML автоматически очищается от потенциально опасного кода:
// Ввод
const dangerousHtml = `
<p>Безопасный текст</p>
<script>alert('XSS')</script>
<img src="x" onerror="alert('XSS')">
`;
// После санитизации
const sanitized = `
<p>Безопасный текст</p>
`;
Настройка разрешенных тегов
{
"name": "content",
"type": "html",
"allowedTags": [
"p", "br", "strong", "em", "u", "s",
"h1", "h2", "h3", "h4", "h5", "h6",
"ul", "ol", "li",
"a", "img",
"blockquote", "code", "pre",
"table", "thead", "tbody", "tr", "th", "td"
],
"allowedAttributes": {
"a": ["href", "title", "target"],
"img": ["src", "alt", "width", "height"]
}
}
Извлечение текста
Получение plain text
const record = await emd.database.collection('articles').get(recordId);
// Текст без HTML-тегов
const plainText = record.content.text;
// Или вручную
function stripHtml(html) {
return html.replace(/<[^>]*>/g, '');
}
Получение краткого описания
const record = await emd.database.collection('articles').get(recordId);
// Первые 200 символов текста
const excerpt = record.content.text.substring(0, 200) + '...';
Подсчет слов
const record = await emd.database.collection('articles').get(recordId);
console.log(`Количество слов: ${record.content.wordCount}`);
Поиск по HTML контенту
Полнотекстовый поиск
const articles = await emd.database.collection('articles').find({
content: { $search: 'ключевое слово' }
});
Поиск по тексту (без тегов)
const articles = await emd.database.collection('articles').find({
'content.text': { $contains: 'поисковый запрос' }
});
Вставка изображений
Загрузка изображения
// Загрузить изображение
const formData = new FormData();
formData.append('file', imageFile);
const uploadedImage = await emd.storage.upload(formData);
// Вставить в HTML
const html = `
<p>Текст перед изображением</p>
<img src="${uploadedImage.url}" alt="Описание изображения" />
<p>Текст после изображения</p>
`;
await emd.database.collection('articles').update(recordId, {
content: html
});
Base64 изображения
// Конвертировать изображение в Base64
function imageToBase64(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
const base64 = await imageToBase64(imageFile);
const html = `<img src="${base64}" alt="Изображение" />`;
Вставка видео
YouTube
const youtubeUrl = 'https://www.youtube.com/watch?v=VIDEO_ID';
const videoId = 'VIDEO_ID';
const html = `
<div class="video-container">
<iframe
width="560"
height="315"
src="https://www.youtube.com/embed/${videoId}"
frameborder="0"
allowfullscreen>
</iframe>
</div>
`;
Vimeo
const vimeoId = 'VIDEO_ID';
const html = `
<div class="video-container">
<iframe
src="https://player.vimeo.com/video/${vimeoId}"
width="640"
height="360"
frameborder="0"
allowfullscreen>
</iframe>
</div>
`;
Таблицы
Создание таблицы
const html = `
<table>
<thead>
<tr>
<th>Название</th>
<th>Цена</th>
<th>Количество</th>
</tr>
</thead>
<tbody>
<tr>
<td>Товар 1</td>
<td>1000 ₽</td>
<td>5</td>
</tr>
<tr>
<td>Товар 2</td>
<td>2000 ₽</td>
<td>3</td>
</tr>
</tbody>
</table>
`;
Блоки кода
Вставка кода
const html = `
<p>Пример кода на JavaScript:</p>
<pre><code class="language-javascript">
function hello() {
console.log('Hello, World!');
}
</code></pre>
`;
Подсветка синтаксиса
Используйте библиотеки типа Prism.js или Highlight.js для подсветки кода при отображении.
Шаблоны
Шаблон письма
const emailTemplate = `
<div style="font-family: Arial, sans-serif; max-width: 600px; margin: 0 auto;">
<h1 style="color: #333;">Добро пожаловать!</h1>
<p>Спасибо за регистрацию на нашем сайте.</p>
<a href="{{confirmUrl}}" style="display: inline-block; padding: 10px 20px; background: #007bff; color: white; text-decoration: none; border-radius: 5px;">
Подтвердить email
</a>
</div>
`;
Шаблон статьи
const articleTemplate = `
<article>
<header>
<h1>{{title}}</h1>
<p class="meta">
<time>{{date}}</time> |
<span>{{author}}</span>
</p>
</header>
<div class="content">
{{content}}
</div>
</article>
`;
Экспорт в другие форматы
HTML в Markdown
import TurndownService from 'turndown';
const turndownService = new TurndownService();
const markdown = turndownService.turndown(record.content.html);
HTML в PDF
import html2pdf from 'html2pdf.js';
const element = document.getElementById('content');
element.innerHTML = record.content.html;
html2pdf()
.from(element)
.save('article.pdf');
Рекомендации
- Используйте HTML для форматированного контента (статьи, описания)
- Для простого текста используйте тип String
- Всегда включайте санитизацию для безопасности
- Ограничивайте разрешенные теги в зависимости от контекста
- Используйте CDN для изображений вместо Base64
- Оптимизируйте изображения перед вставкой
- Добавляйте alt-текст для изображений (SEO и доступность)
- Используйте семантические теги (h1-h6, p, ul, ol)
- Тестируйте отображение на разных устройствах
Безопасность
Защита от XSS
- Автоматическая санитизация включена по умолчанию
- Удаление опасных тегов (
<script>,<iframe>,<object>) - Удаление опасных атрибутов (
onclick,onerror) - Валидация URL в ссылках и изображениях
Content Security Policy
// Настройка CSP для безопасного отображения HTML
const cspMeta = `
<meta http-equiv="Content-Security-Policy"
content="default-src 'self';
img-src 'self' https://cdn.emd.one;
script-src 'none';">
`;
Отличие от других типов
- HTML vs String: HTML для форматированного текста, String для простого
- HTML vs JSON: HTML для контента, JSON для структурированных данных
- HTML vs Markdown: HTML для WYSIWYG редактора, Markdown для разметки