Перейти к содержимому
ФреймворкJavaScript

Marks API: строчное форматирование по всему выделению

Range-aware операции со строчными метками для строчных инструментов форматирования. selection.findParentTag смотрит только на два граничных узла выделения (anchor и focus) и их предков. api.marks работает со ВСЕМ диапазоном.

  • has отвечает на вопрос «покрыт ли каждый текстовый узел выделения».
  • apply и remove разрезают частично покрытые обёртки на границах диапазона, обновляют полностью покрывающие обёртки на месте и восстанавливают выделение.
    • Ещё apply и remove расширяют диапазон на замыкающий пробел, который браузеры исключают из выделения по двойному клику.

Метка описывается декларативно через MarkSpec: tag, aliasTags, className, attributes, style. aliasTags позволяет унаследованным вариантам тега считаться ТОЙ ЖЕ меткой, например <b> рядом со <strong> или <em> рядом с <i>. Новые обёртки всегда используют канонический тег.

Строковые значения статичны и входят в идентичность метки. Значения-функции вычисляются из state, переданного в apply/toggle, и сознательно ИСКЛЮЧЕНЫ из идентичности. Поэтому палитра цветов остаётся ОДНОЙ меткой, которая обновляется на месте, а не N взаимно отменяющими друг друга метками.

Две спецификации с одинаковыми tag, classNames и статичными атрибутами принадлежат одной семье. Они компонуются на одном элементе. Цвет текста и цвет фона могут стоять на одном <mark>.

Каждый метод по умолчанию берёт первый диапазон живого выделения.

Экспорт ядра markSanitizerConfig(spec) выводит правило санитайзера для метки. Тег спецификации попадает в allowlist, необъявленные style-свойства и классы вычищаются, объявленные атрибуты сохраняются. Значения-функции учитываются по имени свойства, поэтому динамические значения не теряются при сохранении.

createReactInlineTool в React-адаптере применяет тот же вывод автоматически, когда инструмент объявляет спецификацию mark.

Обновлено 8 сент. 2026 г.Редактировать на GitHub

Как получить экземпляр редактора

Методы ниже вызываются на редакторе, созданном через new Blok(). Они доступны после того, как разрешится editor.isReady.

TypeScript
// You already hold the instance returned by the constructor.
const editor = new Blok({ holder: 'editor' });
await editor.isReady;

// Call any API method on it.
editor.caret.setToLastBlock('end');

Методы

marks.has(spec, range?)

boolean

Находится ли каждый текстовый узел диапазона внутри обёртки, совпадающей со спецификацией. Узлы только из пробелов игнорируются. При схлопнутом курсоре проверяются предки курсора.

selection.findParentTag смотрит только на граничные узлы выделения и их предков. Этот метод строже: выделение, лишь частично несущее метку, даёт false.

Когда использовать

Range-aware замена проверки активности через findParentTag. Выделение, лишь частично несущее метку, даёт false, поэтому иконка на панели инструментов не соврёт.

Параметры

ПараметрТипОбязательныйПо умолчаниюОписание
specMarkSpecОбязательный—

Декларативное описание метки: тег плюс необязательные className, attributes и style.

rangeRange—current selection

Диапазон для проверки. По умолчанию это первый диапазон живого выделения.

TypeScript
const highlight = { tag: 'span', className: 'my-highlight' };

// True only when the WHOLE selection is covered — a half-highlighted
// selection reports false, so a toolbar icon cannot lie
const active = editor.marks.has(highlight);

marks.find(spec, from?)

HTMLElement | null

Ближайший элемент-предок, совпадающий со спецификацией, начиная с заданного узла (или со стартового контейнера текущего выделения). Совпадение учитывает всю спецификацию, а не только имя тега: тег, classNames и статичные значения атрибутов и стилей.

Когда использовать

Поиск предка по спецификации. Как findParentTag, но с учётом classNames и статичных атрибутов и стилей, а не только тега.

Параметры

ПараметрТипОбязательныйПо умолчаниюОписание
specMarkSpecОбязательный—

Декларативное описание метки: тег плюс необязательные className, attributes и style.

fromNode—selection start container

Узел, с которого начинается поиск вверх. По умолчанию это стартовый контейнер текущего выделения.

TypeScript
const wrapper = editor.marks.find({ tag: 'mark' });

if (wrapper) {
  editor.selection.expandToTag(wrapper);
}

marks.read(spec, range?)

MarkSnapshot | null

Прочитать текущие значения объявленных в спецификации свойств из обёртки в начале диапазона. Возвращает null, когда диапазон не находится внутри подходящей обёртки. Снимок несёт найденный элемент, а также его объявленные style-свойства и атрибуты. Незаданные и прозрачные по значению style-свойства опускаются.

Когда использовать

Стройте UI инструмента от документа: предвыберите текущий цвет в палитре до того, как пользователь его сменит.

Параметры

ПараметрТипОбязательныйПо умолчаниюОписание
specMarkSpecОбязательный—

Декларативное описание метки: тег плюс необязательные className, attributes и style.

