D3 transform transition "null parsing transform attribute" - d3.js

The basic problem is that I have a drag event on a path that transitions the path a certain distance in the x axis.
When another event happens I want to return the path back to its original position before the drag event.
I tried to do this with the below code:
svg.select('#' + uniqueid + '-flare')
.select('path')
.transition().duration(1000)
.ease('linear')
.attr('transform', 'translate(0,0)');
This is giving me the warning "null parsing transform attribute" and the SVG container in the HTML shows transform="".
Using this following code works perfectly well, but is choppy. I want the smooth animation provided by the transition.
svg.select('#' + uniqueid + '-flare')
.select('path')
.attr('transform', 'translate(0,0)');
Any ideas? Thanks!
Update
Lars asked to see some more code. So.... (It is very abridged)
var dragtype, dragsum;
var wordDrag = d3.behavior.drag()
.origin(Object)
.on('drag', function (d) {
if(dragType != d.word) {
dragType = d.word;
dragSum = 0;
}
//update current position
dragSum = dragSum + parseInt(d3.event.dx);
var xMovement = parseInt(this.getAttribute('x')) + parseInt(d3.event.dx);
d3.select(this)
.attr('class', 'dragged-points')
.attr('x', xMovement);
if(uniqueId == d.word) {
svg.select('#' + uniqueId + '-flare')
.select('path')
.attr('transform', 'translate(' + dragSum + ',0)');
}
}
});
word.selectAll('text')
.data(data)
.enter().append('text')
.attr('class', 'points')
.attr('id', ... )
.attr('x', ...)
.attr('y', ...)
.call(wordDrag);
tick = setInterval(function(){
animate();
}, 1000);
function animate() {
word.transition()
.duration(1000)
.ease('linear')
.attr('x', ...)
.attr('y', ...);
svg.select('#' + uniqueId + '-flare')
.select('path')
.transition().duration(1000)
.ease('linear')
.attr('transform', 'translate(0,0)');
}

Related

D3 path.transition is not a function

So I have a piechart that all transitions will not work on with the message that they're not a function. Which is true when I dig in the console. The window.d3 har a transition function, but not d3.selectAll('path').transition
I'm a bit of a loss as to why this does not work. Obviously my selection to do the transition is wrong, but how?
(function(d3) {
'use strict';
var tooltip = d3.select('body')
.append('div')
.attr('class', 'pie-tooltip')
.style("opacity", 0);
/**
* Width and height has to be the same for a circle, the variable is in pixels.
*/
var width = 350;
var height = 350;
var radius = Math.min(width, height) / 2;
/**
* D3 allows colours to be defined as a range, beneath is input the ranges in same order as our data set above. /Nicklas
*/
var color = d3.scaleOrdinal()
.range(['#ff875e', '#f6bc58', '#eae860', '#85d280']);
var svg = d3.select('#piechart')
.append('svg')
.attr('width', width+20)
.attr('height', height+20)
.append('g')
.attr('transform', 'translate(' + ((width+20) / 2) +
',' + ((height+20) / 2) + ')');
var arc = d3.arc()
.innerRadius(0)
.outerRadius(radius);
/**
* bArc = biggerArc, this is the arc with a bigger outerRadius thats used when a user mouseovers.
*/
var bArc = d3.arc()
.innerRadius(0)
.outerRadius(radius*1.05);
var pie = d3.pie()
.value(function(d){
return d.value;
})
.sort(null);
var path = svg.selectAll('path')
.data(pie(data))
.enter()
.append('path')
.attr('d', arc)
.attr('fill', function(d) {
return color(d.data.color);
});
path.transition()
.duration(600)
.attrTween("d", makePieAnimation);
path.on("mouseover", function(d){
d3.select(this)
.attr("width", width+10)
.attr("height", height+10);
tooltip.transition()
.duration(200)
.style("opacity", .9)
.style("display", null)
.text(d.data.label + ": " + d.data.value);
d3.select(this).transition()
.duration(300)
.style('fill', d.data.highlight).attr("d", bArc);
});
path.on("mousemove", function(){
tooltip.style("top", (event.pageY-10)+"px")
.style("left",(event.pageX+10)+"px");
});
path.on("mouseout", function(d){
d3.select(this).style('fill', d.data.color);
tooltip.transition()
.duration(300)
.style("opacity", 0);
d3.select(this).transition()
.duration(300)
.attr("d", arc);
});
/**
* makePieAnimation() animates the creation of the pie, setting startangles to 0, interpolating to full circle on creation in path.transition. D3 magic.
* b is an array of arc objects.
*/
function makePieAnimation(b) {
b.innerRadius = 0;
var angles = d3.interpolate({startAngle: 0, endAngle: 0}, b);
return function(t) {
return arc(angles(t));
};
}
})(window.d3);
$.each(data, function (index, value) {
$('#legend').append('<span class="label label-legend" style="background-color: ' + value['color'] + '">' + value['label'] + ': ' + value['value'] + '</span>');
});
EDIT:
After digging around Ive found that the d3 file used by typo3 is manually edited: https://forge.typo3.org/issues/83741
I cannot see how this impacts this issue, but it does. When using a CDN with d3 v4.12.2 the error disappears.

