1

EDIT: It seems it is not easily possible to apply SemanticUI styles to SVG elements. The accepted answer provides corrections of the original code and some ideas for a workaround.

I'm quite new to web development. I'm trying to create a graph using d3.js, and I want the nodes to be styled according to the "ui button tiny blue" classes of the SemanticUI stylesheet. However, when I add this class to the nodes in the svg, they are not displayed all. Inspecting them in the browser tells me that the style has been applied properly, but the content box for some reason has negative height and width. The "TEST_NODE" outside the svg gets displayed as intended. What am I doing wrong?

The html document with inline js-script:

<!DOCTYPE html>
<meta charset="utf-8">
<link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet" />
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://d3js.org/d3.v2.min.js?2.9.3"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js"></script>
<style>

    .link {
        stroke: #aaa;
    }

</style>
<body>

<div class="content">
    <div class="ui button tiny blue">TEST_NODE</div>
</div>


<script>
    $(document).ready(function (){
        var width = 960,
            height = 500

        var svg = d3.select("body").append("svg")
            .attr("width", width)
            .attr("height", height);

        var force = d3.layout.force()
            .gravity(.05)
            .distance(100)
            .charge(-100)
            .size([width, height]);

        d3.json("d3_test.json", function(json) {
            force
                .nodes(json.nodes)
                .links(json.links)
                .start();

            var link = svg.selectAll(".link")
                .data(json.links)
                .enter().append("line")
                .attr("class", "link");

            var node = svg.selectAll(".node")
                .data(json.nodes)
                .enter()
                .append("g")
                .call(force.drag);

            node.append("div")
                .attr("class", "ui button tiny blue")
                .text("NODE")


            force.on("tick", function() {
                link.attr("x1", function(d) { return d.source.x; })
                    .attr("y1", function(d) { return d.source.y; })
                    .attr("x2", function(d) { return d.target.x; })
                    .attr("y2", function(d) { return d.target.y; });

                node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
            });
        });
    });

</script>

</body>
duriz
  • 13
  • 4

1 Answers1

0

Your code has 2 errors:

  1. Class is missing for the node elements. Fix it by:
const node = svg.selectAll(".node")
  .data(json.nodes)
  .enter()
  .append("g")
  .classed("node", true)
  .call(force.drag);
  1. You cannot create a <div> under <g>. You can append a <text>:
node.append('rect')
  .attr('x', -50)
  .attr('y', -30)
  .attr('width', 100)
  .attr('height', 60)
  .style('fill', 'blue');

node.append('text')
  .classed('ui ...', true)
  .text('NODE')
  .attr('alignment-baseline', 'middle')
  .attr('text-anchor', 'middle')
  .style('fill', 'white');

... or insert a <div> under a <foreignObject>;

Michael Rovinsky
  • 6,807
  • 7
  • 15
  • 30
  • Thanks a lot. I corrected both parts, the result is not quite what I'd want yet. With appending a text element, the node text ("NODE") shows up, but in black and without the expected blue background color. With the foreignObject and an additional "xhtml:body", I got the proper styling, but now it's inside a rectangle defined by the foreignObject width and height. Anyway, this is already quite a step forward :) – duriz Jan 25 '22 at 13:45
  • @duriz see my updated answer to create white text on blue background rectangle – Michael Rovinsky Jan 25 '22 at 13:53
  • This works, as in, it creates blue boxes with white text. But I'd really like to use the style/classes provided by SemanticUI, since I'm just extending an existing project, and I'd prefer the style to be consistent. I'm not sure if this is possible at all in the svg context, maybe manually copying/hardcoding the style properties is the way to go after all? – duriz Jan 25 '22 at 15:49
  • @duriz I don't think SemanticUI styles applicable to the SVG DOM elements. Either create a `
    ` under a `` or use SVG elements like `` and `` and mimic their styles.
    – Michael Rovinsky Jan 25 '22 at 15:56
  • 1
    Ok, I almost suspected that. Good to know anyways, and thanks again for your help! – duriz Jan 26 '22 at 09:22