При разработке интерфейсов в 1С (например, встроенных чатов, панелей переписки, дашбордов или просмотрщиков форматированного текста) часто используется элемент формы ПолеHTMLДокумента. Однако при попытке программно взаимодействовать с его внутренней структурой через свойство Документ разработчики нередко сталкиваются с ошибкой времени выполнения:
Ошибка при получении значения атрибута контекста (Документ): HTML документ не готов
Разберем детально, почему возникает эта ошибка, как устроен жизненный цикл встроенного веб-компонента и какими способами можно безопасно передавать и обновлять данные в поле HTML.
Проанализируем ситуацию, которая приводит к сбою в коде. Типичный сценарий выглядит следующим образом: при открытии формы или программном создании вкладки в элемент передается базовый шаблон страницы через метод УстановитьТекст():
Процедура ПервоначальноеЗаполнениеПоляHTML(ЭлементФормы) Экспорт
ТекстПоля = ПолучитьБлокТекстаПоляHTML("Заголовок");
ТекстПоля = ТекстПоля + ПолучитьБлокТекстаПоляHTML("Подвал");
ЭлементФормы.УстановитьТекст(ТекстПоля);
КонецПроцедуры
Сразу после этого разработчик пытается манипулировать объектной моделью документа (DOM) через COM-интерфейс:
СтраницаПоля = Элементформы.Документ.getElementsByTagName("body").item(0);
ЭлементДляДобавления = Элементформы.Документ.CreateElement("div");
ЭлементДляДобавления.innerHTML = ТекстПоля;
СтраницаПоля.appendChild(ЭлементДляДобавления);
В этот момент и генерируется исключение. Выясним главные причины:
УстановитьТекст() инициирует загрузку разметки асинхронно через внутренние системные очереди сообщений. Вызов метода лишь отправляет команду браузерному движку. На момент выполнения следующей строки кода внутренний COM-объект HTMLDocument еще физически не успел распарсить переданный текст и построить дерево элементов. Свойство Документ в этот момент не инициализировано.Панель, не получают системного дескриптора окна (HWND) до того момента, пока пользователь не переключится на соответствующую вкладку. Встроенный браузер на скрытой вкладке находится в замороженном состоянии и вообще не запускает загрузку документа.Рассмотрим наиболее надежный и кроссплатформенный подход. Манипуляции с DOM через COM-объект Документ (методы CreateElement, appendChild) создают сильную зависимость от готовности компонента и работают исключительно в толстом клиенте под управлением Windows.
Для задач отображения переписки и лент событий намного проще и безопаснее оперировать готовым строковым представлением HTML. Для этого достаточно сохранять текущее состояние текста страницы или производить вставку новых фрагментов перед закрывающим тегом </BODY>.
Посмотрим на пример процедуры добавления сообщений без обращения к COM-свойству Документ:
Процедура ДобавитьНовоеСообщениеВПолеHTML(ЭлементФормы, ТекстНовогоСообщения) Экспорт
ИмеющийсяТекст = ЭлементФормы.ПолучитьТекст();
// Если документ еще не был инициализирован базовым каркасом
Если ПустаяСтрока(ИмеющийсяТекст) Тогда
БазовыйТекст = ПолучитьБлокТекстаПоляHTML("Заголовок")
+ ТекстНовогоСообщения
+ ПолучитьБлокТекстаПоляHTML("Подвал");
ЭлементФормы.УстановитьТекст(БазовыйТекст);
Возврат;
КонецЕсли;
// Вставляем новое сообщение перед закрывающими тегами
Если Найти(ИмеющийсяТекст, "</BODY>") > 0 Тогда
НовыйТекст = СтрЗаменить(ИмеющийсяТекст, "</BODY>", ТекстНовогоСообщения + "</BODY>");
ИначеЕсли Найти(ИмеющийсяТекст, "</body>") > 0 Тогда
НовыйТекст = СтрЗаменить(ИмеющийсяТекст, "</body>", ТекстНовогоСообщения + "</body>");
Иначе
НовыйТекст = ИмеющийсяТекст + ТекстНовогоСообщения;
КонецЕсли;
ЭлементФормы.УстановитьТекст(НовыйТекст);
КонецПроцедуры
Такой способ гарантирует успешную работу даже в тех случаях, когда вкладка с полем в данный момент не активна.
Если специфика задачи строго требует работы с COM-моделью документа (например, динамическое выполнение сложных JavaScript-сценариев или обращение к вычисляемым свойствам элементов DOM), обращаться к свойству Документ разрешено только после наступления события ДокументСформирован.
Разберем по шагам, как корректно настроить взаимодействие:
ПолеHTMLДокумента на форме и назначим обработчик для события ДокументСформирован. Проверьте, чтобы имя процедуры было явно привязано к событию элемента управления.
Процедура ПриОткрытии()
ПервоначальноеЗаполнениеПоляHTML(ЭлементыФормы.Страница1ПолеHTMLДокумента);
КонецПроцедуры
Процедура ПолеHTMLДокументаДокументСформирован(Элемент)
// В этой точке Документ гарантированно готов к работе
СтраницаПоля = Элемент.Документ.getElementsByTagName("body").item(0);
// Выполняем добавление узлов
ЭлементДляДобавления = Элемент.Документ.CreateElement("div");
ЭлементДляДобавления.innerHTML = "<div>Текст первого сообщения</div>";
СтраницаПоля.appendChild(ЭлементДляДобавления);
// Выполняем автоматическую прокрутку вниз
Попытка
Элемент.Документ.parentWindow.eval("window.scrollTo(0, document.body.scrollHeight)");
Исключение
// Игнорируем ошибки вызова скрипта, если окно недоступно
КонецПопытки;
КонецПроцедуры
Если на форме используется компонент Панель с множеством страниц (например, отдельная страница на каждого собеседника в чате), не следует пытаться инициализировать поля на всех страницах одновременно в цикле. Неактивные страницы вызовут ошибку готовности документа.
Применим принцип отложенной загрузки:
ТекущаяСтраница.ПриСменеСтраницы проверяем состояние поля выбранной страницы:
Процедура ПанельПриСменеСтраницы(Элемент, ТекущаяСтраница)
ИмяПоля = ТекущаяСтраница.Имя + "ПолеHTMLДокумента";
ЭлементПоля = ЭлементыФормы.Найти(ИмяПоля);
Если ЭлементПоля <> Неопределено Тогда
// Если поле еще не содержит разметки, инициализируем его
Если ПустаяСтрока(ЭлементПоля.ПолучитьТекст()) Тогда
ПервоначальноеЗаполнениеПоляHTML(ЭлементПоля);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Для предотвращения проблем с отображением разметки и обработкой кликов пользователя учтем следующие технические нюансы:
<HEAD> всегда добавляйте директиву совместимости <meta http-equiv="X-UA-Compatible" content="IE=edge">, чтобы встроенный компонент использовал современные стандарты рендеринга CSS и скриптов.onclick через COM-объекты часто завершается сбоем из-за различий в моделях событий браузеров. Вместо этого используйте стандартные гиперссылки с якорями (например, <a href="#ОткрытьИнформацию=123">) и обрабатывайте их в штатном событии поля 1С ПриНажатии, прерывая стандартный переход установкой параметра Отказ = Истина.