Shubham Sharma
Shubham Sharma

Reputation: 1831

Why click event on d3.js resuable chart creating problem?

I have used the pattern of reusable charts described by mike bostock. I am facing one problem.

Pie Chart functionality :-
1. When i click on a tab of pie chart, it should rotate and put the color which i clicked, on top.

Problem :-
1. When i click on first chart. The functionality is implemented but it is implemented on 2nd chart.

I think the problem is related to global declaration. But i haven't declared anything globally.

Please someone help me with this pretty basic task.

This is my js fiddle link where you can check this anamoly. https://jsfiddle.net/a0u2nk47/

<html>
    <head>
        <script type="text/javascript" src="d3.v4.min.js"></script>
        <script type="text/javascript" src="d3-path.v1.min.js"></script>
        <script type="text/javascript" src="d3-shape.v1.min.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
        <style>
            body{
                height: 100%;
                width: 100%;
                /*background-image: url("Computer.png");*/
            }
            p{
                float:left;
            }
        </style>
    </head>
    <!-- <img src=Computer.png width="70%" height="100%"> -->
    <p id="chart1"></p>
    <p id="chart2"></p>

<body >

    <script>
        function piechart(){
            var width = 400;
            var height = 400;
            var colors = ['red','green','blue']
            var innerRadius = 40;
            var outerRadius = 60;
            var index = 0;

            var pie = d3.pie()
                .sort(null);

            var arc = d3.arc().innerRadius(innerRadius).outerRadius(outerRadius).cornerRadius(5).padAngle(0.05);                

            function chart(selection){
                selection.each(function() {

                    svg = d3.select(this)
                            .append('svg')
                            .attr('width',width)
                            .attr('height',height)
                            .append('g')
                            .attr('transform','translate('+width/2+','+height/2+')')

                    path = svg.selectAll("path")
                       .data(pie(data))
                       .enter()
                       .append('path')
                       .attr('d',function(d){return arc(d)})
                       .style('fill',function(d,i){return colors[i]})
                       .style('stroke','black')
                       .each(function(d) { this._current = d; });

                    updateData = function(){
                        path.data(pie(data))
                        path.transition().duration(2000).attrTween("d", arcTween);
                    }
                    updateWidth = function(){
                        innerRadius = width*0.6
                        outerRadius = width*0.8
                        arc.innerRadius(innerRadius).outerRadius(outerRadius);
                        path.transition().duration(1000).attr('d',function(d){return arc(d)});                                                      
                    }
                    path.on("click",chart.click);

                })
            }
            function arcTween(a) {                
              var i = d3.interpolate(this._current, a);
              this._current = i(0);
              return function(t) {
                return arc(i(t));
              };
            }

            chart.data = function(value){

                if(!arguments.length) return data;
                data = value;
                if(typeof updateData === 'function') updateData();
                return chart;
            }

            chart.width = function(value){
                if(!arguments.length) return width;
                width = value;
                if(typeof updateWidth === 'function') updateWidth();
                return chart;
            }

            chart.click = function(value){
                debugger;
                index = this.__data__.index
                console.log(index)
                var rotate = 0-(value.startAngle + value.endAngle)/2 / Math.PI * 180;

                // Transition the pie chart
                svg.transition()
                   .attr("transform", "translate("+width/2+","+height/2+") rotate(" + rotate + ")")
                   .duration(1000);
            }
            return chart
        }
        data = [1,5,8];
        var piechart1 = piechart().data(data);
        d3.select('#chart1').call(piechart1);
        var piechart2 = piechart().data(data);
        d3.select('#chart2').call(piechart2);
    </script>
</body>

Upvotes: 0

Views: 39

Answers (1)

Shashank
Shashank

Reputation: 5670

As you're transitioning/transforming the <g> element which is the parent of the paths you click on, you can just use this.parentNode to transform it.

For example, in the click handler, change the following:

svg.transition().attr('....

to

d3.select(this.parentNode).transition().attr('...

Although I'd suggest you assign an ID to each of the charts to separate them out in case of a reusable chart logic and use this newly created ID for all other functionalities instead of depending on the global variables. For instance:

var piechart1 = piechart(1).data(data); // pass an ID here

and assign it the following way:

svg.append('g').attr('id', 'container_' + id) // assign the ID

Here's a snippet followed with a fiddle fork:

			function piechart(id){
				var width = 300;
				var height = 300;
				var colors = ['red','green','blue']
				var innerRadius = 40;
				var outerRadius = 60;
				var index = 0;

				var pie = d3.pie()
				    .sort(null);

				var arc = d3.arc().innerRadius(innerRadius).outerRadius(outerRadius).cornerRadius(5).padAngle(0.05);				

				function chart(selection){
					selection.each(function() {
						svg = d3.select(this)
								.append('svg')
								.attr('width',width)
								.attr('height',height)
								.append('g').attr('id', 'container_' + id)
								.attr('transform','translate('+width/2+','+height/2+')')
						
						path = svg.selectAll("path")
						   .data(pie(data))
						   .enter()
						   .append('path')
						   .attr('d',function(d){return arc(d)})
						   .style('fill',function(d,i){return colors[i]})
						   .style('stroke','black')
						   .each(function(d) { this._current = d; });

						path.on("click",chart.click);

					})
				}
				chart.data = function(value){

					if(!arguments.length) return data;
					data = value;
					if(typeof updateData === 'function') updateData();
					return chart;
				}

				chart.click = function(value){
					index = this.__data__.index
			        var rotate = 0-(value.startAngle + value.endAngle)/2 / Math.PI * 180;
										    
			        // Transition the pie chart
			        d3.select(this.parentNode).transition()
			           .attr("transform", "translate("+width/2+","+height/2+") rotate(" + rotate + ")")
			           .duration(1000);
				}
				return chart
			}
			data = [1,5,8];
			var piechart1 = piechart(1).data(data);
			d3.select('#chart1').call(piechart1);
			var piechart2 = piechart(2).data(data);
			d3.select('#chart2').call(piechart2);
<html>
	<head>
		<script src="https://d3js.org/d3.v4.min.js"></script>
		<script src="https://d3js.org/d3-path.v1.min.js"></script>
		<script src="https://d3js.org/d3-shape.v1.min.js"></script>
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
		<style>
			body{
				height: 100%;
				width: 100%;
				/*background-image: url("Computer.png");*/
			}
			p{
				float:left;
			}
		</style>
	</head>
	<!-- <img src=Computer.png width="70%" height="100%"> -->
	<p id="chart1"></p>
	<p id="chart2"></p>

	<body >

	</body>
</html>

https://jsfiddle.net/rL96uv4g/

Hope this helps.

Upvotes: 1

Related Questions