如何在D3图表的标签中包含换行符?

我使用D3来生成条形图(我修改了这个例子中的代码)。 我在x轴上使用的标签每个都是几个字,因为这使得所有的标签重叠,我需要跨越这些标签。 (如果我可以用换行符replace每个标签中的所有空格,那就没问题了。)

我最初试图通过在标签的<text>元素上用空格replace空格( )和设置xml:space="preserve" 。 不幸的是,事实certificate,SVG不尊重这个属性。 接下来,我尝试将每个单词包装在一个<tspan> ,以便以后可以使用。 我通过这个函数传递了每个标签:

 function (text) { return '<tspan>' + text.replace(/ /g, '</tspan><tspan>') + '</tspan>'; } 

但是这只是将文字<tspan>放入输出中。 如何将我的文本标签包裹在tspan (或者做其他事情),以便我的标签不重叠?

我结束了使用下面的代码跨行打破每个x轴标签:

 var insertLinebreaks = function (d) { var el = d3.select(this); var words = d.split(' '); el.text(''); for (var i = 0; i < words.length; i++) { var tspan = el.append('tspan').text(words[i]); if (i > 0) tspan.attr('x', 0).attr('dy', '15'); } }; svg.selectAll('gxaxis g text').each(insertLinebreaks); 

请注意,这假定标签已经被创build。 (如果你遵循规范的直方图的例子,那么标签就会按照你需要的方式设置。)也没有任何真正的断行逻辑存在。 该函数将每个空间转换为换行符。 这符合我的目的,但您可能需要编辑split()行,以便将string的各个部分划分为多行。

SVG文本元素不支持文本换行,所以有两个选项:

  • 将文本分成多个SVG文本元素
  • 在SVG上使用覆盖HTML div

请看Mike Bostock在这里的评论。

我发现有用的是使用“foreignObject”标签,而不是文本或tspan元素。 这允许HTML的简单embedded,允许单词自然地打破。 警告是满足特定需求的对象的整体尺寸:

 var myLabel = svg.append('foreignObject') .attr({ height: 50, width: 100, // dimensions determined based on need transform: 'translate(0,0)' // put it where you want it... }) .html('<div class"style-me"><p>My label or other text</p></div>'); 

无论您放置在此对象内的元素是否可以稍后使用d3.select / selectAll来dynamic更新文本值,都可以获得。

环顾四周后,我发现Mike Bostock提供了一个解决scheme,使您能够围绕文本。

http://bl.ocks.org/mbostock/7555321

在我的代码上实现它(我正在使用折叠的树形图)。 我只是复制“包装”的方法。

然后附上以下内容

  // Standard code for a node nodeEnter.append("text") .attr("x", function(d) { return d.children || d._children ? -10 : 10; }) .attr("dy", ".35em") .text(function(d) { return d.text; }) // New added line to call the function to wrap after a given width .call(wrap, 40); 

我没有看到任何理由,这不应该为一个武力指挥,酒吧或任何其他模式

修正案:

我已经将wrap函数修改为以下内容供读者阅读并使用collapisible graph。 “x”属性的改变正确地设置了alignment,在原始代码中注意到问题在单独行上增加了行号,并且“y”已经被硬设置为零,否则会发生行间距增加每一行。

 function wrap(text, width) { text.each(function() { var text = d3.select(this), words = text.text().split(/\s+/).reverse(), word, line = [], lineNumber = 0, y = text.attr("y"), dy = parseFloat(text.attr("dy")), lineHeight = 1.1, // ems tspan = text.text(null).append("tspan").attr("x", function(d) { return d.children || d._children ? -10 : 10; }).attr("y", y).attr("dy", dy + "em"); while (word = words.pop()) { line.push(word); tspan.text(line.join(" ")); var textWidth = tspan.node().getComputedTextLength(); if (tspan.node().getComputedTextLength() > width) { line.pop(); tspan.text(line.join(" ")); line = [word]; ++lineNumber; tspan = text.append("tspan").attr("x", function(d) { return d.children || d._children ? -10 : 10; }).attr("y", 0).attr("dy", lineNumber * lineHeight + dy + "em").text(word); } } }); } 

包装长标签也有这个答案。

 <!DOCTYPE html> <meta charset="utf-8"> <style> .bar { fill: steelblue; } .bar:hover { fill: brown; } .title { font: bold 14px "Helvetica Neue", Helvetica, Arial, sans-serif; } .axis { font: 10px sans-serif; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } .x.axis path { display: none; } </style> <body> <script src="http://d3js.org/d3.v3.min.js"></script> <script> var margin = {top: 80, right: 180, bottom: 80, left: 180}, width = 960 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var x = d3.scale.ordinal() .rangeRoundBands([0, width], .1, .3); var y = d3.scale.linear() .range([height, 0]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom"); var yAxis = d3.svg.axis() .scale(y) .orient("left") .ticks(8, "%"); var svg = d3.select("body").append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); d3.tsv("data.tsv", type, function(error, data) { x.domain(data.map(function(d) { return d.name; })); y.domain([0, d3.max(data, function(d) { return d.value; })]); svg.append("text") .attr("class", "title") .attr("x", x(data[0].name)) .attr("y", -26) .text("Why Are We Leaving Facebook?"); svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis) .selectAll(".tick text") .call(wrap, x.rangeBand()); svg.append("g") .attr("class", "y axis") .call(yAxis); svg.selectAll(".bar") .data(data) .enter().append("rect") .attr("class", "bar") .attr("x", function(d) { return x(d.name); }) .attr("width", x.rangeBand()) .attr("y", function(d) { return y(d.value); }) .attr("height", function(d) { return height - y(d.value); }); }); function wrap(text, width) { text.each(function() { var text = d3.select(this), words = text.text().split(/\s+/).reverse(), word, line = [], lineNumber = 0, lineHeight = 1.1, // ems y = text.attr("y"), dy = parseFloat(text.attr("dy")), tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em"); while (word = words.pop()) { line.push(word); tspan.text(line.join(" ")); if (tspan.node().getComputedTextLength() > width) { line.pop(); tspan.text(line.join(" ")); line = [word]; tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word); } } }); } function type(d) { d.value = +d.value; return d; } </script> 

和数据文件“data.tsv”:

 name value Family in feud with Zuckerbergs .17 Committed 671 birthdays to memory .19 Ex is doing too well .10 High school friends all dead now .15 Discovered how to “like” things mentally .27 Not enough politics .12 

使用<tspan>

和nv.d3

nv.models.axis = function(){

  .select('text') .attr('dy', '0em') .attr('y', -axis.tickPadding()) .attr('text-anchor', 'middle') .text(function(d,i) { var v = fmt(d); return ('' + v).match('NaN') ? '' : v; }); 

将所有出现的.text(改为.html(