Все разработчики знают о теге <input>, это рабочая лошадка веба.

Но что такое <output>? Большинство его никогда не касались. Кто-то даже не подозревает о его существовании.

И очень жаль, ведь этот тег решает проблему, которую мы годами пытались решить связкой <div> и ARIA: динамические результаты, по умолчанию объявляемые программам для чтения экрана.

Этот тег уже много лет находится в спецификации, но почему-то скрывается у всех на виду.


Вот, что говорится в спецификации HTML5:

Элемент <output> представляет результат вычислений, выполняемых приложением, или результат действия пользователя.

В дереве accessibility он сопоставлен с role="status". Проще говоря, он объявляет о своём значении при его изменении, если уже имеет aria-live="polite" aria-atomic="true".

На практике это означает, что обновления не прерывают пользователя. Они считываются вскоре после действий, и при этом сообщается обо всём контенте, а не только об изменившейся части. При необходимости можно переопределить это поведение, задав собственные свойства ARIA.

Пользоваться им крайне просто:

<output>Здесь находится ваше динамическое значение</output>

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

Мой момент открытия

Я обнаружил <output> в accessibility-проекте с многоэтапной формой. Форма обновляла оценку риска в процессе изменения значений полей. В браузере она выглядела идеально, но пользователи программ для чтения экрана понятия не имели, что оценка обновляется.

Мы устранили проблему добавлением ARIA live region. Но я всегда считал, что в первую очередь следует использовать семантический HTML, а live region часто похожи на патч.

Тогда я начал изучать спецификацию и обнаружил <output>. Он понимает формы, не требуя их, и нативным образом объявляет об их изменениях. Оказалось, что самое простое решение проблемы всё это время скрывалось в спецификации.

Почему же мы его не используем?

Потому что мы о нём забыли. В большинстве туториалов о нём ничего не говорится. Он не выглядит, как нечто привлекательное. Когда я поискал по публичным репозиториям GitHub, то практически ни разу не встретил его.

Похоже, о нём не помнят и разработчики паттернов библиотек компонентов. Из-за этого возникает цикл обратной связи: если никто не обучает тегу, то им никто не пользуется.

Что нужно знать

Как и у <label>, у <output> есть атрибут for="". В нём мы через пробелы перечисляем id всех элементов <input>, от которых зависит результат:

<input id="a" type="number"> +
<input id="b" type="number"> =
<output for="a b"></output>

Для большинства пользователей визуально ничего не изменится. Но в дереве accessibility это создаст семантическую связь, позволяющую пользователям специальных возможностей соединять input с вычисляемым результатом.

Этот тег не требует <form>. Можно использовать его везде, где динамический текст страницы обновляется в зависимости от пользовательского ввода.

По умолчанию <output> встроен, поэтому обычно нужно стилизовать его под свою структуру страницы, как мы делаем это с <span> или <div>.

А поскольку тег добавлен в спецификацию ещё в 2008 году, он замечательно поддерживается во всех браузерах и программах для чтения экрана. Также он отлично взаимодействует с любыми JavaScript-фреймворками, например с React и Vue.

Дополнение: выяснилось, что некоторые программы для чтения экрана не обновляют изменения в теге, поэтому, вероятно, стоит явно указывать атрибут role, пока ситуация с поддержкой не улучшится: <output role="status">.

Стоит также отметить, что  <output> предназначен для результатов, связанных с пользовательским вводом и действиями, а не с глобальными уведомлениями наподобие всплывающих сообщений. Такие уведомления лучше обрабатывать при помощи role="status" или role="alert" в обобщённом элементе, потому что они представляют системную обратную связь, а не вычисляемый вывод.

Как же это выглядит на практике?

Примеры из реальных проектов

Лично я использовал <output> во многих реальных проектах:

Простое приложение-калькулятор

