简体   繁体   中英

Bring a SVG line to front

I want to add some circles and a redline above the circles to my SVG. However, no matter in which order they are appended: I always have the line in the back.

How do I bring it to front? I tried to use moveToFront and z-axis property but I cannot fix it.

My tentative:

d3.select("svg")
  .append("g")
  .attr("id", "chart")
  .attr("width", width)
  .attr("height", height)

filename = "assets/sources/datafile.csv"
d3.csv(filename, d => dataViz(d));

function dataViz(input_data) {
  d3.select("svg")
    .selectAll("circle")
    .data(input_data)
    .enter()
    .append("circle")
}
d3.select("#chart")
  .append('line')
  .attr("id", "avg_line")
  .attr("x1", xScale(0))
  .attr("y1", yScale(3.75))
  .attr("x2", xScale(upper_limit))
  .attr("y2", yScale(3.75))
  .attr("stroke-width", 5)
  .attr("stroke", "red")

That is an interesting variation of a well-known topic on d3.json ! Since d3.json is asynchronous the callback will not be invoked before the loading has finished. Although the call to append the line seems to be located after appending the circles, it will actually execute right away, ie before appending the circles, whereby positioning the line under the circles in any case. The solution would be to append the line inside the callback after the circles were appended.

function dataViz(input_data) {
  d3.select("svg")
    .selectAll("circle")
    .data(input_data)
    .enter()
    .append("circle")

  d3.select("#chart")   // <-- This should be inside the callback.
    .append('line')
      .attr("id","avg_line")
      .attr("x1", xScale(0))
      .attr("y1", yScale(3.75))
      .attr("x2", xScale(upper_limit))
      .attr("y2", yScale(3.75))
      .attr("stroke-width",5)
      .attr("stroke","red");
}

Another mistake is in the way you select the elements to which you want to append to. The corrected code above will generate the following structure:

<svg>
  <g id="chart" width="" height="">
    <line id="avg_line" x1="" y1="" x2="" y2="" stroke-width="5" stroke="red"></line>
  </g>
  <circle></circle>
</svg>

This is because you are first appending a <g id="chart"> to the <svg> to which only the line is appended. The circles on the other hand get appended to the <svg> itself. Looking at that structure it becomes apparent, why the line is still located behind the circles.

Given that you have not provided your full code I cannot tell for sure what the best solution would be. However, since a <g> is just a container element, which does not have width and height attributes, the first statement should ditch the .append("g") . On the other hand, because the svg is the element you want to append to, it is good practice to keep a reference to it, which also frees you from repeatedly re-selecting the same element:

var svg = d3.select("svg")   // Keep the reference to the <svg> for later use.
    .attr("id", "chart")
    .attr("width",width)
    .attr("height",height);

In your callback you are then able to use this reference to append the elements in the desired order. Putting all this together you end up with something like the following:

var svg = d3.select("svg")
    .attr("id", "chart")
    .attr("width",width)
    .attr("height",height);

filename="assets/sources/datafile.csv"

d3.csv(filename, dataViz);

function dataViz(input_data) {
  svg.selectAll("circle")
    .data(input_data)
    .enter()
    .append("circle")

  svg.append('line')
      .attr("id","avg_line")
      .attr("x1", xScale(0))
      .attr("y1", yScale(3.75))
      .attr("x2", xScale(upper_limit))
      .attr("y2", yScale(3.75))
      .attr("stroke-width",5)
      .attr("stroke","red");
}

The resulting SVG structure will look like the following, rendering the line above any circle:

<svg id="chart" width="" height="">
  <circle></circle>
  <line id="avg_line" x1="" y1="" x2="" y2="" stroke-width="5" stroke="red"></line>
</svg>

Protip not related to your problem:

Notice, how I simplified your call to d3.csv() . The following two statements are basically equivalent (well, before I get chastized, this is not exactly true, but in first-order approximation they are):

d3.csv(filename, d => dataViz(d));
d3.csv(filename, dataViz);

There is no need to wrap your function dataViz() in yet another anonymous arrow function; just pass in the reference to it, D3 will take care of invoking it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM