Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

denewey's avatar

How to get my d3 js script to see the sankey.js file

I'm working on a laravel site where the data is fed from the controller through a jQuery file. I am trying to create a sankey chart, but I can't get the d3 code to recognize the sankey.js file, even though it seems that it is correctly included. The error I am getting is 'Uncaught TypeError: d3.sankey is not a function'.

I have tried embedding the d3 code in the view file html with two "" tags to include the d3 package and the sankey.js file as a plugin. When I pass the data from the js file I have to do it by use of a function and am unable to access the sankey.js file within that function, resulting in the same error as above.

I am now trying it by keeping the d3 code in the jQuery file (chart.js) and passing the completed chart, which to me would be more eloquent, but again I get the "d3.sankey is not a function" error.

This is the view file:

<div class="container">

        <div id="visitors-progress" class="block block-condensed {{ getDateRangeKey(getMainControlsSelections()->dateRange) }}">

            <div class="app-heading">
                <div class="title">
                    <h2>Visitors Progress Graphed</h2>
                    <p>Sankey Visualization</p>
                </div>
            </div>

            <div class="block-content">

                <div class="row">

                    <div id="sk-chart">
                        <div id="sankey_viz"></div>
                        
                    </div>
                </div>
            </div>
        </div>
    </div>

This is the chart.js file:

define([
    '../../../../assets/app/js/progress/sankey.js',
    'progress/chart/skchart',
    'https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js'
], function(sankey, SkClass, d3) {

    var ProgressChartClass = function($element){
        // Private vars
        var _this = this,
            _element = null,
            _container = null;

        // Public var
        this.SkChart = null;
        this.data = [];

        // Private Methods
        var _construct = function () {
            if (!_.isUndefined($element)) { _element = $element; }
            else { _element = $('#visitors-progress'); }

            var element = _element.find("#sk-chart");
            if (element.length) {
                _this.SkChart = new SkClass(element);
            }
            $(window).on("resize", _onResize);
        };

        var _onResize = function () {
            _.delay(function(){
                _this.Resize();
            }, 250);
        };

        // Public Methods
        this.SetLoading = function (option) {
            if (_.isUndefined(option)) { option = false; }

            if (option) { app.block.loading.start(_element); }
            else { app.block.loading.finish(_element); }
        };

        this.Update = function (data) {
            console.log('Sankey, Chart js 41, data for Chart: ', data);
            if (_.isUndefined(data) || _.isNull(data)) {
                data = {};
            }
            if (this.SkChart && data) {
                //d3 code starts here
                var units = "Visitors";

                var margin = {top: 10, right: 10, bottom: 10, left: 10},
                    width = 1200 - margin.left - margin.right,
                    height = 740 - margin.top - margin.bottom;

                var formatNumber = d3.format(".0f"),    // zero decimal places
                    format = function (d) {
                        return formatNumber(d) + " " + units;
                    },
                    color = d3.scale.category20();

                // append the svg canvas to the page
                var svg = d3.select("#sk-chart").append("svg")
                    .attr("width", width + margin.left + margin.right)
                    .attr("height", height + margin.top + margin.bottom)
                    .append("g")
                    .attr("transform",
                        "translate(" + margin.left + "," + margin.top + ")");

                // Set the progress diagram properties
                var sankey = d3.sankey()
                    .nodeWidth(36)
                    .nodePadding(10)
                    .size([width, height]);

                var path = sankey.link();

                // load the data
                d3.json(data, function (error, graph) {
                    console.log('Graph: ', graph);
                    var nodeMap = {};
                    graph.nodes.forEach(function (x) {
                        nodeMap[x.name] = x;
                    });
                    graph.links = graph.links.map(function (x) {
                        return {
                            source: nodeMap[x.source],
                            target: nodeMap[x.target],
                            value: x.value
                        };
                    });

                    sankey
                        .nodes(graph.nodes)
                        .links(graph.links)
                        .layout(32);

                    // add in the links
                    var link = svg.append("g").selectAll(".link")
                        .data(graph.links)
                        .enter().append("path")
                        .attr("class", "link")
                        .attr("d", path)
                        .style("stroke-width", function (d) {
                            return Math.max(1, d.dy);
                        })
                        .sort(function (a, b) {
                            return b.dy - a.dy;
                        });

                    // add the link titles
                    link.append("title")
                        .text(function (d) {
                            return d.source.name + " → " +
                                d.target.name + "\n" + format(d.value);
                        });

                    // add in the nodes
                    var node = svg.append("g").selectAll(".node")
                        .data(graph.nodes)
                        .enter().append("g")
                        .attr("class", "node")
                        .attr("transform", function (d) {
                            return "translate(" + d.x + "," + d.y + ")";
                        })
                        .call(d3.behavior.drag()
                            .origin(function (d) {
                                return d;
                            })
                            .on("dragstart", function () {
                                this.parentNode.appendChild(this);
                            })
                            .on("drag", dragmove));

                    // add the rectangles for the nodes
                    node.append("rect")
                        .attr("height", function (d) {
                            return d.dy;
                        })
                        .attr("width", sankey.nodeWidth())
                        .style("fill", function (d) {
                            return d.color = color(d.name.replace(/ .*/, ""));
                        })
                        .style("stroke", function (d) {
                            return d3.rgb(d.color).darker(2);
                        })
                        .append("title")
                        .text(function (d) {
                            return d.name + "\n" + format(d.value);
                        });

                    // add in the title for the nodes
                    node.append("text")
                        .attr("x", -6)
                        .attr("y", function (d) {
                            return d.dy / 2;
                        })
                        .attr("dy", ".35em")
                        .attr("text-anchor", "end")
                        .attr("transform", null)
                        .text(function (d) {
                            return d.name;
                        })
                        .filter(function (d) {
                            return d.x < width / 2;
                        })
                        .attr("x", 6 + sankey.nodeWidth())
                        .attr("text-anchor", "start");

                    // the function for moving the nodes
                    function dragmove(d) {
                        d3.select(this).attr("transform",
                            "translate(" + (
                                d.x = Math.max(0, Math.min(width - d.dx, d3.event.x))
                            ) + "," + (
                                d.y = Math.max(0, Math.min(height - d.dy, d3.event.y))
                            ) + ")");
                        sankey.relayout();
                        link.attr("d", path);
                    }
                });
            }
        };

        this.Resize = function () {
            if (_.isNull(this.Graph)) { return; }

            this.Graph.configure({
                width: _container.width(),
                height: _container.height()
            });

            this.Render();
        };

        // Init
        _construct();
    };

    return ProgressChartClass;

});