Double bar chart creation

I want to create a bar chart like this:
There are two chart bars one below the other, the first one grows upwards while the second one grows downwards.
They have different scales and data.
This is what I created:
var doublebarSvg1 = d3.select('#doublebar')
.append('svg')
.attr('class', 'doublebarSvg1')
.attr('width', 700)
.attr('height', 400);
var doublebarSvg2 = d3.select('#doublebar')
.append('svg')
.attr('class', 'doublebarSvg2')
.attr('width', 700)
.attr('height', 400);
var margin = {top: 0, right: 0, bottom: 0, left: 50};
var width = doublebarSvg1.attr('width') - margin.left - margin.right;
var height = doublebarSvg1.attr('height') - margin.top - margin.bottom;
var x = d3.scaleBand()
.rangeRound([0, width])
.padding(0.1)
.domain(years);
var y1 = d3.scaleLinear()
.rangeRound([height, 0])
.domain([0, 100]);
var y2 = d3.scaleSqrt()
.rangeRound([height, 0])
.domain([813, 0.1]); // max value 812.05 but domain is [0, 100000]
var doublebarSvgG1 = doublebarSvg1.append('g').attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');
var doublebarSvgG2 = doublebarSvg2.append('g').attr('transform', 'translate(' + margin.left + ', ' + margin.top + ')');
////////////////////////////////////////////////////////////////////////
// Tooltip.
////////////////////////////////////////////////////////////////////////
var svgTip = doublebarSvg1.append('svg').attr('id', 'tooltip');
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-5, 0])
.html(function(d) {
return '<div><span>Country:</span> <span style=\'color:white\'>' + d.country + '</span></div>' +
'<div><span>Perc:</span> <span style=\'color:white\'>' + d.perc + '%</span></div>' +
'<div><span>Rate:</span> <span style=\'color:white\'>' + d.rate + '%</span></div>';
});
svgTip.call(tip);
////////////////////////////////////////////////////////////////////////
// Draw a single double bar
////////////////////////////////////////////////////////////////////////
makeDoublebar1();
function makeDoublebar1() {
// define the axes
var xAxis = d3.axisBottom(x);
var yAxis1 = d3.axisLeft(y1);
// create x axis
doublebarSvgG1.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, ' + height + ')')
.call(xAxis)
.selectAll('text')
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '.15em')
.attr('transform', 'rotate(-65)');
// create y axis
doublebarSvgG1.append('g')
.attr('class', 'y axis')
.call(yAxis1)
.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', 6)
.attr('dy', '.71em')
.style('text-anchor', 'end');
// create bar rect
doublebarSvgG1.selectAll('.bar')
.data(testData1) //.data(covFiltered)
.enter().append('rect')
.attr('fill', 'steelblue')
.attr('class', 'bar')
.attr('x', function(d) {
return x(d.year);
})
.attr('y', function(d) {
if(isNaN(d.perc)) {
d.perc = 0;
}
return y1(d.perc);
})
.attr('width', x.bandwidth())
.attr('height', function(d) {
if(isNaN(d.perc)) {
d.perc = 0;
}
return height - y1(d.perc);
})
.on('mouseover', function(d) {
d3.select(this).attr('fill', 'darkblue');
tip.show(d);
})
.on('mouseout', function(d) {
d3.select(this).attr('fill', 'steelblue');
tip.hide(d);
});
}
////////////////////////////////////////////////////////////////////////
// Draw a single double bar
////////////////////////////////////////////////////////////////////////
makeDoublebar2();
function makeDoublebar2() {
// define the axes
var xAxis = d3.axisBottom(x);
var yAxis2 = d3.axisLeft(y2);
// create x axis
doublebarSvgG2.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, 0)')
.call(xAxis)
.selectAll('text')
.style('text-anchor', 'end')
.attr('dx', '-.8em')
.attr('dy', '.15em')
.attr('transform', 'rotate(-65)');
// create y axis
doublebarSvgG2.append('g')
.attr('class', 'y axis')
.call(yAxis2)
.append('text')
.style('text-anchor', 'end');
// create bar rect
doublebarSvgG2.selectAll('.bar')
.data(testData2)
.enter().append('rect')
.attr('fill', 'tomato')
.attr('class', 'bar')
.attr('x', function(d) { // left start point
return x(d.year);
})
.attr('y', function(d) { // top start point
if(isNaN(d.rate)) {
d.rate = 0;
}
return 0;
})
.attr('width', x.bandwidth())
.attr('height', function(d) {
if(isNaN(d.rate)) {
d.perc = 0;
}
return y2(d.rate);
})
.on('mouseover', function(d) {
d3.select(this).attr('fill', 'red');
tip.show(d);
})
.on('mouseout', function(d) {
d3.select(this).attr('fill', 'tomato');
tip.hide(d);
});
}
PLUNKER here.
There are some problem:
if I replace .axis {display: initial;} with .axis {display: none;}, all the axis disappear but I want the horizontal line between the two chart
I would like there to be only one tooltip, which when the user hovers over any bar, comes out with a tooltip that shows both perc and rate value.
And, more importantly, is this the smartest way to create a chart like that?
Regarding the axis, since you want to keep the horizontal line, just hide the ticks and the texts:
.x.axis text,.x.axis line {
opacity: 0;
}
The tooltip problem is a bit more complex. The issue is that you're binding different data arrays to each set of bars.
Because of that, the best idea is finding the desired object in each array when you hover over a given year and getting the respective properties:
var thisPerc = testData1.find(function(e){return e.year === d.year}).perc;
var thisRate = testData2.find(function(e){return e.year === d.year}).rate;
Then you use those properties for setting the tooltip's text.
Here is the updated Plunker: http://plnkr.co/edit/tfB4TpkETgzp5GF1677p?p=preview
Finally, for your last question ("And, more importantly, is this the smartest way to create a chart like that?"), the answer is no. There are a lot of things that can (and must) be changed here, but this involves a lot of refactoring and it's arguably off topic at Stack Overflow. However, this is an adequate question for Code Review. But please read their docs first for keeping your question on topic, asking there is not the same as asking here.

