Как скопировать текст в буфер обмена в веб-клиенте 1С?

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

Разработчики при проектировании интерфейсов 1С часто сталкиваются с задачей копирования текстовой информации в буфер обмена операционной системы по нажатию одной кнопки. Если в тонком или толстом клиенте под управлением ОС Windows можно было обойтись простым использованием COM-объекта htmlfile, то в веб-клиенте (особенно в различных браузерах) этот подход не работает. Браузерные политики безопасности накладывают жесткие ограничения на прямой доступ к системному буферу обмена.

В данной статье мы подробно разберем, как решить проблему копирования текста в буфер обмена в веб-клиенте. Мы рассмотрим несколько подходов: от классического обходного пути с использованием Поля HTML-документа и JavaScript до современного нативного механизма, появившегося в платформе 1С:Предприятие.

Метод 1. Нативное решение в платформе 1С (начиная с версии 8.3.24)

Если вы работаете на современных версиях платформы 1С:Предприятие (начиная с 8.3.24), вам больше не требуются сложные манипуляции с HTML-полями и сторонним кодом JavaScript. Фирма «1С» добавила полноценный встроенный механизм для работы с системным буфером обмена, который функционирует во всех типах клиентов, включая веб-клиент.

Давайте разберем пример реализации этого метода по шагам. Для копирования используется глобальный встроенный объект СредстваБуфераОбмена. Поскольку работа с буфером в веб-среде является потенциально длительной и требует подтверждения прав безопасности у пользователя, вызовы выполняются асинхронно.

Проанализируем следующий пример программного кода:


&НаКлиенте
Асинх Процедура СкопироватьВБуферНативно(ТекстДляКопирования)
    
    // Шаг 1. Проверяем доступность буфера в текущем окружении
    Если СредстваБуфераОбмена.ИспользованиеДоступно() Тогда
        
        // Шаг 2. Создаем элемент буфера обмена с указанием формата данных
        ДанныеБуфера = Новый ЭлементБуфераОбмена(СтандартныйФорматДанныхБуфераОбмена.Текст, ТекстДляКопирования);
        
        // Шаг 3. Асинхронно помещаем данные в буфер обмена
        Попытка
            Результат = Ждать СредстваБуфераОбмена.ПоместитьДанныеАсинх(ДанныеБуфера);
            ПоказатьПредупреждение(, "Текст успешно скопирован в буфер обмена!");
        Исключение
            ИнформацияОбОшибке = ИнформацияОбОшибке();
            ПоказатьПредупреждение(, "Не удалось скопировать данные: " + КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
        КонецПопытки;
        
    Иначе
        ПоказатьПредупреждение(, "Работа с буфером обмена не поддерживается в данном клиенте или браузере.");
    ЕслиКонец;
    
КонецПроцедуры

Использование встроенного объекта СредстваБуфераОбмена — это наиболее надежный, безопасный и перспективный метод. Он автоматически учитывает особенности безопасности браузеров и не требует создания фиктивных элементов на форме.

Метод 2. Использование Поля HTML-документа и JavaScript (для старых версий платформ)

Если ваша база работает на платформе версии ниже 8.3.24, вам придется использовать обходной путь через интеграцию JavaScript в контексте управляемой формы 1С. Для этого применяется элемент формы Поле HTML документа.

Давайте выясним причину, почему простая попытка вызвать JS-код напрямую из 1С может завершиться ошибкой вида 'document' - определение отсутствует. Дело в том, что объект document существует только внутри контекста веб-страницы (DOM-модели браузера). Из программного кода 1С напрямую обратиться к нему нельзя. Нам нужно загрузить HTML-страницу со встроенной JS-функцией внутрь Поля HTML-документа и вызывать ее через обращение к документу этого поля.

Выполним интеграцию по шагам:

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

Рассмотрим структуру загружаемого HTML-документа. Мы применим классический сценарий создания временного элемента ввода input, программного фокуса на нем, вызова команды копирования и последующего удаления элемента:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    // Заполняем HTML-код со встроенной JS-функцией
    ДанныеHTML = 
    "<!DOCTYPE HTML>" + 
    "<html>" + 
    "<head>" + 
    "   <meta charset=""utf-8"">" + 
    "   <script>" + 
    "       function copyToClipboard(text) {" + 
    "           var copytext = document.createElement('textarea');" + 
    "           copytext.value = text;" + 
    "           document.body.appendChild(copytext);" + 
    "           copytext.select();" + 
    "           try {" + 
    "               document.execCommand('copy');" + 
    "           } catch (err) {" + 
    "               console.error('Ошибка копирования:', err);" + 
    "           }" + 
    "           document.body.removeChild(copytext);" + 
    "       }" + 
    "   </script>" + 
    "</head>" + 
    "<body style=""margin:0; padding:0; overflow:hidden;"">" + 
    "</body>" + 
    "</html>";
    
КонецПроцедуры

Теперь для копирования текста нам достаточно вызвать JS-функцию copyToClipboard из модуля управляемой формы 1С. Разберем, как выглядит этот вызов при нажатии кнопки на форме:


&НаКлиенте
Процедура КомандаКопироватьТекст(Команда)
    
    ТекстДляКопирования = "Этот текст попадет в буфер обмена из веб-клиента!";
    
    // Получаем DOM-документ Поля HTML документа и вызываем функцию
    ДокументHTML = Элементы.ПолеHTMLБуфер.Документ;
    Если ДокументHTML <> Неопределено Тогда
        // Обращаемся к родительскому окну (window) для вызова глобальной JS-функции
        ДокументHTML.defaultView.copyToClipboard(ТекстДляКопирования);
    КонецЕсли;
    
КонецПроцедуры

Важное примечание по совместимости: В коде выше вместо устаревшего свойства ParentWindow мы использовали стандартное свойство defaultView, которое поддерживается всеми современными браузерами и старым IE 11.

Решение проблемы видимости Поля HTML-документа

При использовании Метода 2 разработчики часто сталкиваются со следующей проблемой: Поле HTML-документа нужно только как «технический контейнер» для запуска JS-скрипта. Показывать его пользователю на форме нет никакой необходимости. Однако, если у элемента формы установить свойство Видимость = Ложь, то платформа 1С в целях оптимизации рендеринга вообще не будет создавать данный элемент в DOM-дереве веб-страницы. В результате свойство Элементы.ПолеHTMLБуфер.Документ вернет Неопределено, и вызвать функцию копирования станет невозможно.

Проанализируем способы обхода этого ограничения. Нам нужно сделать элемент невидимым для глаз пользователя, но сохранить его присутствие в интерфейсе для платформы. Выделим три эффективных варианта:

  1. Минимизация размеров элемента. Оставьте свойство Видимость в значении Истина. В конфигураторе или программно сожмите элемент до минимальных размеров. Установите свойства:
    • Ширина = 1;
    • Высота = 1;
    • РастягиватьПоГоризонтали = Ложь;
    • РастягиватьПоВертикали = Ложь.
    Элемент сожмется в крошечную точку на форме, которая визуально сольется с фоном интерфейса, но сохранит полную работоспособность.
  2. Скрытие средствами CSS внутри самого HTML. Сделайте само Поле HTML на форме небольшим (например, разместите в самом низу формы), но в загружаемый HTML-код добавьте стили, скрывающие отображение тела страницы. Например: <body style="display: none;"> Или задайте абсолютное позиционирование контейнера за пределами видимого экрана: <body style="position: absolute; left: -9999px;">
  3. Размещение на неактивной вкладке. Поместите Поле HTML-документа на служебную страницу элемента формы типа «Группа - Страницы». Выберите вкладку, которая не показывается пользователю по умолчанию. На многих версиях платформы 1С это позволяет объекту корректно инициализироваться в оперативной памяти браузера, не загромождая основную рабочую область пользователя.

Метод 3. Современный JavaScript API (Clipboard API)

Если вы хотите использовать современный веб-стандарт, то следует помнить, что метод document.execCommand('copy') официально признан устаревшим (хотя и поддерживается большинством браузеров ради обратной совместимости). Современным стандартом является асинхронный Clipboard API.

Его использование делает JS-код значительно чище, так как отпадает необходимость создавать временные теги input или textarea в документе. Вместо этого применяется метод navigator.clipboard.writeText().

Посмотрим на пример современной функции JavaScript, которую вы можете встроить в свое Поле HTML-документа:


async function copyToClipboardModern(text) {
    try {
        await navigator.clipboard.writeText(text);
        console.log('Текст успешно скопирован через Clipboard API');
    } catch (err) {
        console.error('Не удалось скопировать данные: ', err);
        // Резервный вариант на случай ограничений безопасности
        fallbackCopyToClipboard(text);
    }
}

function fallbackCopyToClipboard(text) {
    var copytext = document.createElement('textarea');
    copytext.value = text;
    document.body.appendChild(copytext);
    copytext.select();
    document.execCommand('copy');
    document.body.removeChild(copytext);
}

Важное предупреждение (HTTPS-ограничение): Современный Clipboard API в целях безопасности работает исключительно в безопасном контексте (Secure Context). Это означает, что браузер разрешит его использование только в том случае, если ваш веб-клиент 1С запущен по протоколу HTTPS или на локальном сервере (localhost). Если публикация информационной базы использует обычный протокол HTTP, свойство navigator.clipboard вернет undefined. Именно поэтому в коде выше рекомендуется использовать конструкцию try-catch с откатом к классическому (резервному) методу создания временного поля ввода.

Подводя итог, при разработке конфигураций 1С рекомендуется всегда проверять версию платформы. Если у вас развернута версия 8.3.24 и выше — используйте нативный объект СредстваБуфераОбмена. Если же приходится поддерживать более ранние релизы, оптимальным решением станет интеграция HTML-поля со скрытием его размеров и вызовом гибридной JavaScript-функции.

← На главную