rangeRange—current selection

Диапазон, из которого читать. По умолчанию это первый диапазон живого выделения.

TypeScript
const colorMark = {
  tag: 'mark',
  style: { color: (state) => state.color },
};

// Preselect the current colour in a picker UI
const snapshot = editor.marks.read(colorMark);
const current = snapshot?.style['color']; // e.g. 'rgb(37, 99, 235)'

marks.apply(spec, state?, range?)

HTMLElement[]

Обернуть диапазон меткой или обновить подходящие обёртки на месте.

  • Разрезает частично покрытые обёртки той же семьи на границах диапазона.
  • Расширяет диапазон на замыкающий пробел, который браузеры исключают из выделения по двойному клику.
  • Оставляет новое содержимое выделенным.
  • Возвращает созданные или обновлённые элементы-обёртки.

Схлопнутый диапазон (голый курсор, когда ничего не выделено) это пустая операция: apply возвращает пустой массив, не трогая ни DOM, ни выделение.

Когда использовать

Идемпотентен в рамках семьи: повторное применение с новым state обновляет ТУ ЖЕ обёртку на месте. Поэтому палитра остаётся одной меткой, а не вложенными отменами.

Параметры

ПараметрТипОбязательныйПо умолчаниюОписание
specMarkSpecОбязательный—

Декларативное описание метки: тег плюс необязательные className, attributes и style.

stateState——

Значение, передаваемое свойствам атрибутов и стилей спецификации, заданным в виде функции.

rangeRange—current selection

Диапазон для форматирования. По умолчанию это первый диапазон живого выделения.

TypeScript
editor.marks.apply(colorMark, { color: '#d97706' });

// Same identity → the SAME wrapper is updated in place, not nested
editor.marks.apply(colorMark, { color: '#2563eb' });

marks.remove(spec, range?)

HTMLElement[]

Удаляет объявленные в спецификации свойства и классы из обёрток в диапазоне и снимает обёртки, оставшиеся пустыми.

  • Для несхлопнутого диапазона частично покрытые обёртки разрезаются, чтобы текст за пределами диапазона сохранил своё форматирование.
  • Схлопнутый курсор вместо этого целиком берёт охватывающую его обёртку: разреза не происходит, и свойства спецификации снимаются со всей обёртки.

Выделение восстанавливается в обоих случаях. Возвращает обёртки, которые уцелели, потому что всё ещё несут другие свойства.

Когда использовать

Удаляются только свойства самой спецификации. Обёртка, разделяемая с другой меткой семьи, выживает с остальными свойствами.

Параметры

ПараметрТипОбязательныйПо умолчаниюОписание
specMarkSpecОбязательный—

Декларативное описание метки: тег плюс необязательные className, attributes и style.

rangeRange—current selection

Диапазон для снятия форматирования. По умолчанию это первый диапазон живого выделения.

TypeScript
editor.marks.remove(colorMark);
// A <mark> that also carried a background-colour spec survives
// with only the colour stripped

marks.toggle(spec, state?, range?)

boolean

Вызывает remove, когда диапазон уже несёт метку, и apply в остальных случаях. Возвращает получившееся состояние: true, когда метка теперь применена.

При схлопнутом курсоре без уже имеющейся метки возвращает true, ничего не применив, потому что apply на схлопнутом диапазоне это пустая операция. Вызывайте toggle на несхлопнутом диапазоне или считайте возвращаемое значение намеренным состоянием, а не подтверждением.

Когда использовать

Однострочник для простых инструментов-переключателей. В паре с has() даёт активное состояние.

Параметры

ПараметрТипОбязательныйПо умолчаниюОписание
specMarkSpecОбязательный—

Декларативное описание метки: тег плюс необязательные className, attributes и style.

stateState——

Значение, передаваемое свойствам атрибутов и стилей спецификации, заданным в виде функции.

rangeRange—current selection

Диапазон для переключения. По умолчанию это первый диапазон живого выделения.

TypeScript
const highlight = { tag: 'span', className: 'my-highlight' };

const nowApplied = editor.marks.toggle(highlight);
TypeScript
// One spec = one mark. Static values (tag, className, attribute/style
// strings) form the mark's identity; function-form values do not.
const textColor = {
  tag: 'mark',
  style: { color: (state) => state.color },
};
const bgColor = {
  tag: 'mark',
  style: { 'background-color': (state) => state.color },
};

// Range-aware: splits partially-covered wrappers at the boundaries,
// updates fully-covering wrappers in place, restores the selection
editor.marks.apply(textColor, { color: '#2563eb' });

// Same family (same tag, no conflicting statics) → composes on ONE element
editor.marks.apply(bgColor, { color: '#fef3c7' });
// → <mark style="color: #2563eb; background-color: #fef3c7">…</mark>

// Derive the sanitizer rule the mark produces for a vanilla inline tool
import { markSanitizerConfig } from '@bloklabs/core';

class TextColorTool {
  static get sanitize() {
    return markSanitizerConfig(textColor);
  }
}