How can i display tooltip at the top of each vertical bar

I have tried writing couple of equations for the same but unabe to get it aligned well. I need to display tooltip at the top of each bar.
Here is fiddle of the same
I am using mouseover events to display tooltips
sets.append("rect")
.attr("class","global")
.attr("width", xScale.rangeBand()/2)
.attr('y', function(d) {
return yScale((d.global/total)*100);
})
.attr("height", function(d){
return h - yScale((d.global/total)*100);
})
.attr('fill', function (d, i) {
return color(d.global);
})
.on('mouseover', function(d, i) {
var xPos = xScale.rangeBand()*i;
//console.log(xScale(i)); 6 190 282
console.log(xScale.rangeBand()*i);
var yPos = yScale((d.global / total) * 100);
d3.select('#hor_tooltip')
.style('left', xPos + 'px')
.style('top', yPos + 'px')
.style('display', 'block')
.html(d.global);
})
.on('mouseout', function() {
d3.select('#hor_tooltip').style('display', 'none');
})
.append("text")
.text(function(d) {
return commaFormat((d.global/total)*100);
})
.attr("font-family", "sans-serif")
.attr("font-size", "11px")
You should use d3.event inside the mouseover event handler. You can use the x,y coordinates of the event or request the bounding box of the evnet target element.
The following code should help but you still need to adjust for the height of the tootip itself:
var px = d3.event.pageX;
var py = d3.event.target.getBoundingClientRect().top;
You could also try foxTooltip.js. It has an option to always position on a specific side of an element.
https://github.com/MichaelRFox/foxToolTip.js

