【问题标题】:Dynamic styling of SVG textSVG 文本的动态样式
【发布时间】:2012-12-19 22:26:22
【问题描述】:
<svg>
    <text id="test" x="0" y="15"></text>
</svg>

,

d3.select("#test").text("aaa");

document.getElementById("test").textContent("aaa");

工作正常。但我需要为文本添加样式。现在:

d3.select("#test").text('aaa <tspan style="font-weight:bold">bbb</tspan> ccc');

不幸的是不起作用,因为参数被视为纯字符串。解决此问题的最佳跨浏览器解决方案是什么?

【问题讨论】:

  • d3.select("#test).html('&lt;span&gt;Hi&lt;/span&gt;') 不是吗?嗯,实际上这就是 jQuery 语法,否则你需要 ("#test").innerHTML+=("#test").element.innerHTML+=。这取决于d3.select("#test") 返回的内容
  • ”中没有“”标签
  • 我的意思是这个..:d3.select("#test").html('aaa &lt;tspan style="font-weight:bold"&gt;bbb&lt;/tspan&gt; ccc');
  • 例如,我在控制台中的这个站点github.com/mbostock/d3/wiki/Selections 上尝试了这个d3.select("#wiki-content").html("&lt;h1&gt;Abcd&lt;/h1&gt;"),它起作用了:) 这意味着你也可以添加内联样式或类(=更好)跨度>

标签: javascript svg d3.js


【解决方案1】:

工作解决方案

主要问题是SVG元素上没有innerHTML属性,你需要自己创建所有子节点。但是您可以通过仅使用此字符串创建一个临时 DOM 对象,将您的 HTML 字符串转换为样式化的tspans(没有)字符串解析魔法。之后,您可以使用 d3 创建正确的 tspans 元素。

var text  = d3.select("#test"), tmp = document.createElement("text");  //create tmp
tmp.innerHTML = 'aaa <tspan style="font-weight:bold">bbb</tspan> ccc'  //create HTML children (not yet an SVG!)
var nodes = Array.prototype.slice.call(tmp.childNodes)                 //create a real array from the childNodes variable
nodes.forEach( function(node) {
    text.append("tspan")
        .attr("style", node.getAttribute && node.getAttribute("style"))
        .text(node.textContent)
})

剩余问题:此解决方案不支持 tspan 中的子节点,这些子节点需要遍历源元素的 childNodes,从子节点和 append 新子 tspan 元素(和其他可能的标签)到相应的父 tspan(或其他标签)。这最终会产生一个子 tspan 兼容的解析器来彻底解决这个问题。

编辑:还有一个更通用的innerSVG shim,也可以处理子节点。

破碎的解决方案

还有一些其他解决方案应该可以工作,但目前已被破坏。正如其他人已经指出的那样,直接在 jQuery 或 d3 中使用 .html() 是行不通的,因为它们依赖于 innerHTML

//not working
$("#test").html(...); d3.select("#test").html(...)

通过 jQuery 复制和附加(类似于我的工作解决方案)也被破坏了。它创建了正确的 DOM,但没有显示 tspan(在 Firefox 和 Chrome 中尝试过):

//creates correct DOM, but disables/hides the created tspan
var tmp = $("<text>")
tmp.html('aaa <tspan style="font-weight:bold">bbb</tspan> ccc')
$("#test").append( Array.prototype.slice.call(tmp[0].childNodes) )

根据 W3C 规范,mix tspan's 和 TextNodes 应该是可以的。

【讨论】:

  • 不错。如果没有更好的,我会接受。但这并不完美。例如。如果&lt;tspan&gt; 中有&lt;tspan&gt; 将不起作用:aaa &lt;tspan style="font-weight:bold"&gt;bbb&lt;tspan style="fill:red"&gt;ddd&lt;/tspan&gt;&lt;/tspan&gt; ccc。那么非 jQuery 解决方案呢? d3 中的.html() 不起作用,因为它基于 innerHTML 属性,而 SVG 元素不存在此属性。
  • 嘿,来吧 ;-) 所以答案不应该提供完整的解决方案。但是给你一个正确方向的提示(特别是对于由兼容性问题/浏览器中缺少实现引起的复杂问题)。我很想在 tspans(或throw "SubNodesNotSupportedError")中添加 tspans 的解决方案,但里面甚至可以有其他标签。最后,您将需要一个与 tspan 子节点兼容的解析器,它可以解析源字符串或处理切片的 DOM 元素数组。
  • 当然,我只是指出来了。但是很高兴看到非 jQuery 解决方案。如何在纯 JS 中创建临时元素?无论如何,感谢您的帮助,我会在几天内接受您的答复。
  • 你去非 jq 解决方案,我找到了一个 innerSVG 垫片,你可以尝试一下
【解决方案2】:
var t = d3.select("#test")
t.append("tspan").text("aaa ");
t.append("tspan").text("bbb").style("font-weight","bold");
t.append("tspan").text(" ccc");

【讨论】:

  • 这当然可以,但如果我将整个字符串放在某个变量中(如我的情况),这不是一个方便的解决方案。 (aaa &lt;tspan style="font-weight:bold"&gt;bbb&lt;/tspan&gt; ccc) 我将不得不编写另一个 JS 函数,该函数将使用正则表达式将字符串切割成多个部分,然后实现您的代码。更重要的是,稍后我需要从#test 中删除任何比赛,而t.text(""); 将不再起作用。
【解决方案3】:
document.getElementById("test").style.fontWeight="bold"

以上内容改变了一切。

不过你可以试试innerHTML方法:

document.getElementById("test").innerHTML('aaa <tspan style=\"font-weight:bold\">bbb</tspan> ccc');

【讨论】:

  • 它应该如何解决问题?它为整个事物设置样式。
  • 不,它不起作用。为什么它已经有 2 票了? &lt;svg&gt; elems 没有 innerHTML 属性。此外,innerHTML 不是函数。
【解决方案4】:

试试这个

d3.select("#test").html('aaa <tspan style="font-weight:bold">bbb</tspan> ccc');

【讨论】:

  • 明天在我的电脑上试试。
猜你喜欢
  • 1970-01-01
  • 2016-08-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多