Почему возникает ошибка «HTML документ не готов» в поле HTML-документа 1С и как её исправить?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке интерфейсов в 1С (например, встроенных чатов, панелей переписки, дашбордов или просмотрщиков форматированного текста) часто используется элемент формы ПолеHTMLДокумента. Однако при попытке программно взаимодействовать с его внутренней структурой через свойство Документ разработчики нередко сталкиваются с ошибкой времени выполнения:

Ошибка при получении значения атрибута контекста (Документ): HTML документ не готов

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

Причины возникновения ошибки

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


Процедура ПервоначальноеЗаполнениеПоляHTML(ЭлементФормы) Экспорт
    ТекстПоля = ПолучитьБлокТекстаПоляHTML("Заголовок");
    ТекстПоля = ТекстПоля + ПолучитьБлокТекстаПоляHTML("Подвал");
    ЭлементФормы.УстановитьТекст(ТекстПоля);
КонецПроцедуры

Сразу после этого разработчик пытается манипулировать объектной моделью документа (DOM) через COM-интерфейс:


СтраницаПоля = Элементформы.Документ.getElementsByTagName("body").item(0);
ЭлементДляДобавления = Элементформы.Документ.CreateElement("div");
ЭлементДляДобавления.innerHTML = ТекстПоля;
СтраницаПоля.appendChild(ЭлементДляДобавления);

В этот момент и генерируется исключение. Выясним главные причины:

  1. Асинхронный парсинг DOM-дерева. Метод УстановитьТекст() инициирует загрузку разметки асинхронно через внутренние системные очереди сообщений. Вызов метода лишь отправляет команду браузерному движку. На момент выполнения следующей строки кода внутренний COM-объект HTMLDocument еще физически не успел распарсить переданный текст и построить дерево элементов. Свойство Документ в этот момент не инициализировано.
  2. Элемент управления расположен на неактивной странице панели. В интерфейсе обычных форм элементы, расположенные на скрытых страницах элемента Панель, не получают системного дескриптора окна (HWND) до того момента, пока пользователь не переключится на соответствующую вкладку. Встроенный браузер на скрытой вкладке находится в замороженном состоянии и вообще не запускает загрузку документа.
  3. Блокировка интерфейсного потока. Если инициализация и вывод данных выполняются в едином непрерывном цикле 1С без передачи управления системе, события завершения загрузки документа не могут быть обработаны операционной системой.

Способ 1. Отказ от прямого обращения к DOM в пользу строковой сборки

Рассмотрим наиболее надежный и кроссплатформенный подход. Манипуляции с DOM через COM-объект Документ (методы CreateElement, appendChild) создают сильную зависимость от готовности компонента и работают исключительно в толстом клиенте под управлением Windows.

Для задач отображения переписки и лент событий намного проще и безопаснее оперировать готовым строковым представлением HTML. Для этого достаточно сохранять текущее состояние текста страницы или производить вставку новых фрагментов перед закрывающим тегом </BODY>.

Посмотрим на пример процедуры добавления сообщений без обращения к COM-свойству Документ:


Процедура ДобавитьНовоеСообщениеВПолеHTML(ЭлементФормы, ТекстНовогоСообщения) Экспорт
    ИмеющийсяТекст = ЭлементФормы.ПолучитьТекст();
    
    // Если документ еще не был инициализирован базовым каркасом
    Если ПустаяСтрока(ИмеющийсяТекст) Тогда
        БазовыйТекст = ПолучитьБлокТекстаПоляHTML("Заголовок") 
                     + ТекстНовогоСообщения 
                     + ПолучитьБлокТекстаПоляHTML("Подвал");
        ЭлементФормы.УстановитьТекст(БазовыйТекст);
        Возврат;
    КонецЕсли;
    
    // Вставляем новое сообщение перед закрывающими тегами
    Если Найти(ИмеющийсяТекст, "</BODY>") > 0 Тогда
        НовыйТекст = СтрЗаменить(ИмеющийсяТекст, "</BODY>", ТекстНовогоСообщения + "</BODY>");
    ИначеЕсли Найти(ИмеющийсяТекст, "</body>") > 0 Тогда
        НовыйТекст = СтрЗаменить(ИмеющийсяТекст, "</body>", ТекстНовогоСообщения + "</body>");
    Иначе
        НовыйТекст = ИмеющийсяТекст + ТекстНовогоСообщения;
    КонецЕсли;
    
    ЭлементФормы.УстановитьТекст(НовыйТекст);