D3 drag error 'Cannot read property x of undefined'

I'm trying to get drag functionality to work on D3, and have copied the code directly from the developer's example.
However it seems the origin (what is being clicked) is not being passed correctly into the variable d, which leads to the error: 'Cannot read property 'x' of undefined'
The relevant code:
var drag = d3.behavior.drag()
.on("drag", function(d,i) {
d.x += d3.event.dx
d.y += d3.event.dy
d3.select(this).attr("transform", function(d,i){
return "translate(" + [ d.x,d.y ] + ")"
})
});
var svg = d3.select("body").append("svg")
.attr("width", 1000)
.attr("height", 300);
var group = svg.append("svg:g")
.attr("transform", "translate(10, 10)")
.attr("id", "group");
var rect1 = group.append("svg:rect")
.attr("rx", 6)
.attr("ry", 6)
.attr("x", 5/2)
.attr("y", 5/2)
.attr("id", "rect")
.attr("width", 250)
.attr("height", 125)
.style("fill", 'white')
.style("stroke", d3.scale.category20c())
.style('stroke-width', 5)
.call(drag);
Usually, in D3 you create elements out of some sort of datasets. In your case you have just one (perhaps, one day you'll want more than that). Here's how you can do it:
var data = [{x: 2.5, y: 2.5}], // here's a dataset that has one item in it
rects = group.selectAll('rect').data(data) // do a data join on 'rect' nodes
.enter().append('rect') // for all new items append new nodes with the following attributes:
.attr('x', function (d) { return d.x; })
.attr('y', function (d) { return d.y; })
... // other attributes here to modify
.call(drag);
As for the 'drag' event handler:
var drag = d3.behavior.drag()
.on('drag', function (d) {
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this)
.attr('transform', 'translate(' + d.x + ',' + d.y + ')');
});
Oleg's got it, I just wanted to mention one other thing you might do in your case.
Since you only have a single rect, you can bind data directly to it with .datum() and not bother with computing a join or having an enter selection:
var rect1 = svg.append('rect')
.datum([{x: 2.5, y: 2.5}])
.attr('x', function (d) { return d.x; })
.attr('y', function (d) { return d.y; })
//... other attributes here
.call(drag);

"Tick is not defined" error on Firefox, using d3

