3
\$\begingroup\$

I developed a javascript component to display a tree, based on D3.js

I'm not sure how clean my code is, I'm not used to code a lot of javascript and I'm constrained to write everything in 1 file.

So I'd take every advice on how to make this code better. And in particular, there's a behaviour I don't understand: a "magical" line of code which shouldn't affect anything, but when removed something is broken.

Here's the whole code:

ApplicationMap = function(vaadinConnector, element) {
    var self = this;
    this.vaadinConnector = vaadinConnector;
    self.root = {};

    var red = "#f5696d";
    var green = "#40bc96";
    var orange = "#fabd57";
    var grey = "#e7e7e7";
    var statusColor = function(status) {
        if (status == "SUCCESS") {
            return green;
        } else if (status == "WARNING") {
            return orange;
        } else if (status == "CRITICAL") {
            return red;
        } else {
            return grey;
        }
    };

    var domAttach = d3.select(element);
    var nodeWidth = 100;
    var nodeHeight = 70;

    var tree = d3.layout.tree()
        .nodeSize([nodeWidth + 10, nodeHeight + 20])
        .separation(function (a, b) {
            return (a.parent == b.parent ? 1.5 : 2.5);
        });

    var diagonal = d3.svg.diagonal()
        .projection(function (d) {
            return [d.x, d.y];
        });

    domAttach.html("");
    var cx = parseInt(domAttach.style('width')) / 2;
    var cy = 50;
    var vis = domAttach.append("svg:svg")
        .attr("width", "100%")
        .attr("height", "100%")
        .append("svg:g");

    vis.attr("transform", "translate(" + cx + "," + cy + ")");

    // Toggle children
    this.toggle = function(d) {
        if (d.children) {
            d.hiddenChildren = d.children;
            d.children = null;
        } else {
            d.children = d.hiddenChildren;
            d.hiddenChildren = null;
        }
    };

    this.toggleAll = function(d) {
        if (d.children) {
            d.children.forEach(self.toggleAll);
            self.toggle(d);
        }
    };

    var update = function(source) {
        var duration = d3.event && d3.event.altKey ? 5000 : 500;

        // Compute the new tree layout.
        var nodes = tree.nodes(self.root).reverse();

        // Update the nodes…
        var node = vis.selectAll("g.node")
            .data(nodes, function (d) {
                return d.id;
            });

        // Enter any new nodes at the parent's previous position.
        var nodeEnter = node.enter().append("svg:g")
            .attr("class", "node")
            .attr("transform", function () {
                return "translate(" + source.x0 + "," + source.y0 + ")";
            })
            .on("click", function (d) {
                self.vaadinConnector.onClickNode(d.name);
            })
            .on("mouseover", function () {
                d3.select(this).classed("hover", true);
            })
            .on("mouseout", function () {
                d3.select(this).classed("hover", false);
            });

        nodeEnter.append("svg:rect")
            .attr("width", nodeWidth)
            .attr("height", nodeHeight)
            .attr("x", -nodeWidth / 2)
            .attr("y", 7 - nodeHeight / 2)
            .style("fill-opacity", 1e-6)
            .style("fill", function (d) {
                return statusColor(d.status);
            });

        nodeEnter.append("svg:text")
            .attr("text-anchor", "middle")
            .attr("y", -5)
            .classed("pointed", true)
            .text(function (d) {
                return d.name;
            })
            .style("fill-opacity", 1e-6);

        // Expand / collapse icon
        var filtered = nodeEnter.filter(function(d) {
            return (d.children && d.children.length > 0) || (d.hiddenChildren && d.hiddenChildren.length > 0);
        }).append("svg:text")
            .attr("text-anchor", "right")
            .attr("x", 34)
            .attr("y", 35)
            .classed({
                "toggle-expand": true,
                "pointed": true,
                "faw": true
            })
            .style("fill-opacity", 1e-6)
            .on("click", function(d) {
                d3.event.stopPropagation();
                self.toggle(d);
                update(d);
                d3.select(this).text(function(d) {
                    return (d.children && d.children.length > 0) ? "\uf150" : "\uf151";
                });
            });

        filtered.text(function(d) {
            return (d.children && d.children.length > 0) ? "\uf150" : "\uf151";
        });

        // Transition nodes to their new position.
        var nodeUpdate = node.transition()
            .duration(duration)
            .attr("transform", function(d) {
                return "translate(" + d.x + "," + d.y + ")";
            });

        nodeUpdate.selectAll("*")
            .style("fill-opacity", 1);

        nodeUpdate.select("rect")
            .style("fill", function(d) {
                return statusColor(d.status);
            });

        // <magic>
//        nodeUpdate.select(".toggle-expand");
        // </magic>

        // Transition exiting nodes to the parent's new position.
        var nodeExit = node.exit().transition()
            .duration(duration)
            .attr("transform", function () {
                return "translate(" + source.x + "," + source.y + ")";
            })
            .remove();

        nodeExit.selectAll("*")
            .style("fill-opacity", 1e-6);

        nodeExit.selectAll("circle")
            .style("stroke-opacity", 1e-6);

        // Stash the old positions for transition.
        nodes.forEach(function (d) {
            d.x0 = d.x;
            d.y0 = d.y;
        });
    };

    this.updateRoot = function(strRoot) {
        self.root = JSON.parse(strRoot);
        self.root.x0 = 0;
        self.root.y0 = 0;
        update(self.root);
    }
};

You can see it in action here: http://jsfiddle.net/jg0h24hr/1/

As you may notice on jsfiddle, if you wait 3 seconds the tree is updated but then, the node won't expand / reduce anymore when you click on their bottom right icon. However if I uncomment the "magical" line of code (line 154 on jsfiddle), then it works correctly. So I suspect either a misconception in my code, or maybe a bug in D3.js? Any idea about that?

Maybe there's some underlying invalid cached data in D3, that gets refreshed when calling d3.select ?

\$\endgroup\$
4
  • \$\begingroup\$ Welcome to Code Review! I'm afraid this question does not match what this site is about. Code Review is about improving existing, working code. Code Review is not the site to ask for help in fixing or changing what your code does. Once the code does what you want, we would love to help you do the same thing in a cleaner way! Please see our help center for more information. \$\endgroup\$ Commented Nov 24, 2015 at 13:27
  • 3
    \$\begingroup\$ Well, thank you for your comment, but this code actually works if I uncomment the line I'm talking about. I don't ask for a fix, I already have it. I just ask for ideas or suggestions. \$\endgroup\$ Commented Nov 24, 2015 at 14:13
  • \$\begingroup\$ Mods please read the OPs comment instead of voting to close. He's asking for a code review on working code, and separately also has a small question about how one line of that code works. \$\endgroup\$ Commented Nov 24, 2015 at 16:06
  • \$\begingroup\$ @Joel Please edit your question to make it clear \$\endgroup\$ Commented Nov 24, 2015 at 16:17

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.