How to link D3 zoom and pan controls
<p>I would like to link the zoom and pan controls of multiple charts together so they all pan and zoom together when one of the charts pan and zoom controls are engaged.</p>

<p>I tried creating a single zoom object and passing it to the charts, but only the last chart actually pan and zoomed.   The others remained static even though I was zooming in their areas.</p>

<p>Here's a snapshot of the charts.  Each chart has an overview chart with a viewport that can be moved as well.  I would like to link all the controls together so the viewports are also the same on each chart.</p>

<p>So, how can I link the pan and zoom controls on multiple charts?</p>

<p>Here is a jsfiddle for this code: <a href="https://jsfiddle.net/babazaroni/a52oukzn/" rel="nofollow noreferrer">https://jsfiddle.net/babazaroni/a52oukzn/</a></p>

<p><a href="https://i.stack.imgur.com/VzDHG.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/VzDHG.png" alt="enter image description here"></a></p>

<p>Here is my code:</p>

<p>The overall chart creator.  This code makes 3 charts.</p>

<p><div class="snippet" data-lang="js" data-hide="false" data-console="false" data-babel="false">
<div class="snippet-code">
<pre class="snippet-code-html lang-html prettyprint-override"><code>define([
    'd3',
    'components/sl',
    'MockData',
    'components/candlestickSeries',
    'Chart'
], function (d3, sl, MockData,candlestickSeries,Chart) {
    'use strict';




    function generateData()
    {

      var data = new MockData(0.1, 0.1, 100, 50, function (moment) {
        return !(moment.day() === 0 || moment.day() === 6);
      })
      .generateOHLC(new Date(2014, 1, 1), new Date(2014, 8, 1));

      return data;
    }


    var data = generateData();


   d3.select('#chart1')
       .datum(data)
     .call(Chart());

     data = generateData();



    d3.select('#chart1')
        .datum(data)
      .call(Chart());

         data = generateData();


          d3.select('#chart1')
        .datum(data)
      .call(Chart());




});</code></pre>
</div>
</div>
</p>

<p>Here is the chart code:</p>

