Предоставляет возможность определения идентичности (ссылаются на один и тот же объект) двух узлов.
Синтаксис
УзелИдентичен(<Узел>)Полное описание, параметры и ограничения
ДокументHTML.УзелИдентичен (HTMLDocument.IsSameNode)
ДокументHTML (HTMLDocument)
УзелИдентичен (IsSameNode)
Доступен, начиная с версии 8.2.
Синтаксис:
УзелИдентичен(<Узел>)
Параметры:
<Узел> (обязательный)
Тип: АтрибутHTML, ЭлементHTML, ЭлементКнопкаHTML, ЭлементВводаHTML, ЭлементЗаголовокHTML, ЭлементРазметкаHTML, ЭлементПлавающийФреймHTML, ЭлементВставкаHTML, ЭлементФреймHTML, ЭлементНаборФреймовHTML, ЭлементМетаHTML, ЭлементОбъектHTML, ЭлементСкриптHTML, ЭлементТаблицаHTML, ЭлементСтрокаТаблицыHTML, ЭлементЯчейкаТаблицыHTML, ЭлементКолонкаТаблицыHTML, ЭлементЗаголовокТаблицыHTML, ЭлементБлокHTML, ЭлементЛинияHTML, ЭлементФорматированногоТекстаHTML, ЭлементКартинкаHTML, ЭлементСвязьHTML, ЭлементЯкорьHTML, ЭлементАплетHTML, ЭлементФормаHTML, ДокументHTML, ТекстHTML, КомментарийHTML.
Узел HTMLDOM, с которым производится сравнение.
Возвращаемое значение:
Тип: Булево.
Описание:
Предоставляет возможность определения идентичности (ссылаются на один и тот же объект) двух узлов.
Доступность:
Тонкий клиент, мобильный клиент, сервер, толстый клиент, внешнее соединение, мобильное приложение (клиент), мобильное приложение (сервер), мобильный автономный сервер.
Использование в версии:
Доступен, начиная с версии 8.2.
Методическая информация
Практические примеры
Различить тот же HTML-документ и равную глубокую копию
Одна переменная ссылается на исходный документ, другая — на его глубокую копию. Проверяем идентичность и структурное равенство, затем меняем текст только в копии.
Где и как запускать
Полный серверный общий модуль УчебныйДокументHTML85. Свой HTML создаётся строкой и разбирается в памяти, без браузера и внешних источников.
Подготовка
Используйте отдельную учебную базу 8.5.1.1343. Создайте общий модуль УчебныйДокументHTML85: Сервер — Да, Клиент — Нет, Глобальный — Нет, Вызов сервера — Нет, Привилегированный — Нет.
Вставьте весь приведённый модуль одного урока. В своей управляемой учебной обработке создайте команду и клиентский обработчик, вызывающий свою процедуру с директивой &НаСервереБезКонтекста. В её тело вставьте УчебныйДокументHTML85.ВыполнитьПример();.
Источник HTML полностью записан в коде: свой вымышленный заказ, покупатель, книга, ручка и элементы с явно заданными id. ЧтениеHTML и ПостроительDOM получают строку, а не URL или внешний файл.
Разбор HTML в этом уроке не открывает браузер, не отправляет форму, не загружает картинки и не исполняет JavaScript. Файлы, сеть, печать, рабочие документы и регистры не используются.
Каждый запуск создаёт новый собственный документ в памяти. Поставьте точку останова перед конечными Сообщить и исследуйте Документ, его ЭлементДокумента и указанные переменные урока в серверном отладчике.
ДокументHTML — корневой объект дерева, а его ЭлементДокумента, Тело и отдельный блок — другие узлы. Одинаковые имена методов не означают одинаковые правила допустимых дочерних узлов.
Результаты ниже ожидаемые. В 1С код не запускался; статическая сверка сигнатур и readonly не подтверждает все особенности реализации HTML DOM вашей среды. Выполните проверки только на своём учебном стенде.
Процедура ВыполнитьПример() Экспорт
HTML = "<html><head><title>Учебный заказ</title><meta id='meta' name='description' content='Вымышленный заказ'></head>
|<body id='body'><p id='note'>Заказ существует только в памяти</p><pre id='code'>A-01: 3
|B-02: 1</pre>
|<form id='order' name='order' action='' method='get'>
|<input id='customer' name='customer' type='text' value='Учебный покупатель'>
|<input id='accepted' name='accepted' type='checkbox' value='yes'>
|<button id='preview' name='preview' type='button' value='preview'>Предпросмотр</button></form>
|<table id='items' summary='Книга и ручка'><caption id='caption'>Товары</caption><colgroup><col id='column'></colgroup><tbody>
|<tr id='book'><td id='book-name'>Книга</td><td>3</td></tr><tr id='pen'><td>Ручка</td><td>1</td></tr>
|</tbody></table><a id='help' name='help' href='#note'>Пояснение</a><img id='logo' alt='Учебная картинка без источника'>
|</body></html>";
Чтение = Новый ЧтениеHTML;
Чтение.УстановитьСтроку(HTML);
Попытка
Построитель = Новый ПостроительDOM;
Документ = Построитель.Прочитать(Чтение);
Исключение
Чтение.Закрыть();
ВызватьИсключение;
КонецПопытки;
Чтение.Закрыть();
Если ТипЗнч(Документ) <> Тип("ДокументHTML") Тогда
ВызватьИсключение "Ожидался ДокументHTML, а не XML-документ";
КонецЕсли;
Документ.Нормализовать();
ТаЖеСсылка = Документ;
Копия = Документ.КлонироватьУзел(Истина);
Если ТипЗнч(Копия) <> Тип("ДокументHTML") Тогда
ВызватьИсключение "Ожидалась глубокая копия HTML-документа";
КонецЕсли;
Копия.Нормализовать();
Сообщить(Документ.УзелИдентичен(ТаЖеСсылка));
Сообщить(Не Документ.УзелИдентичен(Копия));
Сообщить(Документ.УзелРавен(Копия));
Сообщить(Документ.УзелРавен(Документ));
ИсходноеПояснение = Документ.ПолучитьЭлементПоИдентификатору("note");
ПояснениеКопии = Копия.ПолучитьЭлементПоИдентификатору("note");
Если ИсходноеПояснение = Неопределено Или ПояснениеКопии = Неопределено Тогда
ВызватьИсключение "В обеих собственных версиях нужно пояснение note";
КонецЕсли;
ИсходныйТекст = ИсходноеПояснение.ТекстовоеСодержимое;
ПояснениеКопии.ТекстовоеСодержимое = "Изменена только учебная копия";
Сообщить(ИсходноеПояснение.ТекстовоеСодержимое = ИсходныйТекст);
Сообщить(Не Документ.УзелРавен(Копия));
КонецПроцедурыРазбор по шагам
Создать свой исходный документ из строки и нормализовать его перед сравнением.
Присваивание ТаЖеСсылка=Документ не копирует дерево. УзелИдентичен проверяет ссылку на тот же объект, а не сходство его текста.
КлонироватьУзел(Истина) создаёт глубокую копию. Проверяем её тип перед использованием свойств документа.
Нормализовать обе версии: по справке нормализация влияет на структурное равенство узлов.
Исходный документ идентичен ТаЖеСсылка, не идентичен копии, но равен ей по структуре. Самому себе он также равен.
Найти собственное пояснение note в каждой версии и проверить оба результата на Неопределено. Менять ТекстовоеСодержимое только найденного узла копии.
Сохранность исходного текста показывает независимость глубокого дерева. После изменения копия уже структурно не равна оригиналу.
Как проверить результат
Шесть конечных сравнений должны дать Истина: четыре до правки и два после неё.
В отладчике оригинальное note сохраняет текст Заказ существует только в памяти, копия содержит новое пояснение.
УзелИдентичен и УзелРавен отвечают на разные вопросы. Не используйте равенство текста как доказательство общего объекта.
Ожидаемый результат
Различие ссылочной идентичности и структурного равенства показано на полноценном собственном документе и его изменённой копии.
На что обратить внимание
УзелРавен не сравнивает пользовательские данные и не учитывает ряд служебных ссылок документа-владельца и родителей.
Слово Истина в КлонироватьУзел относится к глубине копирования, не к записи документа в базу.
Нельзя применять этот код к неизвестной странице пользователя: оба документа созданы в этом уроке.
Никакой файл или окно браузера не изменяется; запуска в 1С не было.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.
Убрать свою старую HTML-пометку и сохранить текст 125
Разбираем собственный HTML в памяти, находим настоящий КомментарийHTML и вызываем пустую ЗаменитьПолныйТекст. Проверяем не только Неопределено, но и новое дерево: два ребёнка превращаются в один, а число 125 и заголовок остаются.
Где и как запускать
Своя новая пустая файловая CF 8.5.1.1343. Настоящая команда управляемой формы на клиенте; разбор и все операции HTML только в её функции &НаСервереБезКонтекста. Через границу передаётся лишь строка.
Подготовка
Это узкий урок сопровождения старого HTML API: КомментарийHTML и ЗаменитьПолныйТекст не рекомендуются с версии 8.2. Он не предлагает строить новый браузерный интерфейс на этом API. Выполнение здесь не проверялось.
Вручную подготовьте НОВУЮ собственную пустую файловую базу в отдельной собственной Windows VM с 1С 8.5.1.1343. Не используйте рабочую базу или её копию. В свойствах конфигурации: Основной режим запуска — Управляемое приложение, Режим совместимости — Не использовать.
Конфигуратор → Общие → Общие формы: создайте управляемую форму УчебнаяПометкаHTML85. Разместите её в рабочей области начальной страницы. Весь код карточки вставьте в модуль именно этой формы, без оборачивания в дополнительную процедуру.
Реквизиты формы: СогласенНаОпыт85 — Булево, исходно Ложь, выведите как ручной флажок; Состояние85 — Строка неограниченной длины, исходно Не запускалось, выведите в поле ТолькоПросмотр=Да. Фаза85 — внутренняя переменная модуля, не реквизит.
В командах формы создайте УбратьПометку85 и назначьте Действие одноимённой клиентской процедуре. Создайте кнопку с ИмяКоманды=УбратьПометку85. Обработчик получает настоящую КомандаФормы; не вызывайте его вручную.
Сохраните конфигурацию, обновите конфигурацию базы данных и запустите свой стенд толстым управляемым клиентом 8.5.1.1343. Серверная функция формы выполняется в серверном контексте и в файловой базе. Не переносите ПостроительDOM.Прочитать в клиентский обработчик.
HTML уже целиком задан строкой в коде: один DIV, комментарий ARCHIVE:125 и SPAN с числом 125. Не добавляйте пробелы между детьми DIV, URL, скрипты, файлы или внешнюю разметку. Парсер здесь не является браузером; код не открывает страницу и не сохраняет HTML.
&НаКлиенте
Перем Фаза85;
&НаКлиенте
Процедура УбратьПометку85(Команда)
Попытка
Если Фаза85 <> Неопределено Тогда
ВызватьИсключение "Повтор в этом экземпляре запрещен";
КонецЕсли;
Если ТипЗнч(СогласенНаОпыт85) <> Тип("Булево")
Или СогласенНаОпыт85 <> Истина Тогда
ВызватьИсключение "Сначала подтвердите собственный учебный опыт";
КонецЕсли;
СогласенНаОпыт85 = Ложь;
Фаза85 = "НЕИЗВЕСТНО";
Состояние85 = "НЕИЗВЕСТНО: результат еще не подтвержден";
Если ТипЗнч(Команда) <> Тип("КомандаФормы")
Или Команда <> Команды.Найти("УбратьПометку85") Тогда
ВызватьИсключение "Нужна настоящая команда своей формы";
КонецЕсли;
Протокол85 = УбратьСвоюПометкуНаСервере85();
Если ТипЗнч(Протокол85) <> Тип("Строка") Или Протокол85 = "" Тогда
ВызватьИсключение "Не получен серверный протокол";
КонецЕсли;
Фаза85 = "ПОДТВЕРЖДЕНО";
Состояние85 = Протокол85;
Исключение
Фаза85 = "STOP";
СогласенНаОпыт85 = Ложь;
Состояние85 = "STOP: удаление и сохранность содержимого не подтверждены; повторов нет";
КонецПопытки;
КонецПроцедуры
&НаСервереБезКонтекста
Функция УбратьСвоюПометкуНаСервере85()
ИсходныйHTML85 = "<html><head><title>Урок 125</title></head><body><div id='comment85'><!--ARCHIVE:125--><span id='value85'>125</span></div></body></html>";
Чтение85 = Новый ЧтениеHTML;
Попытка
Чтение85.УстановитьСтроку(ИсходныйHTML85);
Построитель85 = Новый ПостроительDOM;
Документ85 = Построитель85.Прочитать(Чтение85);
Исключение
Чтение85.Закрыть();
ВызватьИсключение;
КонецПопытки;
Чтение85.Закрыть();
Если ТипЗнч(Документ85) <> Тип("ДокументHTML") Тогда
ВызватьИсключение "Разобранный документ не является ДокументHTML";
КонецЕсли;
ЗаголовокДо85 = Документ85.Заголовок;
Если ТипЗнч(ЗаголовокДо85) <> Тип("Строка") Или ЗаголовокДо85 <> "Урок 125" Тогда
ВызватьИсключение "Не подтвержден собственный заголовок";
КонецЕсли;
Контейнер85 = Документ85.ПолучитьЭлементПоИдентификатору("comment85");
Поле85 = Документ85.ПолучитьЭлементПоИдентификатору("value85");
Если ТипЗнч(Контейнер85) <> Тип("ЭлементБлокHTML")
Или ТипЗнч(Поле85) <> Тип("ЭлементHTML") Тогда
ВызватьИсключение "Не получены ожидаемые собственные DIV и SPAN";
КонецЕсли;
ВладелецКонтейнера85 = Контейнер85.ДокументВладелец;
Если ТипЗнч(ВладелецКонтейнера85) <> Тип("ДокументHTML") Тогда
ВызватьИсключение "Не получен документ контейнера";
КонецЕсли;
КонтейнерСвой85 = ВладелецКонтейнера85.УзелИдентичен(Документ85);
Если ТипЗнч(КонтейнерСвой85) <> Тип("Булево") Или КонтейнерСвой85 <> Истина Тогда
ВызватьИсключение "Контейнер не принадлежит своему документу";
КонецЕсли;
ДетиДо85 = Контейнер85.ДочерниеУзлы;
Если ТипЗнч(ДетиДо85) <> Тип("СписокУзловHTML") Тогда
ВызватьИсключение "Не получен настоящий список детей";
КонецЕсли;
КоличествоДо85 = ДетиДо85.Количество();
Если ТипЗнч(КоличествоДо85) <> Тип("Число") Или КоличествоДо85 <> 2 Тогда
ВызватьИсключение "Нужны ровно комментарий и SPAN, без других узлов";
КонецЕсли;
Пометка85 = ДетиДо85.Элемент(0);
ВторойУзел85 = ДетиДо85.Элемент(1);
Если ТипЗнч(Пометка85) <> Тип("КомментарийHTML")
Или ТипЗнч(ВторойУзел85) <> Тип("ЭлементHTML") Тогда
ВызватьИсключение "Не подтвержден порядок и тип двух детей";
КонецЕсли;
ПолеВКонтейнере85 = ВторойУзел85.УзелИдентичен(Поле85);
Если ТипЗнч(ПолеВКонтейнере85) <> Тип("Булево") Или ПолеВКонтейнере85 <> Истина Тогда
ВызватьИсключение "Второй ребенок не является своим полем 125";
КонецЕсли;
ВладелецПометки85 = Пометка85.ДокументВладелец;
Если ТипЗнч(ВладелецПометки85) <> Тип("ДокументHTML") Тогда
ВызватьИсключение "Не получен документ комментария";
КонецЕсли;
ПометкаСвоя85 = ВладелецПометки85.УзелИдентичен(Документ85);
Если ТипЗнч(ПометкаСвоя85) <> Тип("Булево") Или ПометкаСвоя85 <> Истина Тогда
ВызватьИсключение "Комментарий не принадлежит своему документу";
КонецЕсли;
ДанныеДо85 = Пометка85.Данные;
ВидимыйТекстДо85 = Контейнер85.ТекстовоеСодержимое;
ТекстПоляДо85 = Поле85.ТекстовоеСодержимое;
Если ТипЗнч(ДанныеДо85) <> Тип("Строка") Или ДанныеДо85 <> "ARCHIVE:125"
Или ТипЗнч(ВидимыйТекстДо85) <> Тип("Строка") Или ВидимыйТекстДо85 <> "125"
Или ТипЗнч(ТекстПоляДо85) <> Тип("Строка") Или ТекстПоляДо85 <> ВидимыйТекстДо85 Тогда
ВызватьИсключение "Пометка или контрольный текст отличаются от подготовки";
КонецЕсли;
РезультатЗамены85 = Пометка85.ЗаменитьПолныйТекст("");
Если РезультатЗамены85 <> Неопределено Тогда
ВызватьИсключение "Пустая замена не вернула документированное Неопределено";
КонецЕсли;
КонтейнерПосле85 = Документ85.ПолучитьЭлементПоИдентификатору("comment85");
Если ТипЗнч(КонтейнерПосле85) <> Тип("ЭлементБлокHTML") Тогда
ВызватьИсключение "Контейнер после замены отсутствует";
КонецЕсли;
ТотЖеКонтейнер85 = КонтейнерПосле85.УзелИдентичен(Контейнер85);
Если ТипЗнч(ТотЖеКонтейнер85) <> Тип("Булево") Или ТотЖеКонтейнер85 <> Истина Тогда
ВызватьИсключение "После замены найден другой контейнер";
КонецЕсли;
ДетиПосле85 = КонтейнерПосле85.ДочерниеУзлы;
Если ТипЗнч(ДетиПосле85) <> Тип("СписокУзловHTML") Тогда
ВызватьИсключение "Не получен свежий список детей";
КонецЕсли;
КоличествоПосле85 = ДетиПосле85.Количество();
Если ТипЗнч(КоличествоПосле85) <> Тип("Число") Или КоличествоПосле85 <> 1 Тогда
ВызватьИсключение "Удаление комментария не подтверждено составом дерева";
КонецЕсли;
ОставшийсяУзел85 = ДетиПосле85.Элемент(0);
Если ТипЗнч(ОставшийсяУзел85) <> Тип("ЭлементHTML") Тогда
ВызватьИсключение "Остался не собственный SPAN";
КонецЕсли;
ПолеСохранено85 = ОставшийсяУзел85.УзелИдентичен(Поле85);
Если ТипЗнч(ПолеСохранено85) <> Тип("Булево") Или ПолеСохранено85 <> Истина Тогда
ВызватьИсключение "Контрольный узел был заменен";
КонецЕсли;
ВидимыйТекстПосле85 = КонтейнерПосле85.ТекстовоеСодержимое;
ТекстПоляПосле85 = ОставшийсяУзел85.ТекстовоеСодержимое;
ЗаголовокПосле85 = Документ85.Заголовок;
Если ТипЗнч(ВидимыйТекстПосле85) <> Тип("Строка") Или ВидимыйТекстПосле85 <> ВидимыйТекстДо85
Или ТипЗнч(ТекстПоляПосле85) <> Тип("Строка") Или ТекстПоляПосле85 <> ТекстПоляДо85
Или ТипЗнч(ЗаголовокПосле85) <> Тип("Строка") Или ЗаголовокПосле85 <> ЗаголовокДо85 Тогда
ВызватьИсключение "Содержимое или заголовок изменились";
КонецЕсли;
Возврат "Проверено: пометка=" + ДанныеДо85
+ "; детей=" + Строка(КоличествоДо85) + " → " + Строка(КоличествоПосле85)
+ "; текст=" + ВидимыйТекстПосле85 + "; заголовок=" + ЗаголовокПосле85;
КонецФункцииРазбор по шагам
Поставьте флажок и один раз нажмите УбратьПометку85. Согласие расходуется до вызова сервера, а состояние становится НЕИЗВЕСТНО. Повтор в том же экземпляре формы запрещён.
На сервере ЧтениеHTML получает только собственную строку. ПостроительDOM.Прочитать получает настоящий объект чтения; при таком источнике ожидается ДокументHTML. Чтение закрывается и при нормальном разборе, и при исключении.
По двум id находим собственные DIV и SPAN, проверяем реальные типы. SPAN должен быть ЭлементHTML, а не ЭлементРазметкаHTML: последний тип относится к HTMLHtmlElement. Иной фактический тип честно останавливает опыт.
ДокументВладелец контейнера должен быть именно разобранным документом. Количество дочерних узлов должно реально равняться двум; только после этого читаем индексы 0 и 1. Первым нужен настоящий КомментарийHTML, вторым — тот самый собственный SPAN.
Проверяем документ комментария, полную строку ARCHIVE:125 и текст 125. ТекстовоеСодержимое DIV не включает комментарий, поэтому техническая пометка не превращается в видимый текст.
Вызываем ЗаменитьПолныйТекст только у найденного настоящего комментария и только с пустой строкой. Документированное Неопределено — результат именно этой ветки, а не объект для дальнейших вызовов. Иной результат означает STOP.
Не довольствуемся возвращённым значением: снова находим собственный контейнер, проверяем его идентичность и заново читаем ДочерниеУзлы. Должен остаться один узел — прежний SPAN; число 125 и заголовок Урок 125 должны полностью сохраниться.
Только после всех проверок сервер возвращает строковый протокол из фактически прочитанных данных, двух счётчиков, текста и заголовка. В клиент передаётся только эта строка, не HTML/DOM-объекты. При ошибке показывается фиксированное STOP, без исправления дерева или повторной попытки.
Как проверить результат
Без флажка на новом экземпляре: STOP, серверный разбор не начинается, Проверено не появляется. Повторное нажатие в этом экземпляре также не выполняет опыт.
Будущий нормальный запуск должен показать: Проверено: пометка=ARCHIVE:125; детей=2 → 1; текст=125; заголовок=Урок 125. Это ожидаемый контрольный протокол, не сообщение о выполненном сейчас тесте.
В серверном отладчике перед заменой Пометка85 имеет тип КомментарийHTML, КоличествоДо85 — Число 2, ДанныеДо85 — Строка ARCHIVE:125. После замены РезультатЗамены85 реально Неопределено, КоличествоПосле85 — Число 1; оставшийся узел идентичен прежнему Поле85.
В другой новой собственной лаборатории измените только ARCHIVE:125 на ARCHIVE:126 в исходной строке: проверка должна остановить опыт ДО замены. Если добавить третьего ребёнка DIV, состав также отклоняется до замены.
Измените только число SPAN на 126 или только заголовок на другой: STOP до удаления. Не удаляйте проверки ради успеха и не подменяйте результаты счётчиков константами.
Если устаревшая платформа не выполняет описанную пустую замену, возвращает другой тип узла или сохраняет комментарий, это STOP, а не успешный пример. Запуск должен проверить именно вашу платформу; здесь есть только статическая сверка контрактов.
Ожидаемый результат
Будущий успешный ручной запуск должен подтвердить реальное удаление собственной пометки: детей 2 → 1, прежний SPAN сохранён, текст 125 и заголовок Урок 125 не изменились. В 1С сейчас не запускалось.
На что обратить внимание
ЗаменитьПолныйТекст описывает замену логически соседних текстовых узлов. Здесь используется только документированная пустая ветка на изолированной собственной пометке. Непустая ветка с ТекстDOM и РазделитьТекст в урок не входят.
ДокументHTML.СоздатьКомментарий в физической справке объявляет КомментарийDOM, не КомментарийHTML. Поэтому здесь нет конструктора комментария или такой фабрики: нужный HTML-комментарий извлекается из реально разобранного HTML и проверяется по типу.
Неопределено нельзя считать доказательством удаления само по себе. Нужны свежий состав своего контейнера и сохранность реального видимого текста и заголовка.
Индексы начинаются с нуля. HTML-парсер может выделять дополнительные текстовые узлы; неожиданный состав не обрезается и не нормализуется ради прохождения проверки.
Все изменения относятся лишь к собственному дереву в памяти серверного вызова. Код не записывает данные базы, не открывает браузер и не читает файлы. Настоящий запуск 1С и проверка поведения устаревшего API ещё не выполнены.
STOP сохраняется только в этом экземпляре формы. Новый опыт требует явной ручной подготовки другого собственного экземпляра; повторное открытие не является автоматическим восстановлением.
Пример не запускался в вашей базе. Перед применением проверьте его в указанном учебном контексте.