title: Implement an SVG mask on a RECT with D3 (in Javascript)?
body: <p>I'm trying to implement an SVG mask in D3, similar to this very simple <a href="http://jsfiddle.net/bologna/o5j93f7e/" rel="nofollow">jsfiddle example</a>, but I must have lost something in translation. My implementation all takes place in a class that renders a graph. I'm trying to apply the mask to define bounds for the graph, so that when the data exceeds those bounds, the graph is neatly clipped. When I apply the mask, the bars of the graph completely disappear. As far as I can tell the mask in the right place. HELP!</p>
  
  <p>Here is where I define the mask in my <code>init()</code> function:</p>
  
  <pre><code>    // Add an SVG element with the desired dimensions and margin.
      this.graph = d3.select(this.config.id).append("svg:svg")
                          .attr("width", this.width + this.m[1] + this.m[3])
                          .attr("height", this.height + this.m[0] + this.m[2])                            
                      .append("svg:g")
                          .attr("transform", "translate(" + this.m[3] + "," + this.m[0] + ")");
  
      var maskWidth  = 640;
      var maskHeight = 321;
  
      this.graph.append('svg:defs')      &lt;------  I START DEFINING IT HERE !
          .call(function (defs) {
            // Appending the mask
            defs.append('svg:mask')
              .attr('id', 'mask')
              .attr('width', maskWidth)
              .attr('height', maskHeight)
              .attr('x', 0)
              .attr('y', 0)
              .call(function(mask) {
                mask.append('svg:rect')
                  .attr('width', maskWidth)
                  .attr('height', maskHeight)
                  .attr('fill', '#ffffff')
              });
          });
  </code></pre>
  
  <p>Here is the Method that draws bars on the graph where I attempt to apply the mask (see the last line):</p>
  
  <pre><code>addBars: function (data){
                  var numberOfBars = Math.floor(this.xMaximum);
                  var barWidth = this.width/numberOfBars;
  
                  // Generate a histogram using twenty uniformly-spaced bins.
                  var histogramData = d3.layout.histogram()
                      .bins(this.xScale.ticks(numberOfBars))
                      (data);    
  
                  //console.trace('typeof: '+typeof this.xScale);
                  var xScale = this.xScale;
                  var yScale = this.yScale;
                  var height = this.height;
  
                  this.bars = this.graph.selectAll("bar")
                          .data(histogramData, function(d){ return d;})
                      .enter()
                      .append("rect")
                          .attr("class","bar")
                          .attr("fill","steelblue")
                          .attr("transform", function(d, i) { 
                              var yOffset = height;
                              return "translate(" + (i * barWidth - barWidth/2) + ","+yOffset+")";
                              })
                          .attr("y", function(d,i) { 
                              var yPosition = yScale(d.length)- height;
                              return (yScale(d.length)-height); 
                              })
                          .attr("height", function(d) { 
                              return height - yScale(d.length);
                              })
                          .attr("width", barWidth - 1)
                          .attr('mask', 'url(#mask)');   &lt;---- OVER HERE  !!!!
      },
  </code></pre>
  
  <p>Here is a link to the resulting HTML in Chrome Developer Tools (I've highlighted the <code>&lt;defs&gt;</code> and one of the graph bars that should be masked):<a href="http://www.jonahelgart.com/stackoverflow/HTMLoutput.jpg" rel="nofollow">Chrome Developer Tools Dynamic HTML</a></p>
  
  <p>As far as I can tell everything looks good. This leads me to believe that the mask is mis-aligned with the bar, causing the bar to be invisible. However, in the developer tools, when I hover over the <code>&lt;rect&gt;</code> element, it shows it as overlaying the graph bars, so it doesn't seem like an alignment issue. Any help would be appreciated.</p>
  
  <p>Lastly, I've made a jsfiddle of the class being used in my application (see the comments for the link.). Below is also the entire class for drawing the graph, just in case it would be helpful to see the code in context:</p>
  
  <pre><code>// HistogramGrapher class - constructor
  var HistogramGrapher = function() {
  
      // assign default properties
      this.config = {
          id: "",
          xAxisLabel: "xAxis",
          yAxisLabel: "yAxis",
          width: 1000,
          height: 400,
          title: "Title",
          mean: 20
      };
  
      // define variables
      this.m = [40, 80, 40, 80]; // margins
      this.width; // width
      this.height; // height
      this.xAxisLabel;
      this.yAxisLabel;
      this.graph;
      this.bars; 
      this.lines;
      this.xScale;
      this.xScaleInvert;
      this.xAxis;
      this.yScale;
      this.yScaleInvert;
      this.yAxis;
      this.yMaximum = 25;
      this.xMaximum = 2 * this.config.mean;
  }
  
  
  // methods for this class
  HistogramGrapher.prototype = {
  
      init: function (options) {
          // copy properties of `options` to `config`. Will overwrite existing ones.
          for(var prop in options) {
              if(options.hasOwnProperty(prop)){
                  this.config[prop] = options[prop];
              }
          }
  
          // update variables
          this.updateWidth(this.config.width);
          this.updateHeight(this.config.height);
          this.updateXMaximum(this.config.mean);
  
          // X scale will fit all values from datay[] within pixels 0-w
          this.xScale = d3.scale.linear()
                          .domain([0, this.xMaximum])
                          .range([0, this.width]);
  
          this.xScaleInvert = d3.scale.linear()
                          .range([0, this.xMaximum])
                          .domain([0, this.width]);
  
          // Y scale 
          this.yScale = d3.scale.linear()
                          .domain([0, this.yMaximum])
                          .range([this.height,0]);
  
          this.yScaleInvert = d3.scale.linear()
                          .range([0, this.yMaximum])
                          .domain([this.height,0]);
  
  
          // Add an SVG element with the desired dimensions and margin.
          this.graph = d3.select(this.config.id).append("svg:svg")
                              .attr("width", this.width + this.m[1] + this.m[3])
                              .attr("height", this.height + this.m[0] + this.m[2])                            
                          .append("svg:g")
                              .attr("transform", "translate(" + this.m[3] + "," + this.m[0] + ")");
  
          var maskWidth  = 640;
          var maskHeight = 321;
  
          this.graph.append('svg:defs')
              .call(function (defs) {
                // Appending the mask
                defs.append('svg:mask')
                  .attr('id', 'mask')
                  .attr('width', maskWidth)
                  .attr('height', maskHeight)
                  .attr('x', 0)
                  .attr('y', 0)
                  .call(function(mask) {
                    mask.append('svg:rect')
                      .attr('width', maskWidth)
                      .attr('height', maskHeight)
                      .attr('fill', '#ffffff')
                  });
              });
  
  
  
          // create xAxis
          this.xAxis = d3.svg.axis().scale(this.xScale)
              .tickSize(-this.height)
              .tickSubdivide(true);
  
          // create yAxis
          this.yAxis = d3.svg.axis().scale(this.yScale)
              .tickSize(-this.width)
              .tickSubdivide(true)
              .orient("left");
  
          // Add the x-axis label.
          this.graph.append("text")
              .attr("class", "x label")
              .attr("text-anchor", "end")
              .attr("x", this.width)
              .attr("y", this.height + 25)
              .text(this.config.xAxisLabel);
  
          // Add the y-axis label.
          this.graph.append("text")
              .attr("class", "y label")
              .attr("text-anchor", "end")
              .attr("y", -30)
              .attr("dy", ".75em")
              .attr("transform", "rotate(-90)")
              .text(this.config.yAxisLabel);
  
          // add Title
          this.graph.append("text")
              .attr("x", this.width/2 )             
              .attr("y", -20  )               
              .attr("text-anchor", "middle")  
              .style("font-size", "12px")                 
              .text(this.config.title);
  
          // Add the x-axis.
          this.graph.append("svg:g")
                .attr("class", "x axis")
                .attr("transform", "translate(0," + this.height + ")")
                .call(this.xAxis);
  
          // Add the y-axis.
          this.graph.append("svg:g")
                .attr("class", "y axis")
                .call(this.yAxis);        
      },      
  
      updateWidth: function(width){
              this.width = width - this.m[1] - this.m[3];
      },
  
      updateHeight: function(height){
              this.height = height - this.m[0] - this.m[2]; // height 
      },  
  
      updateXMaximum: function(mean){
          this.xMaximum = 2.5 * mean;
      },
  
      addBars: function (data){
                  var numberOfBars = Math.floor(this.xMaximum);
                  var barWidth = this.width/numberOfBars;
  
                  // Generate a histogram using twenty uniformly-spaced bins.
                  var histogramData = d3.layout.histogram()
                      .bins(this.xScale.ticks(numberOfBars))
                      (data);    
  
                  //console.trace('typeof: '+typeof this.xScale);
                  var xScale = this.xScale;
                  var yScale = this.yScale;
                  var height = this.height;
  
                  this.bars = this.graph.selectAll("bar")
                          .data(histogramData, function(d){ return d;})
                      .enter()
                      .append("rect")
                          .attr("class","bar")
                          .attr("fill","steelblue")
                          .attr("transform", function(d, i) { 
                              var yOffset = height;
                              return "translate(" + (i * barWidth - barWidth/2) + ","+yOffset+")";
                              })
                          .attr("y", function(d,i) { 
                              var yPosition = yScale(d.length)- height;
                              return (yScale(d.length)-height); 
                              })
                          .attr("height", function(d) { 
                              return height - yScale(d.length);
                              })
                          .attr("width", barWidth - 1)
                          .attr('mask', 'url(#mask)');
      },
  
      addLine: function (data){  // the data must be in the form " [ {'x':x1, 'y':y1} , {'x':x2, 'y':y2} , {'x':x3, 'y':y3} ... ]
          var xScale = this.xScale;
          var yScale = this.yScale;
          var height = this.height;
  
          // create a line function that can convert data[] into x and y points
          var lineFunction = d3.svg.line()
              // assign the X function to plot our line as we wish
              .x(function(d) { return xScale(d.x); })
              .y(function(d) { return yScale(d.y); })
              .interpolate("linear");
  
          this.lines = this.graph.append("path")
                          .attr("d", lineFunction(data))
                          .attr("class", "line")
                          .attr("stroke", "green")
                          .attr("stroke-width", 2)
                          .attr("fill","none");
  
      },
  
      clear: function () {
          var bars = d3.selectAll(".bar").remove();
          var lines = d3.selectAll(".line").remove();
      },
  
      getxScale: function () {
          return this.xScale;
      },
  
      getxScaleInvert: function () {
          return this.xScaleInvert;
      }
  }
  </code></pre>