Search code examples
javascripthtmlcsssvgd3.js

Styling d3.js using CSS does not apply the CSS style


I'm working on this code that gets some data from a .JSON (at the moment embedded here in the code) and then draw some boxes with text and some connection lines. I'd like to view the bounding box around the text when I set some kind of debug flag.

This is the related code

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }

    svg {
      display: block;
    }

    .label {
      text-anchor: middle;
      alignment-baseline: middle;
      font-family: Arial, sans-serif;
      font-size: 12px;
    }

    .label .debugBoundingBox {
      fill: none; /* Transparent fill */
      stroke-width: 0.5;
      stroke: #ccc;
      stroke-dasharray: 1, 1;
    }

    .connection {
      stroke: #e74c3c; /* Red color for connection */
      stroke-width: 2;
      stroke-dasharray: 5, 5; /* Add dashed line effect */
    }
  </style>
  <script src="https://d3js.org/d3.v5.min.js"></script>
</head>
<body>

<svg id="dot-sequence"></svg>

<script>
  class Label {
    constructor(name, x, y, text) {
      this.name = name;
      this.x = parseInt(x); // Parse coordinates as integers
      this.y = parseInt(y);
      this.text = text;
    }

    draw(svg, showDebugBoundingBox) {
      const textElement = svg.append("text")
        .attr("class", "label")
        .attr("x", this.x)
        .attr("y", this.y)
        .text(this.text);

      const bbox = textElement.node().getBBox();

      if (showDebugBoundingBox) {
        const debugBBox = svg.append("rect")
          .attr("x", bbox.x)
          .attr("y", bbox.y)
          .attr("width", bbox.width)
          .attr("height", bbox.height)
          .attr("class", "label debugBoundingBox")
      }
    }
  }

  class Connection {
    constructor(label1, label2) {
      this.label1 = label1;
      this.label2 = label2;
    }

    draw(svg) {
      svg.insert("line", ":first-child") // Insert line as the first child
        .attr("class", "connection")
        .attr("x1", this.label1.x)
        .attr("y1", this.label1.y)
        .attr("x2", this.label2.x)
        .attr("y2", this.label2.y)
        .attr("stroke-dasharray", "5, 5") // Add dashed line effect
        .attr("stroke", "#e74c3c"); // Set red color for connection
    }
  }

  function composer(labelData, connectionData, showDebugBoundingBox) {
    const svg = d3.select("#dot-sequence")
      .attr("width", 500)
      .attr("height", 150);

    const labels = labelData.map(data => new Label(data.name, data.x, data.y, data.text));

    // Create connections and draw them
    connectionData.forEach(data => {
      const label1 = labels.find(label => label.name === data.start);
      const label2 = labels.find(label => label.name === data.end);

      if (label1 && label2) {
        const connection = new Connection(label1, label2);
        connection.draw(svg);
      }
    });

    // Draw labels after connections
    labels.forEach(label => label.draw(svg, showDebugBoundingBox));
  }

  const labelDataJSON = [
    { "name": "label1", "x": "50", "y": "50", "text": "Text 1" },
    { "name": "label2", "x": "150", "y": "100", "text": "Text 2" },
    { "name": "label3", "x": "250", "y": "50", "text": "Text 3" },
    { "name": "label4", "x": "350", "y": "100", "text": "Text 4" },
    { "name": "label5", "x": "450", "y": "50", "text": "Text 5" }
  ];

  const connectionDataJSON = [
    { "start": "label1", "end": "label2" },
    { "start": "label2", "end": "label3" },
    { "start": "label3", "end": "label4" },
    { "start": "label4", "end": "label5" }
  ];

  const showDebugBoundingBox = true; // Set to true to show debug boxes, false to hide
  composer(labelDataJSON, connectionDataJSON, showDebugBoundingBox);
</script>

</body>
</html>

my goal is to style a bounding box around the text, but now I can only see a black box covering my text.

But if I change this piece:

  if (showDebugBoundingBox) {
    const debugBBox = svg.append("rect")
      .attr("x", bbox.x)
      .attr("y", bbox.y)
      .attr("width", bbox.width)
      .attr("height", bbox.height)
      .attr("class", "label debugBoundingBox")
      .attr("fill", "none") // Set fill to none
      .style("stroke-width", 0.5)
      .style("stroke", "#ccc")
      .attr("stroke-dasharray", "1, 1");
  }

all works as I want. Why can't I use the .CSS styling in there?

Thank you


Solution

  • Your CSS selector is .label .debugBoundingBox, which means a .debugBoundingBox that is a descendant of a .label. You want .label.debugBoundingBox, which means an element that has both classes applied.