Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

190
Vistas
How to avoid overlapping and stack long text legends in D3 graph?

I have D3 graph base on Multi-line graph 3 with v7: Legend, this sample contains few and shorts legends for the graph. In my sample I want to increase the length for legends and stack the data if is necessary, I want to avoid overlapping in the legends,

https://bl.ocks.org/d3noob/d4a9e3e45094e89808095a47da19808d

dataNest.forEach(function(d,i) { 

    svg.append("path")
        .attr("class", "line")
        .style("stroke", function() { // Add the colours dynamically
            return d.color = color(d.key); })
        .attr("d", priceline(d.value));

    // Add the Legend
    svg.append("text")
        .attr("x", (legendSpace/2)+i*legendSpace)  // space legend
        .attr("y", height + (margin.bottom/2)+ 5)
        .attr("class", "legend")    // style the legend
        .style("fill", function() { // Add the colours dynamically
            return d.color = color(d.key); })
        .text(d.key); 

});

My legends overlaped

about 4 years ago · Juan Pablo Isaza
1 Respuestas
Responde la pregunta

0

There are two possible solutions I can think of, shown in this JSFiddle.

First, if it is acceptable that the legend is not part of the svg, then realize the legend with a simple unordered list in a container next to the svg. This is probably the best when there is a varying number of legend entries and there are no restrictions considering the styling via css. The browser takes care of varying lengths and does an automatic line break.

Second, if the legend has to be a part of the svg, one can use the getBBox()-method that determines the coordinates of the smallest rectangle around an object inside an svg.

In a first step select all the legend entries that have been rendered and get the bounding boxes:

const bbox = svg.selectAll(".legend")
  .nodes()
  .map(legend_entry => legend_entry.getBBox());

With this array and the width of the svg, we can calculate the positions for each legend entry:

  bbox.reduce((pos, box) => {
    let left, right, line;
    if (pos.length === 0) {
      left = 0;
      line = 1;
    } else {
      /* The legend entry starts where the last one ended. */
      left = pos[pos.length - 1].right;
      line = pos[pos.length - 1].line;
    }
    /* Cumulative width of legend entries. */
    right = left + box.width;
    /* If right end of legend entry is outside of svg, make a line break. */
    if (right > svg_width) {
        line = line + 1;
      left = 0;
      right = box.width;
    }
    pos.push({
      left: left,
      right: right,
      line: line,
    });
    return pos;
  }, []);

Margins and paddings have to be included manually in the calculation of the positions. Of course, one could obtain the maximum width of all legend entries and make them all the same width with center alignment as in the d3noob example.

In the JSFiddle, I realized this repositioning by first rendering a hidden legend and then an additional one that is visible. It is of course possible to use only one legend, but I would not to take any chances that the process of repositioning is visible in the rendered document.

about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda