Just a student Just a student - 3 months ago 13
CSS Question

CSS reference to SVG filter in separate element

Playing around with SVG filters, I ran into an issue with referencing an SVG filter from CSS. In some situations, applying a filter will remove the element to which the filter is applied from the page. I thought that there could be 2 causes:


  1. the fact that I was adding the filters dynamically using D3JS;

  2. the fact that I am referencing the filters in CSS, defined in a separate file.



To test this, I created a MWE that demonstrates the issue. For me, this renders in Firefox and Chrome with nothing in the left box and a gray circle (the expected result) in the right box. This eliminates cause 1, I think. After I did this, I tried:


  1. Referencing using
    url(.#filter-id)
    , so with a leading dot. This did not change the result.

  2. Moving the
    defs
    in the same
    svg
    element as where the circle is. This fixes the issue.



My question: is it still possible to somehow reference a filter that is defined in a separate SVG element? I would very much prefer to do it that way in my application. I have read that there are problems with external files, but a different element in the same file should surely be possible?

Answer

I think the problem is the display:none in the hidden class, you can use another way to hidden, this for example:

svg.hidden {
  height:0;
  margin:0;
  border:none;
}

var svg = d3.select('#container').append('svg'),
    defs = svg.append('defs'),
    dsFilter;

svg.attr('width', 400).attr('height', 200);

dsFilter = defs.append('filter').attr('id', 'grayscale-d3')
dsFilter.append('feColorMatrix')
  .attr('type', 'matrix')
  .attr('values',
        '0.3333 0.3333 0.3333 0 0 ' +
        '0.3333 0.3333 0.3333 0 0 ' +
        '0.3333 0.3333 0.3333 0 0 ' +
        '0      0      0      1 0')

svg.append('text').attr('x', 2).attr('y', 2).text('appended by D3');
svg.append('circle').attr('class', 'd3').attr('cx', 200).attr('cy', 120).attr('r', 60);
body {
  margin: 0;
  font-size: 0;
}

circle {
  fill: #bada55;
  stroke: #000;
  stroke-width: 1px;
}
circle.of {
  filter: url('#grayscale-of');
}
circle.d3 {
  filter: url('#grayscale-d3');
}

svg {
  display: inline-block;
  margin: 1rem 0 0 1rem;
  border: 1px solid #000;
}
svg.hidden {
  height:0;
  margin:0;  
  border:none;
}

text {
  dominant-baseline: text-before-edge;
  font-size: 1rem;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<svg class="hidden">
  <defs>
    <filter id="grayscale-of">
      <feColorMatrix values="0.3333 0.3333 0.3333 0 0
                             0.3333 0.3333 0.3333 0 0
                             0.3333 0.3333 0.3333 0 0
                             0      0      0      1 0"
                     type="matrix">
      </feColorMatrix>
    </filter>
  </defs>
</svg>
<div id="container">
  <svg width="400" height="200">
    <text x="2" y="2">already in page</text>
    <circle class="of" cx="200" cy="120" r="60" />
  </svg>
</div>

Comments