CODEXE

D3 Multi Line Chart Hover Scatter Plot


              .tick line{
			opacity: 0.2;
		  }
		.line{
		  fill: none;
		  stroke-width: 1.5px;
		  pointer-events:visible;
		}
		.line:hover{
			stroke-width:2.5px;
		}
		.hover-rect{
			fill:none; 
			pointer-events:visibleFill;
		}
		.hover-line {
		  stroke: steelblue;
		  stroke-width: 1px;
		  stroke-dasharray: 3,3;
		}
            	

     var margin = {top: 20, right: 20, bottom: 50, left: 70};
    var width = 500 - margin.left - margin.right;
    var height = 200 - margin.top - margin.bottom;
	
	//add svg with margin !important
	//this is svg is actually group
	var svg = d3.select("#diagram").append("svg")
				.attr("width",width+margin.left+margin.right)
				.attr("height",height+margin.top+margin.bottom)
				.append("g")  //add group to leave margin for axis
				.attr("transform","translate("+margin.left+","+margin.top+")");
	
	
	var dataset = [
		[
		 [5, 20], [480, 90], [250, 50], [100, 33], [330, 95],
         [410, 12], [475, 44], [25, 67], [85, 21], [220, 88]
		],
		[
		 [5, 220], [480, 40], [250, 68], [100, 356], [330, 876],
         [410, 56], [475, 45], [25, 3], [85, 456], [234, 88]
		]
	];
	//for each d, d[0] is the first num, d[1] is the second num
	//set y scale
	var yScale = d3.scaleLinear().rangeRound([0,height]).domain([d3.max(dataset,function(d){return d3.max(d,function(dd){return dd[1];});}),0]).nice();//show negative
	
	//add x axis
	var xScale = d3.scaleLinear().rangeRound([0,width]).domain([0,d3.max(dataset, function(d){return d3.max(d,function(dd){return dd[0];});})]).nice();//scaleBand is used for  bar chart
	var color = d3.scaleOrdinal().range(["red","blue"]);
	//sort x
	$.each(dataset,function(key,val){
		val.sort(function(a,b){
		if(a[0]< b[0]){
			return -1;
		}
		else{
			return 1;
		}
	})
	});
	
	
	var line = d3.line()
				.x(function(d){return xScale(d[0]);})
				.y(function(d){return yScale(d[1]);});
				
	
	var lineGroups = svg.selectAll("g").data(dataset).enter().append("g");
	//the data has been bind to "g", so its no need to bind data to path. Just call it
	lineGroups.append("path").attr("class","line")
		.attr("stroke",function(d){return color(d[0]);})
		.attr("d",function(d){
		return line(d);
	});

	
	//add x and y axis
	var yAxis = d3.axisLeft(yScale).tickSize(-width);
	svg.append("g").call(yAxis);
	

	var xAxis = d3.axisBottom(xScale);/*.tickFormat("");remove tick label*/
	svg.append("g").call(xAxis).attr("transform", "translate(0,"+height+")");
	
	//add label for x axis and y axis
	svg.append("text").text("Y Label")
		.attr("x",0-height/2)
		.attr("y",0-margin.left)
		.attr("dy","1em")
      	.style("text-anchor", "middle")
		.attr("transform","rotate(-90)");
	svg.append("text").text("X Label")
		.attr("x",width/2)
		.attr("y",height+margin.bottom)
      	.style("text-anchor", "middle");
	//add scatter plot
	var hoverGroup = svg.append("g").attr("class",".hover-group");
	var newArr = [];
	dataset.forEach(function(d){
		newArr = newArr.concat(d);
	});
	var circles = hoverGroup.selectAll("g").data(newArr).enter().
	append("g").attr("class","hover").append("circle").attr("visibility","hidden");
	circles.attr("cx",function(d){
			  return xScale(d[0]);//i*(width/dataset.length);
			  })
	.attr("cy",function(d){
		return yScale(d[1]);
	})//for bottom to top
	.attr("r", 3);
	circles.attr("fill",function(d){
		return "orange";
	});
		
	var xLine = hoverGroup.append("line")
	.attr("class","hover-line")
	.attr("x1",0)
	.attr("x2",0)
	.attr("y1",0)
	.attr("y2",height)
	.attr("visibility","hidden");
	
	//add rect for mouse event
	var bisectX = d3.bisector(function(d) { return d[0]; }).left;
	d3.select("svg").append("rect")
		.attr("width",width).attr("height",height)
		.attr("class","hover-rect")
		.attr("transform","translate("+margin.left+","+margin.top+")")
		.on("mousemove",function(e){
			xLine.attr("visibility","visible");
			circles.attr("visibility","hidden");
			var x = xScale.invert(d3.mouse(this)[0]);
			var i = bisectX(newArr, x, 1);//0 is the first point
			i=i==newArr.length?newArr.length-1:i;
		
			datax = (newArr[i][0]-x)>(x-newArr[i-1][0])?newArr[i-1][0]:newArr[i][0];

			circles.each(function(d){//linear search used for small data set
				if(this.getAttribute("cx")==xScale(datax)){
					this.setAttribute("visibility","visible");
				}
			});
			xLine.attr("transform", "translate(" + xScale(datax) + ",0)");
			
		})	
		.on("mouseout",function(e){
			xLine.attr("visibility","hidden");
			circles.attr("visibility","hidden");
		});