Почему JavaScript в поле HTML документа 1С срабатывает дважды и как это исправить?

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

При работе с механизмом Поле HTML документа на платформе 1С:Предприятие разработчики часто сталкиваются со странным поведением: код JavaScript, прописанный внутри тегов script, выполняется два раза. Например, вместо одного модального окна alert пользователь видит два, или данные в документе дублируются. Особенно часто это проявляется на версиях платформы до 8.3.14, где в качестве движка отображения используется Internet Explorer (MSHTML).

В этой статье мы подробно разберем, почему это происходит, и рассмотрим несколько надежных способов решения проблемы — от простых "костылей" до правильной архитектуры взаимодействия JS и 1С.

Выясним причину: почему скрипт запускается дважды?

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

  1. Инициализация контейнера: Платформа подготавливает среду для отображения (часто загружая пустой шаблон about:blank).
  2. Запись потока (Stream): Содержимое передается в объект управления браузером. В некоторых реализациях платформы это вызывает повторную интерпретацию блока script: первый раз при разборе (парсинге) документа и второй раз при окончательной отрисовке формы и связывании данных.
  3. Особенности MSHTML: Старый движок Internet Explorer может перезапускать скрипты при определенных манипуляциях с DOM-деревом, которые 1С выполняет "под капотом" для обеспечения интеграции.

Использование метода document.write, который мы видим в примере автора, только усугубляет ситуацию. Этот метод напрямую пишет в поток вывода документа, и если он вызывается в момент неопределенного состояния готовности страницы, это гарантированно приводит к визуальным артефактам и дублированию.

Решение 1: Использование события window.onload

Самый простой и действенный способ избежать преждевременного выполнения кода — дождаться полной загрузки объектной модели документа (DOM). Если мы вынесем логику в обработчик события onload, браузер выполнит её только один раз, когда документ будет полностью сформирован в памяти.

Рассмотрим, как изменить HTML-код. Вместо прямого вызова внутри тега script, используем следующий подход:


|<html>
|<head>
|  <script>
|    window.onload = function() {
|        // Ваш код здесь
|        var p = document.createElement('p');
|        p.innerText = 'qwerty';
|        document.body.appendChild(p);
|    };
|  </script>
|</head>
|<body>
|</body>
|</html>

Также можно использовать атрибут onload непосредственно в теге body. Это решение считается более "топорным", но в условиях 1С оно работает безотказно:


|<body onload="console.log('Выполнено один раз');">
|  <p>Контент страницы...</p>
|</body>

Решение 2: Защита от повторного выполнения через флаг

Если специфика вашей задачи не позволяет ждать полной загрузки страницы (например, скрипт должен подготовить данные для рендеринга), можно применить технику "защелки" или флага. Мы создаем глобальную переменную в контексте window и проверяем её наличие перед выполнением кода.

Разберем пример реализации такой защиты:


|<script>
|  if (typeof window.alreadyRun === 'undefined') {
|      window.alreadyRun = true;
|      
|      // Основная логика скрипта
|      console.log('Этот код выполнится только один раз');
|      // ...
|  }
|</script>

Поскольку при повторном "прогоне" скрипта переменная alreadyRun уже будет существовать в глобальной области видимости текущей страницы, блок кода внутри if будет проигнорирован.

Решение 3: Программный вызов JS из кода 1С

Наиболее современный и правильный подход — не вшивать исполняемую логику в сам текст HTML, а вызывать JS-функции из кода 1С тогда, когда это действительно нужно. В новых версиях платформы (8.3.15+) для этого предназначен метод ИсполнитьJavaScript(), но в версии 8.3.13 мы можем использовать объектную модель документа.

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


|<!DOCTYPE html>
|<html>
|<head>
|    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
|    <script>
|        function CalculateData(param) {
|            // Выполняем полезную работу
|            var result = "Обработано: " + param;
|            document.getElementById("resultField").innerHTML = result;
|            return result;
|        }
|    </script>
|</head>
|<body>
|    <div id="resultField">Ожидание данных...</div>
|</body>
|</html>

Затем в коде 1С (например, в обработчике нажатия кнопки на форме) вызовем эту функцию напрямую через parentWindow (для старых движков) или через коллекцию scripts. Однако самый стабильный способ для 8.3.13 — использование метода eval через объект Документ:


&НаКлиенте
Процедура ВыполнитьJS(Команда)
    // Получаем COM-объект документа
    ДокументHTML = Элементы.ПолеHTMLДокумента.Документ;
    Если ДокументHTML = Неопределено Тогда
        Возврат;
    КонецЕсли;

    // Вызываем функцию JS
    Попытка
        ДокументHTML.parentWindow.eval("CalculateData('Привет из 1С!')");
    Исключение
        Сообщить("Ошибка выполнения JS: " + ОписаниеОшибки());
    КонецПопытки;
КонецПроцедуры

Сложное взаимодействие: передача данных и получение результата

Иногда нужно не просто запустить скрипт, а передать в него массив данных, дождаться обработки и вернуть результат обратно в 1С. Рассмотрим пример с использованием скрытых элементов (буферов), который упоминался в обсуждении на форуме. Это отличный способ обойти ограничения безопасности и избежать двойного срабатывания.

В HTML создается скрытый div для хранения результата и кнопка (или другой элемент), событие которого мы будем "имитировать" из 1С:


|<!-- Внутри HTML -->
|<div id="BufferData" style="display:none"></div>
|<button id="HiddenButton" style="display:none" onclick="document.getElementById('BufferData').innerHTML = myComplexFunction();"></button>

В 1С мы программно нажимаем на эту кнопку и считываем значение из буфера:


&НаКлиенте
Функция ПолучитьДанныеИзJS()
    Документ = Элементы.ПолеHTMLДокумента.Документ;
    
    // Эмулируем клик для выполнения логики
    Кнопка = Документ.getElementById("HiddenButton");
    Кнопка.click(); 
    
    // Забираем результат из невидимого поля
    Результат = Документ.getElementById("BufferData").innerHTML;
    Возврат Результат;
КонецФункции

Важные рекомендации по настройке Поля HTML документа

Проанализируем еще несколько технических моментов, которые помогут сделать работу с JS в 1С стабильной:

Подводя итог, можно сказать: двойное срабатывание скриптов — это не ошибка вашего кода, а особенность взаимодействия 1С с системным компонентом браузера. Используя событие window.onload или программный вызов функций через объектную модель Документ, вы обеспечите надежную и предсказуемую работу вашего интерфейса.

← На главную