Arash Howaida Arash Howaida - 5 months ago 83
Javascript Question

All Brush No Zoom

I'm working with a focus + context d3 visualization. I'm using a path instead of an area chart, which is the example on Unfortunately, I can't get it working properly. I quickly noticed I wasn't the only one who struggled to re-purpose this graph. See this post:

D3.js Focus + Context Overflow

My brush works, but the zoom for the focus doesn't seem to work. I'm not sure why I haven't solved it after 3 days. I have had a lot of time and a lot of examples so that I could learn from others' mistakes. In the previous post, the url(#clip) was the issue, however, in my chart the issue seems to be more a problem with the brush/zoom function. Another thing that is tripping me up a is that I'm using d3 version 3, so the current example block isn't so easy to infer things from. There are many differences in syntax/function calling in d3 v4.

The infamous bruzh zoom block in d3.v4.js:

I'm wondering why have so many struggled with this. Maybe we're all just careless, but I'm suspecting there could also be something tricky/subtle going on here.

Can someone post a fiddle or block of a functional zoom for my chart in d3.v3? I'd love to see how you did it.

Here is my block:

Here is the full screen graph:

Many thanks


You aren't "redrawing" the line path in your brush event:

var brush = d3.svg.brush()
  .on('brush', function brushed() {
    xScaleTop.domain(brush.empty() ? xScaleBottom.domain() : brush.extent());'.line').attr('d', lineTop); //<-- REDRAW LINE'.x.axis').call(xAxisTop);

Note, you also have a brushed function which isn't being used (took me a second to find the actual event handler).

Here's your code running:

<!DOCTYPE html>

  <title>Focus + Context</title>
  <style type="text/css">
    p {
      color: white
    body {
      background-color: #282c34
  <meta charset="utf-8">
  <script src="//"></script>

<body style="overflow: hidden">
  <p>Programming underway, please stand by.</p>
    var width = 600,
      height = 400;

    var margins = {
      top: 10,
      left: 50,
      right: 50,
      bottom: 50,
      between: 50

    var bottomGraphHeight = 50;
    var topGraphHeight = height - ( + margins.bottom + margins.between + bottomGraphHeight);
    var graphWidths = width - margins.left - margins.right;

    var svg ='body')
      .attr('width', width)
      .attr('height', height)
      .style('font', '10px sans-serif');

      .attr('id', 'clip')
      .attr('width', width)
      .attr('height', height);

    var focus = svg
      .attr('transform', 'translate(' + margins.left + ',' + + ')');

    var context = svg.append('g')
      .attr('class', 'context')
      .attr('transform', 'translate(' + margins.left + ',' +
        ( + topGraphHeight + margins.between) + ')');

    var xScaleTop = d3.scale.linear().range([0, graphWidths]),
      xScaleBottom = d3.scale.linear().range([0, graphWidths]),
      yScaleTop = d3.scale.linear().range([topGraphHeight, 0]),
      yScaleBottom = d3.scale.linear().range([bottomGraphHeight, 0]);

    var xAxisTop = d3.svg.axis().scale(xScaleTop).orient('bottom'),
      xAxisBottom = d3.svg.axis().scale(xScaleBottom).orient('bottom');
    var yAxisTop = d3.svg.axis().scale(yScaleTop).orient('left');

    var lineTop = d3.svg.line()
      .x(function(d, i) {
        return xScaleTop(i);
      .y(function(d) {
        return yScaleTop(d.y2);

    var lineBottom = d3.svg.line()
      .x(function(d, i) {
        return xScaleBottom(i);
      .y(function(d) {
        return yScaleBottom(d.y2);

    var brush = d3.svg.brush()
      .on('brush', function brushed() {
        xScaleTop.domain(brush.empty() ? xScaleBottom.domain() : brush.extent());'.line').attr('d', lineTop);'.x.axis').call(xAxisTop);

    var url = "";
    d3.tsv(url, function(error, rawData) {
      var data = {
        return {
          y2: +d.wav1

      xScaleTop.domain(d3.extent(data, function(d, i) {
        return i;
      yScaleTop.domain([-.02, .02]);
      xScaleBottom.domain(d3.extent(data, function(d, i) {
        return i;
      yScaleBottom.domain([-.02, .02]);

      var topXAxisNodes = focus.append('g')
        .attr('class', 'x axis')
        .attr('transform', 'translate(' + 0 + ',' + ( + topGraphHeight) + ')')
      styleAxisNodes(topXAxisNodes, 0);

        .attr('class', 'line')
        .attr('d', lineTop);

      var topYAxisNodes = focus.append('g')

        .attr('class', 'line')
        .attr('d', lineBottom);

      var bottomXAxisNodes = context.append('g')
        .attr('transform', 'translate(0,' + bottomGraphHeight + ')')
      styleAxisNodes(bottomXAxisNodes, 0);

        .attr('class', 'x brush')
        .attr('y', -6)
        .attr('height', bottomGraphHeight + 7);

          stroke: '#000',
          'fill-opacity': 0.125,
          'shape-rendering': 'crispEdges'


    function styleLines(svg) {
          fill: 'none',
          'stroke-width': 1.5,
          stroke: 'steelblue',
          'clip-path': 'url(#clip)'

    function styleAxisNodes(axisNodes, strokeWidth) {
          fill: 'none',
          'stroke-width': strokeWidth,
          stroke: 'black'
      axisNodes.selectAll('.tick line')
          fill: 'none',
          'stroke-width': 1,
          stroke: 'black'