Во время недавнего 20-минутного челленджа по кодингу я использовал <output> для отображения результатов вычислений. Программа для чтения экрана объявляла каждый результат при обновлении без добавления каких-либо ARIA role. И не нужно никаких хаков.

Форматирование ползунка диапазона

В Volvo Cars мы отображаем удобные версии значений ползунков. Внутри ползунок может хранить 10000, а вывод показывать 10,000 miles/year. Мы обернули ползунок и <output> в контейнер с role="group" и общую метку, создав связный компонент React:

<div role="group" aria-labelledby="mileage-label">
  <label id="mileage-label" htmlFor="mileage">
    Annual mileage
  </label>
  <input
    id="mileage"
    name="mileage"
    type="range"
    value={mileage}
    onChange={(e) => setMileage(Number(e.target.value))}
  />
  <output name="formattedMileage" htmlFor="mileage">
    {mileage.toLocaleString()} miles/year
  </output>
</div>

Обратная связь валидации форм

Я выяснил, что индикаторы надёжности пароля и сообщения валидации в реальном времени замечательно работают с <output>.

<label for="password">Password</label>
<input type="password" id="password" name="password">
<output for="password">
  Password strength: Strong
</output>

Вывод вычисляется на сервере? Никаких проблем.

Тег <output> вписывается в даже современные паттерны, в которых получаются цены из API, отображается расчёт налогов или генерируемые на сервере рекомендации.

В примере ниже калькулятор стоимости доставки обновляет тег <output>, сообщая пользователям о том, когда стоимость вычислена:

export function ShippingCalculator() {
  const [weight, setWeight] = useState("");
  const [price, setPrice] = useState("");

  useEffect(() => {
    if (weight) {
      // Получаем с сервера стоимость доставки на основании веса посылки
      fetch(`/api/shipping?weight=${weight}`)
        .then((res) => res.json())
        .then((data) => setPrice(data.price));
    }
  }, [weight]);

  return (
    <form>
      <label>
        Package weight (kg):
        <input
          type="number"
          name="weight"
          value={weight}
          onChange={(e) => setWeight(e.target.value)}
        />
      </label>
      
      <output name="price" htmlFor="weight">
        {price ? `Estimated shipping: $${price}` : "Calculating..."}
      </output>
    </form>
  );
}

Качество гарантируется

Есть что-то приятное в том, чтобы использовать нативный элемент HTML для того, для чего он и был придуман, особенно если он повышает accessibilty интерфейса, требуя при этом меньше кода.

Наверно, <output> — самый большой секрет HTML, и обнаружение подобных сокровищ демонстрирует, как много полезного можно найти в спецификации.

Иногда лучший инструмент для задачи тот, о существовании которого вы не знали.

Дополнение: как всегда великолепный Боб Рудис написал страницу с работающими примерами для моего поста. Она находится здесь: https://rud.is/drop/output.html

Комментарии (3)


  1. patyupin
    17.10.2025 12:36

    Прикольно


  1. JerryI
    17.10.2025 12:36

    Было бы здорово, если бы браузер учитывал такой тег в оптимизации. Типа ага, там будет часто меняющееся содержимое...


  1. AlexMih
    17.10.2025 12:36

    Потому что мы о нём забыли

    Это то, что я вижу вокруг во многих стандартах IT, и это меня пугает. Стандарты настолько раздуты и усложнены, что уже недоступны для полного понимания одним человеком, а многие подразделы фактически мертвы. Неужели нам теперь придется специализироваться в подразделах языка разметки - вот специалист по формам, вот по таблицам, а вот по скругленным тоненьким рамкам? Но это же абсурд. Ведь, если вдуматься, задача форматирования контента на экране на самом деле не настолько сложна.

    В идеальном мире я бы хотел, чтобы следующий стандарт языка разметки, условный HTML+CSS 6.0, был бы радикально меньше и лаконичнее существующих. Интересно, повернет ли когда-нибудь эволюция в эту сторону, или доведет до того что все рухнет от сложности?