如何解决如何使 svg 交互式以收集对所描绘元素的评论/注释
我在 networkx 和 nxv 的帮助下从维基数据创建了如下所示的有向图。结果是一个 svg 文件,它可能会嵌入到某个 html 页面中。
现在我希望每个节点和每个边都是“可点击的”,这样用户就可以将他们的评论添加到图表的特定元素中。我认为这可以通过弹出一个模态对话框来完成。这个对话框应该知道它是从哪个元素触发的,它应该通过 post 请求将 textarea 的内容发送到某个 url。
实现这一目标的最佳方法是什么?
解决方法
包裹在 W3C standard Web Component 中(所有现代浏览器都支持),您可以将其设为适用于任何 src="filename.svg"
-
简单示例:How to get SVG document data to be inserted into the DOM?
-
更复杂的例子:
<graphviz-svg-annotator src="https://graphviz.org/Gallery/directed/fsm.svg"> </graphviz-svg-annotator>
-
SVG 加载了异步获取
-
节点和边在此 SO 代码段中是可点击的
-
添加您自己的、更好的模态、窗口并保存到数据库
-
试试来自:https://graphviz.org/Gallery/directed/Genetic_Programming.html
的 SVG
<graphviz-svg-annotator src="fsm.svg"></graphviz-svg-annotator>
<graphviz-svg-annotator src="Linux_kernel_diagram.svg"></graphviz-svg-annotator>
<style>
svg .annotate { cursor:pointer }
</style>
<script>
customElements.define('graphviz-svg-annotator',class extends HTMLElement {
constructor() {
let loadSVG = async ( src,container = this.shadowRoot ) => {
container.innerHTML = `<style>:host { display:inline-block }
::slotted(svg) { width:100%;height:200px }
</style>
<slot name="svgonly">Loading ${src}</slot>`;
this.innerHTML = await(await fetch(src)).text(); // load full XML in lightDOM
let svg = this.querySelector("svg");
svg.slot = "svgonly"; // show only SVG part in shadowDOM slot
svg.querySelectorAll('g[id*="node"],g[id*="edge"]').forEach(g => {
let label = g.querySelector("text")?.innerHTML || "No label";
let shapes = g.querySelectorAll("*:not(title):not(text)");
let fill = (color = "none") => shapes.forEach(x => x.style.fill = color);
let prompt = "Please annotate: ID: " + g.id + " label: " + label;
g.classList.add("annotate");
g.onmouseenter = evt => fill("lightgreen");
g.onmouseleave = evt => fill();
g.onclick = evt => g.setAttribute("annotation",window.prompt(prompt));
})
}
super().attachShadow({ mode: 'open' });
loadSVG("//graphviz.org/Gallery/directed/"+this.getAttribute("src"));
}});
</script>
详细:
-
this.innerHTML = ...
在组件 ligthDOM
中注入完整的 XML (因为元素有shadowDOM,lightDOM在浏览器中是不可见的) -
但你只想要 SVG 部分(graphviz XML 有太多数据)......而且你不想要屏幕闪光;这就是为什么我把 XML .. invisible.. in lightDOM
-
shadowDOM
<slot>
仅用于反射<svg>
-
使用此方法,
<svg>
仍然可以从全局 CSS 设置样式(请参阅cursor:pointer
) -
如果屏幕上有多个 SVG,
<g>
ID 值可能会发生冲突。
完整的 SVG 可以移动到 shadowDOM 中:let svg = container.appendChild( this.querySelector("svg") );
但是你不能再用全局 CSS 设置 SVG 的样式,因为全局 CSS 不能设置 shadowDOM 的样式
据我所知,nxv 为每个节点生成一个 g
元素,其类为“node”,所有元素都嵌套在图 g
中。所以基本上你可以遍历主组内的所有 g
元素,并在每个元素上附加一个点击事件侦听器。 (实际上,根据所需的行为,您可能希望将事件侦听器附加到 g 内部的形状,如下所示。要使形状内部可点击,它必须是 filled)>
点击时,它会更新一个 form
,以做几件事:更新它的样式以将其显示为模态(提交后,表单应该返回隐藏状态),并使用点击的 text
的 g
内容。
基本上是这样的:
<svg>Your nxv output goes here</svg>
<form style="display: none;">
<input type="hidden" id="node_title">
<textarea></textarea>
<input type="submit" value="Send!">
</form>
<script>
const graph = document.querySelector("svg g");
const form = document.querySelector("form");
[...graph.querySelectorAll("g")].map(g => { //loop over each g element inside graph
if (g.getAttribute("class") == "node") { //filter for nodes
let target = "polygon";
if (g.querySelector("polygon") === null) {
target = "ellipse";
}
g.querySelector(target).addEventListener("click",() => {
const node_title = g.querySelector("text").innerHTML;
form.querySelector("#node_title").setAttribute("value",node_title);
form.setAttribute("style","display: block;");
});
}
});
const submitForm = async (e) => { //function for handling form submission
const endpoint = "path to your POST endpoint";
const body = {
source_node: form.querySelector("#node_title").value,textarea: form.querySelector("textarea").value
}
e.preventDefault(); //prevent the default form submission behavior
let response = await fetch(endpoint,{ method: "POST",body: JSON.stringify(body) });
// you might wanna do something with the server response
// if everything went ok,let's hide this form again & reset it
form.querySelector("#node_title").value = "";
form.querySelector("textarea").value = "";
form.setAttribute("style","display: none;");
}
form.addEventListener("submit",submitForm);
</script>
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。