The data does get passed to the d3 code but the execution stops on the line:

var sankey = d3.sankey()

because the code is not seeing the sankey.js file as far as I can tell. Am I not including it correctly at the top of the chart.js file? Or should I include it differently? Any help would be greatly appreciated.

0 likes
2 replies
denewey's avatar

I've installed the d3-sankey package onto the server with npm. It is now loaded into the node_modules directory, but I am still not able to get the code to see it. Obviously, I am not well versed in this area. I copied the package over to the 'public/assets/app/js/progress' directory and I have tried all sorts of approaches to include the correct path to the d3-sankey package in the 'define' section of the chart.js file, yet I continually get a 404 (Not Found) error being generated from the require.min.js file. Can someone point me to the correct path?

denewey's avatar
denewey
OP
Best Answer
Level 2

I resolved this by embedding the javascript code that creates the sankey chart in the view file. The code I got for this feature was downloaded from a site that I can't find now, but one of the files in the download was a sankey.js file which I called with a script tag before the script tag that holds the code that creates the chart. I had to revise the jQuery file into 3 files 'js/progress.js', 'js/progress/skchart.js' and 'js/progress/chart/skchart.js'. The Ajax call is in the second jQuery file and the response is updated to the 'displayChart()' function in the progress.blade.php file.

this.Update = function (data) {
            if (_.isUndefined(data) || _.isNull(data)) {
                data = {};
            }

            if (this.SkChart && data) {
                this.SkChart.Update(displayChart(data));
            }
        };

Code in the progress.blade.php file:

var displayChart = function(data) {
                                    console.log('Progress blade 100: chart data: ', data);

                                    if (!data || Object.keys(data).length === 0 && data.constructor === Object) {
                                        console.log('Progress blade 402: chart data is undefined');
                                        return '';
                                    } 

                                    var units = "Visitors";

                                    var margin = {top: 10, right: 10, bottom: 10, left: 10},
                                        width = 1200 - margin.left - margin.right,
                                        height = 740 - margin.top - margin.bottom;
...

Please or to participate in this conversation.