<p><div class="snippet" data-lang="js" data-hide="false" data-console="false" data-babel="false">
<div class="snippet-code">
<pre class="snippet-code-html lang-html prettyprint-override"><code>define([
    'd3',
    'components/sl',
    'MockData',
    'components/candlestickSeries'
], function (d3, sl, MockData) {
    'use strict';

    function timeSeriesChart() {

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


                var minDate = new Date(d3.min(data, function (d) { return d.date; }).getTime() - 8.64e7);
                var maxDate = new Date(d3.max(data, function (d) { return d.date; }).getTime() + 8.64e7);
                var yMin = d3.min(data, function (d) { return d.low; });
                var yMax = d3.max(data, function (d) { return d.high; });



                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // The primary chart

                // Set up the drawing area
    
                var margin = {top: 20, right: 20, bottom: 30, left: 35},
                    width = 600 - margin.left - margin.right,
                    height = 200 - margin.top - margin.bottom;

                var plotChart = d3.select(this).classed('chart', true).append('svg')
                    .attr('width', width + margin.left + margin.right)
                    .attr('height', height + margin.top + margin.bottom)
                    .append('g')
                    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

                var plotArea = plotChart.append('g')
                    .attr('clip-path', 'url(#plotAreaClip)');

                plotArea.append('clipPath')
                    .attr('id', 'plotAreaClip')
                    .append('rect')
                    .attr({ width: width, height: height });

                // Scales

                var xScale = d3.time.scale(),
                    yScale = d3.scale.linear();



                // Set scale domains
                xScale.domain([minDate, maxDate]);
                yScale.domain([yMin, yMax]).nice();

                // Set scale ranges
                xScale.range([0, width]);
                yScale.range([height, 0]);

                // Axes

                var xAxis = d3.svg.axis()
                    .scale(xScale)
                    .orient('bottom')
                    .ticks(10);

                var yAxis = d3.svg.axis()
                    .scale(yScale)
                    .orient('left');

                plotChart.append('g')
                    .attr('class', 'x axis')
                    .attr('transform', 'translate(0,' + height + ')')
                    .call(xAxis);

                plotChart.append('g')
                    .attr('class', 'y axis')
                    .call(yAxis);

                plotChart.append("text")
                    .attr("x", (width / 2))             
                    .attr("y", 1 - (margin.top / 2))
                    .attr("text-anchor", "middle")  
                    .style("font-size", "16px") 
                    .style("text-decoration", "underline")  
                    .text("Your title goes here");

                // Data series

                var series = sl.series.candlestick()
                    .xScale(xScale)
                    .yScale(yScale);

                var dataSeries = plotArea.append('g')
                    .attr('class', 'series')
                    .datum(data)
                    .call(series);



                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // Navigation chart

                var navWidth = width,
                    navHeight = 100 - margin.top - margin.bottom;

                // Set up the drawing area

                var navChart = d3.select(this).classed('chart', true).append('svg')
                    .classed('navigator', true)
                    .attr('width', navWidth + margin.left + margin.right)
                    .attr('height', navHeight + margin.top + margin.bottom)
                    .append('g')
                    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

                // Scales

                var navXScale = d3.time.scale()
                        .domain([
                            new Date(minDate.getTime() - 8.64e7),
                            new Date(maxDate.getTime() + 8.64e7)
                        ])
                        .range([0, navWidth]),
                    navYScale = d3.scale.linear()
                        .domain([yMin, yMax])
                        .range([navHeight, 0]);

                // Axes

                var navXAxis = d3.svg.axis()
                    .scale(navXScale)
                    .orient('bottom');

                navChart.append('g')
                    .attr('class', 'x axis')
                    .attr('transform', 'translate(0,' + navHeight + ')')
                    .call(navXAxis);

                // Data series

                var navData = d3.svg.area()
                    .x(function (d) { return navXScale(d.date); })
                    .y0(navHeight)
                    .y1(function (d) { return navYScale(d.close); });

                var navLine = d3.svg.line()
                    .x(function (d) { return navXScale(d.date); })
                    .y(function (d) { return navYScale(d.close); });

                navChart.append('path')
                    .attr('class', 'data')
                    .attr('d', navData(data));

                navChart.append('path')
                    .attr('class', 'line')
                    .attr('d', navLine(data));



                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // Viewport

                function redrawChart() {

                    dataSeries.call(series);
                    plotChart.select('.x.axis').call(xAxis);
                }

                function updateZoomFromChart() {

                    var fullDomain = maxDate - minDate,
                        currentDomain = xScale.domain()[1] - xScale.domain()[0];

                    var minScale = currentDomain / fullDomain,
                        maxScale = minScale * 20;

                    zoom.x(xScale)
                        .scaleExtent([minScale, maxScale]);
                }


                var viewport = d3.svg.brush()
                    .x(navXScale)
                    .on("brush", function () {
                        xScale.domain(viewport.empty() ? navXScale.domain() : viewport.extent());
                        redrawChart();
                    })
                    .on("brushend", function () {
                        updateZoomFromChart();
                    });

                navChart.append("g")
                    .attr("class", "viewport")
                    .call(viewport)
                    .selectAll("rect")
                    .attr("height", navHeight);


                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // Zooming and panning

                function updateViewpointFromChart() {

                    if ((xScale.domain()[0] &lt;= minDate) &amp;&amp; (xScale.domain()[1] &gt;= maxDate)) {

                        viewport.clear();
                    }
                    else {

                        viewport.extent(xScale.domain());
                    }

                    navChart.select('.viewport').call(viewport);
                }



                var zoom = d3.behavior.zoom()
                    .x(xScale)
                    .on('zoom', function() {
                        if (xScale.domain()[0] &lt; minDate) {
                            zoom.translate([zoom.translate()[0] - xScale(minDate) + xScale.range()[0], 0]);
                        } else if (xScale.domain()[1] &gt; maxDate) {
                            zoom.translate([zoom.translate()[0] - xScale(maxDate) + xScale.range()[1], 0]);
                        }
                        redrawChart();
                        updateViewpointFromChart();
                    });

                var overlay = d3.svg.area()
                    .x(function (d) { return xScale(d.date); })
                    .y0(0)
                    .y1(height);

                plotArea.append('path')
                    .attr('class', 'overlay')
                    .attr('d', overlay(data))
                .call(zoom);

                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // Setup

                var daysShown = 30;

                xScale.domain([
                    data[data.length - daysShown - 1].date,
                    data[data.length - 1].date
                ]);

                redrawChart();

                updateViewpointFromChart();

                updateZoomFromChart();


                ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                // Helper methods

            });

        }
 //      alert("here we are again and again");
        return chart;
    }
    return timeSeriesChart;
});</code></pre>
</div>
</div>
</p>