I am new to both d3 and web programming generally. I have put together a force layout graph based on https://gist.github.com/mbostock/1153292. The graph works fine in Safari, Chrome and Opera (I haven't checked IE yet).However when I try to use it in Firefox I get the error "Tick is not defined".I am using Firefox 12.
Any advice on this would be much appreciated
Thanks,
Claire
(The code is a js script file and is triggered on a mouse click, the force layout part is below.).
d3.csv("data/sharing.csv?r1", function(error, data) {
dataset = data
var nodes = {};
dataset.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] = {name:link.source});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});
var w = 500;
var h = 600;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(dataset)
.size([w-10,h-10])
.linkDistance(60)
.charge(-375)
.on("tick", tick)
.start();
//Draw svg canvas
var svg = d3.select("#svgContainer").append("svg").attr("id", "viz").attr("width", w).attr("height", h)
// Create arrowheads
svg.append("svg:defs").selectAll("marker")
.data(["end-arrow"])
.enter()
.append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.attr("fill", "black")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
//Add links between the nodes and draw arrowhead at end of it.
var path = svg.append("svg:g").selectAll("path")
.data(force.links())
.enter()
.append("svg:path")
.attr("stroke-width",2)
.attr("stroke", "black")
.attr("fill","none")
.attr("marker-end", "url(#end-arrow)");
//Draw circles for nodes
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter()
.append("svg:circle")
.attr("r", 6)
.attr("fill", "white")
.attr("stroke", "black")
.call(force.drag)
.on("mouseover", fade(.1))
.on("mouseout", fade(1))
//Label the nodes/circles
var text = svg.append("svg:g").selectAll("g")
.data(force.nodes())
.enter()
.append("svg:g")
text.append("svg:text")
.attr("x", 8)
.attr("y", ".31em")
.text(function(d) { return d.name; })
function tick() {
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
circle.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
text.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
=============REPLY TO COMMENT == FULL SCRIPT INCLUDING CALL TO CSV===
//If sharing button is clicked, load sharing data
d3.select("#sharing").on("click", function() {
d3.csv("data/sharing.csv?r1", function(error, data) {
if (error)
{//If error is not null,(i.e : something goes wrong), log the error.
window.console.log(error);
}
else
{//If file loaded correctly, log the data to the console.
dataset = data
window.console.log(dataset)
color = getColor()
vizType = "force";
//Hide date fields/buttons as they are not applicable
d3.select("#instructions").classed("hidden", true);
d3.select("#instructions2").classed("hidden", false);
d3.select("#startLabel").classed("hidden", true);
d3.select("#startDate").classed("hidden", true);
d3.select("#endLabel").classed("hidden", true);
d3.select("#endDate").classed("hidden", true);
d3.select("#removeFilter").classed("hidden", true);
d3.select("#sharing").classed("hidden", true);
d3.select("#showData").classed("hidden", false);
d3.select("#showData").attr("value", "Back to Circles Vizualization");
d3.select("#tipsData").classed("hidden", true);
d3.select("#ncpData").classed("hidden", true);
d3.select("#tipsNCPData").classed("hidden", true);
d3.select("#tipsLabel").classed("hidden", true);
d3.select("#ncpLabel").classed("hidden", true);
d3.select("#tipsNCPLabel").classed("hidden", true);
//Clear the previous viz and data
d3.select("#viz").remove();
d3.select("#stageTable").remove();
d3.select("#userTable").remove();
//Gets a count of sender records/source and stage/type
var senderCount = getSortingCount(dataset,"Sender");
var stageCount = getSortingCount(dataset,"Stage");
//create tables summarising results
var summarySenderTable = tabulate(senderCount, ["Shared", "Sender"], vizType);
var summaryStageTable = tabulate(stageCount, ["Shared", "Stage"], vizType);
var nodes = {};
// For each datapoint, check if a node exists already, if not create a new one.
dataset.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] ={name: link.source});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});
//Set the width and height for the svg, that will display the viz
var w = 500;
var h = 600;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(dataset)
.size([w-10,h-10])
.linkDistance(60)
.charge(-375)
.on("tick", tick)
.start();
//Draw svg
var svg = d3.select("#svgContainer").append("svg")
.attr("id","viz").attr("width",w).attr("height", h)
// Create arrowheads
svg.append("svg:defs").selectAll("marker")
.data(["end-arrow"])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.attr("fill", "black")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5");
//Add links between the nodes and draw arrowhead at end of it.
var path = svg.append("svg:g").selectAll("path")
.data(force.links())
.enter()
.append("svg:path")
.attr("stroke-width",2)
.attr("stroke", function(d){return color(d.ScreenName)})
.attr("fill","none")
.attr("marker-end", "url(#end-arrow)");
//Draw circles for nodes
var circle = svg.append("svg:g").selectAll("circle")
.data(force.nodes())
.enter()
.append("svg:circle")
.attr("r", 6)
.attr("fill", "white")
.attr("stroke", "black")
.call(force.drag)
.on("mouseover", fade(.1))
.on("mouseout", fade(1))
//Label nodes/circles
var text = svg.append("svg:g").selectAll("g")
.data(force.nodes())
.enter()
.append("svg:g")
text.append("svg:text")
.attr("x", 8)
.attr("y", ".31em")
.text(function(d) { return d.name; })
//Set radius for arrows and applies transform
function tick() {
path.attr("d", function(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
});
circle.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
text.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
//Allow for filter by row on stageTable
d3.select("#stage").select("#stageTable").selectAll("tr")
.on("click", function(d){
d3.select(this)
var rowText = this.childNodes[1].innerHTML
var svg = d3.select("#svgContainer").select("svg")
var path = svg.selectAll("path")
.style ("opacity", 1)
.transition()
.duration(250)
.style ("opacity", function(d){
if(d.ScreenName == rowText){
d3.selectAll("marker path").transition().style("stroke-opacity", 1);
return fade(1)
}
else{
d3.selectAll("marker path").transition().style("stroke-opacity", 0.1);
return 0.1
})
d3.select("#removeFilter").classed("hidden", false);
})
//Checks what links are connected to which(used for mouseover)
var linkedByIndex = {};
dataset.forEach(function(d) {linkedByIndex[d.source.index + "," + d.target.index] = 1;});
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] || linkedByIndex[b.index + "," + a.index] || a.index == b.index;
}
//Fades in/out circles and arrows on mouseover.
function fade(opacity) {
return function(d) {
circle.style("stroke-opacity", function(o) {
thisOpacity = isConnected(d, o) ? 1 : opacity;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
});
path.style("stroke-opacity", function(o) {
return o.source === d || o.target === d ? 1 : opacity;
});
};
}
}
})
})
Accessor for colour
function getColor(){
return color
}
Seeing the entire source code helped to clarify things. There is an if/else statement at the very top that checks for an error. The entire rest of the code is inside the else block. This is what's causing the problem.
Function declarations (such as tick() in your case) have browser-specific weird behaviour when defined inside conditional blocks. Here's a pretty good write-up that explains the differences between function declarations, function expressions and the ill-defined and inconsistently supported function statements (which is what you've inadvertently created with so much code living in an else block).
If you pull the code out of the else block, I think the behavior should be more predictable across browsers.
In general, it's not good programming practice to create enormous, long conditional blocks. Not only does it introduce the possibility of these types of errors but it can be very difficult to read and understand. Same thing goes for very deeply nested conditions.
Try to keep your conditions fairly tight so that the code living inside the conditional blocks corresponds directly to the meaning of the condition itself. You should be able to read the intention of condition and block contents out loud and they should make sense together. As much as possible, code that doesn't have to do with the condition should be at the top level of the function containing it. You can increase readability by factoring your code into meaningful functions and keeping conditions under control.
In your example above, you could do:
if (error) {
window.console.log(error);
}
else {
window.console.log(dataset);
}
dataset = data
color = getColor()
vizType = "force";
...
... rest of code
One final comment is that a tool like JSLint or JSHint to validate your code. It would point out problems like this automatically. It can be overly strict sometimes but its a good learning experience to at least understand what it's complaining about.

Resources