使用 <object> 实现交互式 SVG?

我正尝试嵌入一个基于 SVG 的交互式数据可视化图表。该 SVG 由另一个系统生成(但仍与我的 Discourse 安装位于同一二级域名下)。我曾尝试使用 OBJECT 标签引入 SVG:

<object type="image/svg+xml" data="full_URL_to_foo.svg">
(如果对象加载失败则显示此内容)
</object>

如果我只用 IMG 标签引入该 SVG(它是每次动态生成的),图表可以显示,但(正如我所预期的)没有交互功能:

<img src="full_URL_to_foo.svg">

关于 SVG 与 Discourse 的结合,您有什么建议或线索可以分享吗?:slight_smile:

1 个赞

更简单的方法是改用 iframe。将 SVG 域名添加到 allowed_iframes 站点设置中,然后通过 HTML 将其添加到帖子中。

5 个赞

嗯,看来这是简单的方法。

有没有更难的方法可以尝试?

……因为我希望在 SVG 中添加链接,以改变浏览器的位置。在 IFRAME 中的 SVG 里的链接,只会改变 IFRAME 中显示的内容……

我怀疑,在话题中、IFRAME 之前嵌入 JavaScript,并在 SVG 的 onclick 事件中调用页面中的 JavaScript 函数……这样做恐怕也不行。

我表达清楚了吗?我是否应该添加一些轻量级的 JavaScript 代码(参考 https://meta.discourse.org/t/mitigate-xss-attacks-with-content-security-policy/104243),然后在 IFRAME 中的 SVG 内调用它?……还是说“在 IFRAME 中”是一个密不透风的堡垒?

也许我应该直接问如何将 SVG 内联嵌入到页面中?然后使用 theme-component 注入的 JavaScript 与我的其他服务器交互,动态更新 SVG?

请给我一些指点 :slight_smile:

2 个赞

将 SVG 放入由特殊 div 包围的帖子中,并使用主题组件通过 decorateCooked 回调将其转换为 object 标签,可能会起作用。请查看 Discourse 主题开发者指南

3 个赞

……以防有人正在跟进,decorateCooked() 已弃用。我正在尝试使用 decorateCookedElement()。

1 个赞

我遇到了 CORS(跨域资源共享)/ Access-Control-Allow-Origin 的问题。我不太熟悉它的运作机制……

我有两个站点:托管的 Discourse 站点(forum.moversmindset.com)和基于 Apache 的 WordPress 站点(moversmindset.com)。请注意,WordPress 站点上没有明显的内容——它主要用于生成 RSS 源、提供媒体服务等。如果你访问该域名,它只会将你重定向到论坛。

我有一个目录,用于对 GET 请求返回 SVG 类型的响应。例如(并非真实 URL):https://moversmindset.com/foo/bar.php

在我的 Discourse 主题中,我正在尝试使用脚本代码(最终将作为一个正式的插件)。它对带有特定 data-custom 属性的 DIV 调用 api.decorateCooked()。因此,在 decorateCooked() 调用的函数内部,我实际上是在执行类似以下的操作:

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

所以,我想获取 SVG 并将其附加到 DOM 中。但我的浏览器错误控制台显示:

问题:

这是否意味着我需要在 Discourse 安装上或在 Apache/WordPress 上配置 CORS?

我确实已经在 Discourse 上将 https://moversmindset.com 配置为 CORS 允许的源。

2 个赞

觉得应该是在这边。如果你还没看过的话,能否查看一下 https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin,看看是否能给你一些线索?

4 个赞

虽然只是小小的一步,但成功了!

我不得不在 Apache/WP 服务器上添加一个 Access-Control-Allow-Origin 头。这让来自 Discourse 平台的 JavaScript 文件顺利运行了。谢谢。

3 个赞

我一直在慢慢弄明白这件事。要提出我的问题,需要做很多准备工作:

SVG

我有一个生成 SVG 的 Web 服务器。针对这个问题,它生成了一个非常简单的测试 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。

最终目标是制作一个合适的插件。但我目前只是在构建一个概念验证。所以它只是简单地粘贴到我主题自定义的 <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)boop 我的 SVG 就内联了……

UMB.svgload() 内部的 alert() 随后按预期触发了。(这显然是一个调试 hack,对吧?:)

问题(终于)

我在托管的 Discourse 论坛中有一个仅限员工的主题,您可以在其中看到实际效果。(我正在与 Discourse 员工/支持人员交谈,他们可以以管理员身份闯入我的安装环境。)

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

为什么……

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

……没有选中任何 PATH 元素?

它没有任何反应——没有错误。什么都没有。

下一步

如果我能让这个简单的 alert() 概念验证正常工作,我接下来的计划是……

我知道我正在“操作”的 DOM 片段(在调用 UMB.svgload() 时)尚未连接到实际的文档 DOM。这就是为什么我期望 $(target)… 是我需要的东西。

最终,我将内联极其复杂的 SVG,并且需要使用更复杂的 JQuery 选择器。我想在 $(target) 内部找到许多元素,并附加事件处理程序(例如 onclick),这些处理程序将调用其他 UMB.… 全局函数。

1 个赞

如果你需要,IFRAME 可以改变顶级导航。

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

3 个赞

感谢提示!

不过现在确实更倾向于使用内联 SVG。

不管怎样,几周后,这行不通了。

一旦 SVG 内联到主文档中,似乎无法操作其 DOM 元素。因此,我无法确定如何添加事件触发器或动作(如果 SVG 加载在 iframe 中,可以轻松通过 onclick 等方式实现)。
(_不_使用 iframe 正是我的初衷。)

¯\_(ツ)_/¯

1 个赞

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.