Skip to main content
cancel
Showing results for 
Search instead for 
Did you mean: 

Join us for an expert-led overview of the tools and concepts you'll need to become a Certified Power BI Data Analyst and pass exam PL-300. Register now.

Reply
Anonymous
Not applicable

Using D3.JS visual in PowerBI and I can't get it to work.

Hi all, 
I am new to the D3.js visual in PowerBI and can't seem to get it to work. I have followed the tutorial the best I know how without any luck. Here is the tutorial I am referring to: https://azurebi-docs.jppp.org/powerbi-visuals/d3js.html?tabs=docs%2Cdocs-open

 

I have an animated multiple line chart: the lines are the different ids in my dataset, the animation is based off of a timestamp column in which the datapoints (and the connecting lines) display based on each datapoint's timestamp. There is a replay button to replay the animation. 
My data has the following columns: id, left, top, timestamp. One id may have multiple rows. y-axis is left, x-axis is top. 
I also have a background image for the plotArea.

This is what i have so far in the D3.JS editor:

 

 

 

 

var margin = {top: 20, right: 30, bottom: 50, left: 40},
    width = pbi.width - margin.left - margin.right,
    height = pbi.height - margin.top - margin.bottom;

var x = d3.scaleLinear()      
    .domain([0, 1800])
    .range([margin.left, width - margin.right]).clamp(true);

var y = d3.scaleLinear()     
    .domain([0, 1200])      
    .range([margin.top, height - margin.bottom]).clamp(true);

var svg = d3.select("#chart")
      	.attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom);

var plotAreaWidth = pbi.width - margin.left - margin.right;
var plotAreaHeight = pbi.height - margin.top - margin.bottom;

var plotArea = svg.append("g")
               .attr("class", "plot-area")
               .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

plotArea.append("image")
        .attr("xlink:href", "backdrop.png") 
        .attr("width", plotAreaWidth)
        .attr("height", plotAreaHeight);

svg.append("g")
   .attr("transform", "translate(0," + (height - margin.bottom) + ")")
   .call(d3.axisBottom(x).ticks(4).tickSizeOuter(0))
   .call(function(g) { g.select(".domain").remove(); })
   .call(function(g) { g.selectAll(".tick line").clone()
   						.attr("y2", -height)
    					.attr("stroke-opacity", 0.1); })
   .append("text")
   .attr("x", width - 4)
   .attr("y", -4)
   .attr("font-weight", "bold")
   .attr("text-anchor", "end")
   .attr("fill", "currentColor")
   .text("Top");

svg.append("g")
   .attr("transform", "translate(" + margin.left + ",0)")
   .call(d3.axisLeft(y).ticks(6).tickSizeOuter(0))
   .call(function(g) { g.select(".domain").remove(); })
   .call(function(g) { g.selectAll(".tick line").clone()
                    	.attr("x2", width)
                    	.attr("stroke-opacity", 0.1); })
   .append("text")
   .attr("x", 4)
   .attr("y", margin.top - 10)
   .attr("font-weight", "bold")
   .attr("text-anchor", "start")
   .attr("fill", "currentColor")
   .text("Left");

var loadChart = function(){
pbi.dsv(function(data){
        data.sort(function(a, b) { return new Date(a.timestamp) - new Date(b.timestamp); });

	var ids = Array.from(new Set(data.map(function(d) { return d.id; })));
  	
  	var color = d3.scaleOrdinal()
                .domain(ids)
                .range(d3.schemeCategory10);

    ids.forEach(function(id) {
       var idData = data.filter(function(d) { return d.id === id; });
       var lineData = idData; // Include all data points for the line

       plotArea.append("path")
               .datum(lineData)
               .attr("fill", "none")
               .attr("stroke", color(id))
               .attr("stroke-width", 2.5)
               .attr("stroke-linejoin", "round")
               .attr("stroke-linecap", "round")
               .attr("d", d3.line()
                        .x(function(d) { return x(d.top); })
                        .y(function(d) { return y(d.left); }));
    });
  
    data.forEach(function(d, i) {
         	setTimeout(function() {
             	plotArea.append("circle")
                        .attr("cx", x(d.top))
                        .attr("cy", y(d.left))
                        .attr("r", 4)
                        .attr("fill", "steelblue")
                        .on("mouseover", function() {
                            d3.select(this.nextSibling).style("visibility", "visible");
                        })
                        .on("mouseout", function() {
                            d3.select(this.nextSibling).style("visibility", "hidden");
                        });

                plotArea.append("text")
                        .attr("x", x(d.top))
                        .attr("y", y(d.left) - 10)
                        .attr("text-anchor", "middle")
                        .text(d.timestamp.toLocaleString())
                        .attr("fill", "black")
                        .style("visibility", "hidden");

                    ids.forEach(function(id) {
                        var idData = data.filter(function(item) { return item.id === id && item.timestamp <= d.timestamp; });
                        var lineData = idData; // Include all data points for the line

                        plotArea.select('path[stroke="' + color(id) + '"]')
                            .datum(lineData)
                            .attr("d", d3.line()
                                .x(function(d) { return x(d.top); })
                                .y(function(d) { return y(d.left); }));
                    });
                }, i * 100);
            });
  
});
}

