Mike Bostock's Data Driven Documents library uniquely allows for DOM elements to represent data interactively, and provides visually interesting transitions to attract and direct the viewer's attention. The New York Times, which employs Mr. Bostock as a graphical designer, makes excellent use of the library to convey information to the online reader that the information alone cannot. But why is D3 special, when similar functions can be performed by existing libraries like jQuery?

D3's tools can cover some of the same ground as jQuery, but it is capable of so much more when dynamically updating a web page with new data, and displaying that change. While jQuery can be used to add and manipulate DOM elements, D3 supplies better methods to use the DOM to project data.

Let's look at some code to compare. This jQuery code will produce a simple bar chart using jQuery, courtesy of Derek Mack (source here: http://provide.smashingmagazine.com/graphtutfiles/ex_03.html)

The jQuery method requires that we create containers for each graph element, and for the data that we will use in the graph. We also need to determine the scale for the graph, and set that before we even begin handling the data in visual form.

var figureContainer = $('<div id="figure"></div>');
var graphContainer = $('<div class="graph"></div>');
var barContainer = $('<div class="bars"></div>');
var data = $(data);
var container = $(container);

chartData = tableData.chartData();      
chartYMax = tableData.chartYMax();
columnGroups = tableData.columnGroups();

    chartYMax: function() {
        var chartData = this.chartData();
        var chartYMax = Math.ceil(Math.max.apply(Math, chartData) / 1000) * 1000;
        return chartYMax;
        },

    columnGroups: function() {
        var columnGroups = [];
        var columns = data.find('tbody tr:eq(0) td').length;
        for (var i = 0; i < columns; i++) {
            columnGroups[i] = [];
            data.find('tbody tr').each(function() {
                columnGroups[i].push($(this).find('td').eq(i).text());
            });
        }
        return columnGroups;
    }

    var tableData = {
    chartData: function() {
        var chartData = [];
        data.find('tbody td').each(function() {
            chartData.push($(this).text());
        });
        return chartData;
    },

    chartYMax: function() {
        var chartData = this.chartData();
        var chartYMax = Math.ceil(Math.max.apply(Math, chartData) / 1000) * 1000;
        return chartYMax;
    },

    yLegend: function() {
        var chartYMax = this.chartYMax();
        var yLegend = [];
        var yAxisMarkings = 5;                      
        for (var i = 0; i < yAxisMarkings; i++) {
            yLegend.unshift(((chartYMax * i) / (yAxisMarkings - 1)) / 1000);
        }
        return yLegend;
    },

    columnGroups: function() {
        var columnGroups = [];
        var columns = data.find('tbody tr:eq(0) td').length;
        for (var i = 0; i < columns; i++) {
            columnGroups[i] = [];
            data.find('tbody tr').each(function() {
                columnGroups[i].push($(this).find('td').eq(i).text());
            });
        }
        return columnGroups;
    }

    var xLegend = tableData.xLegend();      
    var xAxisList   = $('<ul class="x-axis"></ul>');
    $.each(xLegend, function(i) {           
        var listItem = $('<li><span>' + this + '</span></li>')
        .appendTo(xAxisList);
    });
       xAxisList.appendTo(graphContainer);

    var yLegend = tableData.yLegend();
    var yAxisList   = $('<ul class="y-axis"></ul>');
    $.each(yLegend, function(i) {           
        var listItem = $('<li><span>' + this + '</span></li>')
        .appendTo(yAxisList);
    });
    yAxisList.appendTo(graphContainer);     

    barContainer.appendTo(graphContainer);      

    graphContainer.appendTo(figureContainer);

    figureContainer.appendTo(container);

    function displayGraph(bars, i) {        
        if (i < bars.length) {
            $(bars[i].bar).animate({
            height: bars[i].height
             }, 800);
         barTimer = setTimeout(function() {
            i++;                
            displayGraph(bars, i);
        }, 100);
    }

    $.each(columnGroups, function(i) {
        var barGroup = $('<div class="bar-group"></div>');
        for (var j = 0, k = columnGroups[i].length; j < k; j++) {
            var barObj = {};
            barObj.label = this[j];
            barObj.height = Math.floor(barObj.label / chartYMax * 100) + '%';
            barObj.bar = $('<div class="bar fig' + j + '"><span>' + barObj.label +                    '</span></div>')
            .appendTo(barGroup);
            bars.push(barObj);
        }
        barGroup.appendTo(barContainer);            
    });

function resetGraph() {
    $.each(bars, function(i) {
    $(bars[i].bar).stop().css('height', 0);
    });`

Now, here is some D3 code for creating a bar chart:

<svg class="chart" data-bar-chart data-data="23,43,10,13,10,20,30,23,43,10,13,10,20,30" data-bar-width="15"></svg>
<svg class="chart" data-bar-chart data-data="19,65,23,31,32,44,5,32,23,23,54,65" data-bar-width="15"></svg>
<svg class="chart" data-bar-chart data-data="34,43,65,21,5,43,43,32,32,12,31,32,12,32,23,12" data-bar-width="15"></svg>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>

$('[data-bar-chart]').each(function (i, svg) {
    var $svg = $(svg);
    var data = $svg.data('data').split(',').map(function (datum) {
    return parseFloat(datum);
});

var barWidth = parseFloat($svg.data('bar-width')) || 15;
var barSpace = parseFloat($svg.data('bar-space')) || 0.5;
var chartHeight = $svg.outerHeight();

var y = d3.scale.linear()
.domain([0, d3.max(data)])
.range([0, chartHeight]);

d3.select(svg)
    .selectAll("rect")
    .data(data)
    .enter().append("rect")
    .attr("class", "bar")
    .attr("width", barWidth)
    .attr("x", function (d, i) { return barWidth*i + barSpace*i; })
    .attr("y", chartHeight)
    .attr("height", 0)
    .transition()
    .delay(function (d, i) { return i*100; })
    .attr("y", function (d, i) { return chartHeight-y(d); })
    .attr("height", function (d) { return y(d); });
    });
</script>

I have omitted the HTML and CSS for these code snippets, but they are similar enough that their absence doesn't make a significant difference. While D3 cannot match jQuery for versatility and browser compatibility, it never was intended to, and is unparalleled in its ability to represent data within a web page.