КонецПроцедуры

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

Способ 2. Использование события «ДокументСформирован»

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

Разберем по шагам, как корректно настроить взаимодействие:

  1. Откроем палитру свойств элемента ПолеHTMLДокумента на форме и назначим обработчик для события ДокументСформирован. Проверьте, чтобы имя процедуры было явно привязано к событию элемента управления.
  2. При открытии формы вызываем только первичную установку каркаса HTML:
    
    Процедура ПриОткрытии()
        ПервоначальноеЗаполнениеПоляHTML(ЭлементыФормы.Страница1ПолеHTMLДокумента);
    КонецПроцедуры
    
  3. Все операции добавления блоков и вызова скриптов переносим в сработавшее событие:
    
    Процедура ПолеHTMLДокументаДокументСформирован(Элемент)
        // В этой точке Документ гарантированно готов к работе
        СтраницаПоля = Элемент.Документ.getElementsByTagName("body").item(0);
        
        // Выполняем добавление узлов
        ЭлементДляДобавления = Элемент.Документ.CreateElement("div");
        ЭлементДляДобавления.innerHTML = "<div>Текст первого сообщения</div>";
        СтраницаПоля.appendChild(ЭлементДляДобавления);
        
        // Выполняем автоматическую прокрутку вниз
        Попытка
            Элемент.Документ.parentWindow.eval("window.scrollTo(0, document.body.scrollHeight)");
        Исключение
            // Игнорируем ошибки вызова скрипта, если окно недоступно
        КонецПопытки;
    КонецПроцедуры
    

Способ 3. Ленивая инициализация при переключении страниц панели

Если на форме используется компонент Панель с множеством страниц (например, отдельная страница на каждого собеседника в чате), не следует пытаться инициализировать поля на всех страницах одновременно в цикле. Неактивные страницы вызовут ошибку готовности документа.

Применим принцип отложенной загрузки:

  1. В момент открытия формы заполняем HTML-поле только для ТекущаяСтраница.
  2. В обработчике события панели ПриСменеСтраницы проверяем состояние поля выбранной страницы:
    
    Процедура ПанельПриСменеСтраницы(Элемент, ТекущаяСтраница)
        ИмяПоля = ТекущаяСтраница.Имя + "ПолеHTMLДокумента";
        ЭлементПоля = ЭлементыФормы.Найти(ИмяПоля);
        
        Если ЭлементПоля <> Неопределено Тогда
            // Если поле еще не содержит разметки, инициализируем его
            Если ПустаяСтрока(ЭлементПоля.ПолучитьТекст()) Тогда
                ПервоначальноеЗаполнениеПоляHTML(ЭлементПоля);
            КонецЕсли;
        КонецЕсли;
    КонецПроцедуры
    

Рекомендации по стабильной работе с HTML в 1С

Для предотвращения проблем с отображением разметки и обработкой кликов пользователя учтем следующие технические нюансы:

  1. Режим отображения браузера: в секцию <HEAD> всегда добавляйте директиву совместимости <meta http-equiv="X-UA-Compatible" content="IE=edge">, чтобы встроенный компонент использовал современные стандарты рендеринга CSS и скриптов.
  2. Обработка кликов по ссылкам: попытка перехвата событий вроде onclick через COM-объекты часто завершается сбоем из-за различий в моделях событий браузеров. Вместо этого используйте стандартные гиперссылки с якорями (например, <a href="#ОткрытьИнформацию=123">) и обрабатывайте их в штатном событии поля 1С ПриНажатии, прерывая стандартный переход установкой параметра Отказ = Истина.
← На главную