В современной разработке на платформе 1С:Предприятие 8.3 часто возникает необходимость интеграции учетной системы с веб-технологиями. Поле HTML-документа на управляемой форме позволяет не просто отображать веб-страницы, но и взаимодействовать с ними: запускать JavaScript-код из 1С, передавать параметры и получать результаты обратно. Однако разработчики часто сталкиваются с ошибками доступа к объектам DOM, особенно после обновления платформы. В этой статье мы подробно разберем, как правильно организовать двусторонний обмен данными между 1С и JavaScript, почему перестали работать старые методы и как обойти ограничения безопасности браузеров.
Если вы пытаетесь использовать старые примеры времен платформы 8.2 или ранних версий 8.3, вы можете столкнуться с тем, что свойство Документ у Поля HTML-документа отсутствует или вызывает ошибку выполнения. Давайте выясним причину этого поведения.
Дело в том, что начиная с версии 8.3.14.1565, фирма «1С» перевела встроенный браузер в тонком и толстом клиентах под Windows с устаревшего движка Internet Explorer (MSHTML) на современный кроссплатформенный движок WebKit. Это кардинально изменило внутреннюю объектную модель. Многие привычные COM-свойства, такие как parentWindow, стали недоступны или ведут себя иначе в зависимости от операционной системы и режима работы клиента. Для обеспечения стабильной работы нам необходимо использовать универсальные методы обращения к элементам веб-страницы.
Рассмотрим классическую задачу: на форме 1С размещено Поле HTML-документа, в которое загружен макет с веб-страницей. Нам нужно из кода 1С вызвать JS-функцию, например zako(), которая определена на этой странице.
Для того чтобы обращение к глобальному контексту окна (объекту window в JS) работало как на старых версиях платформы (через Internet Explorer), так и на новых (через WebKit), мы должны написать универсальную функцию получения контекста. В современных версиях WebKit вместо свойства parentWindow следует использовать свойство defaultView.
Разберем по шагам, как это реализовать в модуле управляемой формы:
&НаКлиенте
Процедура ВызватьСкриптИз1С(Команда)
// Получаем DOM-документ из элемента формы
ДокументHTML = Элементы.ПолеHTML.Документ;
Если ДокументHTML = Неопределено Тогда
ВызватьИсключение "Документ HTML еще не загружен!";
КонецЕсли;
// Определяем контекст окна (window) в зависимости от движка рендеринга
КонтекстОкна = Неопределено;
Если ДокументHTML.parentWindow <> Неопределено Тогда
// Вариант для старого движка IE
КонтекстОкна = ДокументHTML.parentWindow;
ИначеЕсли ДокументHTML.defaultView <> Неопределено Тогда
// Вариант для нового движка WebKit
КонтекстОкна = ДокументHTML.defaultView;
КонецЕсли;
// Если контекст успешно получен, вызываем JS-функцию zako()
Если КонтекстОкна <> Неопределено Тогда
Попытка
КонтекстОкна.zako();
Исключение
Сообщить("Не удалось вызвать функцию zako: " + ОписаниеОшибки());
КонецПопытки;
Иначе
Сообщить("Не удалось определить контекст окна веб-страницы.");
EndЕсли;
КонецПроцедуры
Обратите внимание: обращаться к свойству Документ элемента формы можно только тогда, когда страница полностью загружена. Лучше всего делать это в обработчике события поля HTML-документа ДокументСформирован.
Теперь проанализируем обратную задачу: как передать управление или данные из кода JavaScript обратно в 1С. Существует два принципиально разных подхода.
Подход А. Прямая передача контекста формы (работает только в старом IE)
В старых версиях платформы мы могли передать объект 1С ЭтаФорма напрямую в переменную JavaScript. Это позволяло вызывать экспортные методы формы прямо из JS-кода. Посмотрим на этот пример кода:
// Код в 1С при формировании документа
&НаКлиенте
Процедура ПолеHTMLДокументСформирован(Элемент)
Документ = Элементы.ПолеHTML.Документ;
// Передаем контекст формы в глобальную переменную JS
Документ.parentWindow.SetForm1C(ЭтаФорма);
КонецПроцедуры
// Экспортный метод формы 1С, который мы хотим вызвать из JS
&НаКлиенте
Процедура ПринятьДанныеИзJS(ЗначениеПараметра) Экспорт
ПоказатьПредупреждение(, "Получены данные из JS: " + ЗначениеПараметра);
КонецПроцедуры
На стороне HTML-страницы JavaScript-код выглядел следующим образом:
var Form1C;
function SetForm1C(aForm1C) {
Form1C = aForm1C;
}
// Вызов метода 1С при наступлении события в браузере
function SendTo1C(data) {
if (Form1C) {
Form1C.ПринятьДанныеИзJS(data);
}
}
Важное предупреждение: этот способ абсолютно несовместим с WebKit (начиная с платформы 8.3.14). Новый движок изолирует среду выполнения JS, и прямая передача COM-объекта 1С вызывает критическую ошибку или возвращает undefined.
Подход Б. Кроссплатформенный перехват событий (рекомендуемый метод для WebKit)
Для того чтобы настроить надежную обратную связь на современных версиях 1С, мы должны использовать перехват событий DOM. Суть метода заключается в том, что JS-код изменяет состояние какого-либо элемента на странице (например, генерирует клик по скрытой ссылке или кнопке), а платформа 1С перехватывает это событие в своем обработчике.
Давайте посмотрим, как это реализовать. На нашей HTML-странице создадим скрытый элемент или просто повесим обработчик клика, который будет передавать данные в атрибуты элемента:
<!-- HTML элемент для отправки данных в 1С -->
<button id="senderBtn" style="display:none;"></button>
<script>
function SendDataTo1C(payload) {
var btn = document.getElementById('senderBtn');
// Записываем передаваемые данные в пользовательский атрибут
btn.setAttribute('data-payload', JSON.stringify(payload));
// Эмулируем клик по кнопке
btn.click();
}
</script>
В конфигураторе 1С для элемента формы ПолеHTML назначим обработчик события ПриНажатии. Этот обработчик срабатывает при любом клике на странице, включая программную эмуляцию клика из JS:
&НаКлиенте
Процедура ПолеHTMLПриНажатии(Элемент, ДанныеСобытия, СтандартнаяОбработка)
// Проверяем, что клик совершен именно по нашей скрытой кнопке отправки
Если ДанныеСобытия.Element.id = "senderBtn" Тогда
// Отменяем стандартное поведение браузера на клик
СтандартнаяОбработка = Ложь;
// Извлекаем переданные данные из атрибута "data-payload"
ПереданныеДанныеСтрока = ДанныеСобытия.Element.getAttribute("data-payload");
// Обрабатываем полученные данные (например, парсим JSON)
Если ЗначениеЗаполнено(ПереданныеДанныеСтрока) Тогда
ВыполнитьОбработкуДанныхИзJS(ПереданныеДанныеСтрока);
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот метод гарантированно работает на любой версии платформы 1С, под любыми операционными системами (Windows, Linux, macOS) и во всех режимах работы клиентов (тонкий, веб-клиент).
Иногда перед разработчиком встает задача управлять не собственным макетом HTML, а сторонним ресурсом (например, загрузить в Поле HTML страницу поисковой системы или внешнего сервиса). При попытке обратиться к parentWindow или defaultView на таких сайтах возникает ошибка безопасности: доступ заблокирован.
Выясним причину этой проблемы. Современные веб-браузеры (включая WebKit в 1С) реализуют стандарт безопасности Same-Origin Policy (SOP). Он запрещает скриптам, запущенным из одного источника (например, из локального контекста 1С), получать доступ к свойствам и методам документа, загруженного с другого домена (например, с внешнего веб-сайта). Платформа 1С строго следует этим правилам.
Как же обойти это ограничение, если нам действительно необходимо программно взаимодействовать с внешним сайтом? Рассмотрим эффективный алгоритм обхода:
HTTPСоединение.<script> с необходимыми функциями перед закрывающим тегом </body>.УстановитьТекст()).Поскольку теперь страница считается загруженной из локального источника 1С, политика Same-Origin Policy больше не ограничивает наш доступ к DOM-модели. Мы можем свободно вызывать любые внедренные функции через defaultView.
Если вам требуется выполнить произвольный JavaScript-код «на лету» без предварительного объявления функций в макете, прямое использование функции eval() в контексте окна может блокироваться внутренними политиками безопасности WebKit.
Вместо этого мы рекомендуем использовать динамическое создание тега <script> прямо в DOM-дереве документа. Это самый чистый и безопасный способ внедрения исполняемого кода. Давайте посмотрим на практический пример реализации такой процедуры в 1С:
&НаКлиенте
Процедура ВыполнитьПроизвольныйJS(ТекстСкрипта)
Документ = Элементы.ПолеHTML.Документ;
Если Документ = Неопределено Тогда
Возврат;
КонецЕсли;
// Создаем новый HTML-элемент "script"
НовыйСкрипт = Документ.createElement("script");
НовыйСкрипт.type = "text/javascript";
// Записываем наш JS-код внутрь созданного элемента
НовыйСкрипт.innerText = ТекстСкрипта;
// Добавление тега в секцию head автоматически запускает его выполнение
Документ.head.appendChild(НовыйСкрипт);
// Опционально: удаляем элемент после выполнения, чтобы не засорять DOM
Документ.head.removeChild(НовыйСкрипт);
КонецПроцедуры
С помощью этого метода вы можете выполнять абсолютно любые сценарии на веб-странице, динамически генерируя JS-код в коде 1С в зависимости от текущего контекста и данных информационной базы.