Интерактивный SVG с использованием тега <object>?

Я пытаюсь внедрить интерактивную визуализацию данных на основе SVG. Я генерирую SVG в отдельной системе (но всё ещё в рамках второго уровня домена, где установлена Discourse). Я пробовал вставлять SVG с помощью тега OBJECT…

<object type="image/svg+xml" data="full_URL_to_foo.svg">
(появляется, если загрузка объекта не удалась)
</object>

Если я просто вставляю этот SVG (он генерируется динамически каждый раз) с помощью тега IMG, изображение отображается, но (как я и ожидал) интерактивности нет…

<img src="full_URL_to_foo.svg">

Есть ли какие-то идеи или подсказки, где можно найти информацию о работе с SVG и Discourse? :slight_smile:

Самый простой способ заставить это работать — использовать iframe. Добавьте домен SVG в параметр сайта allowed_iframes и добавьте его в пост, используя HTML.

Хм, значит, это простой способ.

Есть ли сложный способ, который я могу попробовать?

…потому что я хочу иметь ссылки в SVG, которые меняют расположение браузера. Ссылки в SVG внутри IFRAME меняют то, что отображается в IFRAME…

Я подозреваю, что вставка JavaScript в тему, прямо перед IFRAME, и вызов функции JavaScript в обработчике onclick страницы для SVG… тоже не сработает.

Я имею в виду? Должен ли я добавить немного JavaScript-кода, как в Mitigate XSS Attacks with Content Security Policy, а затем пытаться вызвать его из SVG внутри IFRAME? …или “внутри IFRAME” — это непроницаемая крепость?

Может быть, мне стоит спросить, как я могу напрямую встроить SVG в страницу? Затем использовать JavaScript, внедренный через theme-component, для взаимодействия с моим другим сервером и динамического обновления SVG?

Пожалуйста, дайте совет :slight_smile:

Размещение SVG в сообщении, помеченном специальным div-контейнером, и использование theme-component для преобразования его в тег object через обратный вызов decorateCooked может сработать. Ознакомьтесь с Руководством разработчика по темам Discourse.

…на случай, если кто-то следит за процессом: decorateCooked() устарела. Вместо неё я сейчас работаю с decorateCookedElement().

Я столкнулся с проблемой CORS (Access-Control-Allow-Origin). Я не очень хорошо разбираюсь в том, как это работает…

У меня есть два сайта: размещённый форум Discourse (forum.moversmindset.com) и сайт на WordPress с Apache (moversmindset.com). Обратите внимание, что на сайте WordPress нет явного контента — он генерирует RSS-ленты, обслуживает медиафайлы и так далее. Если перейти на домен, вас просто перенаправит на форум.

У меня есть каталог, который отвечает на GET-запросы SVG-изображениями. Например (не настоящий URL): https://moversmindset.com/foo/bar.php

В теме для Discourse я экспериментирую со скриптами (в будущем это станет полноценным плагином). Он вызывает api.decorateCooked() для определённых DIV-элементов, у которых есть атрибут data-custom. Таким образом, внутри функции, вызываемой decorateCooked(), я делаю примерно следующее:

$.get(‘https://moversmindset.com/foo/bar.php’ … bla bla blah

Итак, я хочу получить SVG и добавить его в DOM. Но в консоли браузера я вижу ошибку:

Вопрос:

Означает ли это, что мне нужно настроить CORS либо на установке Discourse, либо на стороне Apache/WordPress?

У меня уже настроено, что https://moversmindset.com разрешён в CORS на стороне Discourse.

Я думаю, что это на этой стороне. Можешь заглянуть на Access-Control-Allow-Origin header - HTTP | MDN, если ещё не делал этого, и посмотреть, не поможет ли это найти подсказку?

Маленькие, крошечные шаги, но ДА!

Мне пришлось добавить заголовок Access-Control-Allow-Origin на сервере Apache/WP. Это порадовало j/s (загружаемый с платформы Discourse). Спасибо.

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

SVG

У меня есть веб-сервер, который генерирует SVG. В данном случае он создает очень простой тестовый SVG…

<svg xmlns="http://www.w3.org/2000/svg" stroke-linejoin="round" viewBox="0 0 100 100">
<path d="M50,4L4,50L50,96L96,50Z" stroke="#40638C" stroke-width="3"></path>
<path d="M50,5L5,50L50,95L95,50Z" stroke="#333" fill="#40638C" stroke-width="3"></path>
<path d="M37,42c-1,0,11-20,13-20c1,0,15,20,13,20h-9c0,8,9,22,12,25l-4,4l-8,-7v13h-10v-35z" stroke="#40495E" fill="#40495E"></path>
<path d="M35,40c-1,0,11-20,13-20c1,0,15,20,13,20h-9c0,8,9,22,12,25l-4,4l-8,-7v13h-10v-35z" stroke="#333" fill="#555"></path>
</svg>

Это просто стилизованный знак «слияния». Обратите внимание, что он содержит четыре элемента PATH.

Инлайнинг

Чтобы вставить SVG в пост, я использую JavaScript, добавленный через мою тему.

Конечная цель — создать полноценный плагин. Но сейчас я просто пытаюсь создать proof-of-concept. Поэтому код просто вставлен в секцию <head> кастомизации моей темы:

<script type="text/discourse-plugin" version="0.8">
var UMB = {
    svgload: function(base, target) {
        var url = base + $(target).text();
        $(target).html('');
        $.ajax({
            method: "GET",
            url: url,
            async: false,
            dataType: "text",
            success: function(data) { $(target).append(data); }
        });
        alert('loaded!');
        $(target).children('path').each(function(){alert('here is a path element');});
    },
}
$.fn.umbdv = function() {
    this.each(
        function() {
            UMB.svgload('__URL_REDACTED__', this);
        }
    );
    return this;
};
api.decorateCooked(
  $elem => $elem.children('.cooked div[data-custom="umbdv"]').umbdv(),
  { id: 'umbdv' }
);
</script>

Где…

var UMB = { — это просто глобальная переменная, которая избавляет меня от необходимости писать огромные анонимные функции повсюду.

$.fn.umbdv = — это [как я понимаю] «плагин», расширяющий JQuery.

api.decorateCooked( позволяет мне манипулировать постом до того, как он будет отправлен в браузер.

Вызов

Затем в теме я пишу…

<div data-custom="umbdv">/vtest</div>

Для этого DIV вызывается UMB.svgload('__URL_REDACTED__', this).

UMB.svgload() корректно распознает строку /vtest, формирует URL и выполняет AJAX-запрос. Он успешно выполняет append(data), и бип — мой SVG становится инлайн-элементом…

Затем срабатывает мой alert() внутри UMB.svgload(), как и ожидалось. (Это, очевидно, отладочный хак, верно? :))

Вопрос (наконец)

У меня есть тема ТОЛЬКО ДЛЯ СОТРУДНИКОВ на моем хостинге Discourse, где можно увидеть это в действии. (Я обращаюсь к сотрудникам Discourse/поддержке, которые могут войти в мою установку как администраторы.)

https://forum.moversmindset.com/t/svg-experimentation-in-progress/1109

Почему…

$(target).children('path').each(function(){alert('here is a path element');});

…не выбирает ни один из элементов PATH?

Ничего не происходит — никаких ошибок. Ничего.

Далее

Куда я пойду, если этот тривиальный proof-of-concept с alert() заработает…

Я знаю, что фрагмент DOM, с которым я «работаю», еще не подключен к фактическому документу DOM (в момент вызова UMB.svgload()). Поэтому я ожидаю, что $(target)… будет тем, что мне нужно.

В конечном итоге я буду встраивать намного более сложные SVG, и мне понадобятся более сложные селекторы JQuery. Я хочу находить множество элементов внутри $(target) и прикреплять обработчики событий (например, onclick), которые будут вызывать другие глобальные функции UMB.….

Если нужно, IFrame могут изменять навигацию на верхнем уровне.

http://w3c-test.org/html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation_by_user_activation-manual.html

Спасибо за совет!

Но сейчас я определённо предпочитаю встроенный SVG.

В общем-то. Спустя несколько недель. Это не работает.

Кажется, невозможно манипулировать элементами SVG-документа, когда он встроен в основной документ. Поэтому я не могу понять, как добавить триггеры событий или действия (что можно было бы легко сделать через onclick и тому подобное, если бы SVG был загружен в iframe).

((Я как раз и не хотел использовать iframe.))

¯\_(ツ)_/¯