loadChart();
var replayButton = d3.select("body").append("button").attr("id", "replay-button").text("Replay");
        replayButton.on("click", function() {
            d3.select("#chart").selectAll("*").remove(); // Clear the chart
            drawConnectedScatterplot(data);
        });

 

 

 

 


I have tried different variations and have tried simplifying it by focusing on simply getting a line chart to display but i can't get it to work, nothing gets generated at all - all i see is a blank placeholder.
 
This is the html version that works as expected:

 

 

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Connected Scatterplot</title>
    <script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
    <div id="chart"></div>

    <script>
        function drawConnectedScatterplot(data) {
            // Sort the data based on the timestamp
            data.sort(function(a, b) { return a.timestamp - b.timestamp; });

            var width = 600;
            var height = 400;
            var margin = { top: 20, right: 30, bottom: 50, left: 40 };

            var x = d3.scaleLinear()      
                .domain([0, 1800])
                .range([margin.left, width - margin.right]).clamp(true);

            var y = d3.scaleLinear()     
                .domain([0, 1200])      
                .range([margin.top, height - margin.bottom]).clamp(true);

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

            var plotAreaWidth = width - margin.left - margin.right;
            var plotAreaHeight = height - margin.top - margin.bottom;

            var plotArea = svg.append("g")
                .attr("class", "plot-area")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

            // Append background image to the plot area
            plotArea.append("image")
                .attr("xlink:href", "backdrop.png")
                .attr("width", plotAreaWidth)
                .attr("height", plotAreaHeight);

                svg.append("g")
                .attr("transform", "translate(0," + (height - margin.bottom) + ")")
                .call(d3.axisBottom(x).ticks(4).tickSizeOuter(0))
                .call(function(g) { g.select(".domain").remove(); })
                .call(function(g) { g.selectAll(".tick line").clone()
                    .attr("y2", -height)
                    .attr("stroke-opacity", 0.1); })
                .append("text")
                .attr("x", width - 4)
                .attr("y", -4)
                .attr("font-weight", "bold")
                .attr("text-anchor", "end")
                .attr("fill", "currentColor")
                .text("Top");

            svg.append("g")
                .attr("transform", "translate(" + margin.left + ",0)")
                .call(d3.axisLeft(y).ticks(6).tickSizeOuter(0))
                .call(function(g) { g.select(".domain").remove(); })
                .call(function(g) { g.selectAll(".tick line").clone()
                    .attr("x2", width)
                    .attr("stroke-opacity", 0.1); })
                .append("text")
                .attr("x", 4)
                .attr("y", margin.top - 10)
                .attr("font-weight", "bold")
                .attr("text-anchor", "start")
                .attr("fill", "currentColor")
                .text("Left");

            var ids = Array.from(new Set(data.map(function(d) { return d.id; })));

            var color = d3.scaleOrdinal()
                .domain(ids)
                .range(d3.schemeCategory10);

            ids.forEach(function(id) {
                var idData = data.filter(function(d) { return d.id === id; });
                var lineData = idData; // Include all data points for the line

                plotArea.append("path")
                    .datum(lineData)
                    .attr("fill", "none")
                    .attr("stroke", color(id))
                    .attr("stroke-width", 2.5)
                    .attr("stroke-linejoin", "round")
                    .attr("stroke-linecap", "round")
                    .attr("d", d3.line()
                        .x(function(d) { return x(d.top); })
                        .y(function(d) { return y(d.left); }));
            });

            

            data.forEach(function(d, i) {
                setTimeout(function() {
                    plotArea.append("circle")
                        .attr("cx", x(d.top))
                        .attr("cy", y(d.left))
                        .attr("r", 4)
                        .attr("fill", "steelblue")
                        .on("mouseover", function() {
                            d3.select(this.nextSibling).style("visibility", "visible");
                        })
                        .on("mouseout", function() {
                            d3.select(this.nextSibling).style("visibility", "hidden");
                        });

                    plotArea.append("text")
                        .attr("x", x(d.top))
                        .attr("y", y(d.left) - 10)
                        .attr("text-anchor", "middle")
                        .text(d.timestamp.toLocaleString())
                        .attr("fill", "black")
                        .style("visibility", "hidden");

                    ids.forEach(function(id) {
                        var idData = data.filter(function(item) { return item.id === id && item.timestamp <= d.timestamp; });
                        var lineData = idData; // Include all data points for the line

                        plotArea.select('path[stroke="' + color(id) + '"]')
                            .datum(lineData)
                            .attr("d", d3.line()
                                .x(function(d) { return x(d.top); })
                                .y(function(d) { return y(d.left); }));
                    });
                }, i * 100);
            });
        }

        var data = [
            { id: "ID1", left: 744, top: 987, timestamp: new Date("2024-01-01T05:00:00") },
            { id: "ID1", left: 828, top: 938, timestamp: new Date("2024-01-01T10:00:00") },
            { id: "ID1", left: 886, top: 883, timestamp: new Date("2024-01-01T11:00:00") },
            { id: "ID1", left: 940, top: 734, timestamp: new Date("2024-01-01T12:00:00") },
            { id: "ID1", left: 997, top: 638, timestamp: new Date("2024-01-01T13:00:00") },
            { id: "ID1", left: 1056, top: 577, timestamp: new Date("2024-01-01T14:00:00") },
            { id: "ID1", left: 1101, top: 516, timestamp: new Date("2024-01-01T15:00:00") },
            { id: "ID1", left: 1146, top: 473, timestamp: new Date("2024-01-01T16:00:00") },
            { id: "ID1", left: 1179, top: 411, timestamp: new Date("2024-01-01T17:00:00") },
            { id: "ID1", left: 1212, top: 372, timestamp: new Date("2024-01-01T18:00:00") },
            { id: "ID2", left: 1417, top: 312, timestamp: new Date("2024-01-01T06:00:00") },
            { id: "ID2", left: 1436, top: 330, timestamp: new Date("2024-01-01T11:30:00") },
            { id: "ID2", left: 1448, top: 355, timestamp: new Date("2024-01-01T12:45:00") },
            { id: "ID2", left: 1445, top: 386, timestamp: new Date("2024-01-01T13:30:00") },
            { id: "ID2", left: 1444, top: 420, timestamp: new Date("2024-01-01T14:15:00") },
            { id: "ID2", left: 1430, top: 467, timestamp: new Date("2024-01-01T15:00:00") },
            { id: "ID2", left: 1414, top: 512, timestamp: new Date("2024-01-01T15:45:00") },
            { id: "ID2", left: 1392, top: 573, timestamp: new Date("2024-01-01T16:30:00") },
            { id: "ID2", left: 1363, top: 628, timestamp: new Date("2024-01-01T17:15:00") },
            { id: "ID2", left: 1318, top: 699, timestamp: new Date("2024-01-01T18:00:00") }
        ];

        drawConnectedScatterplot(data);

        var replayButton = d3.select("body").append("button").attr("id", "replay-button").text("Replay");
        replayButton.on("click", function() {
            d3.select("#chart").selectAll("*").remove(); // Clear the chart
            drawConnectedScatterplot(data);
        });
    </script>
</body>
</html>

 

 

 

Since i am in powerbi desktop im finding it excessively difficult to find the issue/s since i can't debug as i would in the browser.


Any guidance on how to get it to work is much appeciated!

1 ACCEPTED SOLUTION
Anonymous
Not applicable

HI @Anonymous,

I'd like to suggest you to check the d3.js visual official support page to know the usage and the limitations at the first:

Power BI D3.js Visual - Azure BI (jppp.org)

Regards,

Xiaoxin Sheng

View solution in original post

1 REPLY 1
Anonymous
Not applicable

HI @Anonymous,

I'd like to suggest you to check the d3.js visual official support page to know the usage and the limitations at the first:

Power BI D3.js Visual - Azure BI (jppp.org)

Regards,

Xiaoxin Sheng

Helpful resources

Announcements
Join our Fabric User Panel

Join our Fabric User Panel

This is your chance to engage directly with the engineering team behind Fabric and Power BI. Share your experiences and shape the future.

June 2025 Power BI Update Carousel

Power BI Monthly Update - June 2025

Check out the June 2025 Power BI update to learn about new features.

June 2025 community update carousel

Fabric Community Update - June 2025

Find out what's new and trending in the Fabric community.