Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hidding SVG affects other SVG styles in the same page

An SVG is loaded several times in the same page. The SVG is used to show a graphic representation of values. Think of a map where every region shows a given value using a color code.

In each SVG, for every region a CSS class is dynamically applied to match the desired SVG pattern fill used.

CSS styles and patterns are defined in the SVG file. Here is an example:

  <svg height="100" width="100">
    <style>
    /*  */
    .striped-pain-1 {fill: url(#striped-pain-1);}
    /*  */
    </style>
    <defs>
        <pattern id="striped-pain-1" width="4" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
            <line x1="0" y1="0" x2="0" y2="2" style="stroke:#EABFD5; stroke-width:6"></line>
        </pattern>
    </defs>

The problem is when on of the SVGs is hidden (via display:none for example) all SVGs from there to the bottom of the page loose the pattern fill.

I have made a simplified Plunker showing the problem.

https://plnkr.co/edit/F5TzOwDEzneHEW7PT3Ls?p=preview

The only way I have found to prevent this is using different pattern ids for every SVG, but since all share the same file I don't like the solution to duplicate all of them and rename the ids just for this. I wonder there should be a better solution.

like image 940
David Casillas Avatar asked Mar 28 '18 09:03

David Casillas


2 Answers

I would put the patterns in a different svg element: <svg class="defs">. This svg element may have position:absolute and a very small width and height. If you add left: -200px; this svg element is invisible.

Also: if one SVG element has this css rule: .striped-pain-1 {fill: url(#striped-pain-1);} you don't need to add it to the second one. In fact you can delete the <style> element from the svg and add this rule to the css.

Please try it: click the numbers (1,2,3) to hide or unhide the svg elements.

let spans = Array.from(document.querySelectorAll("#commands span"))
let svgs = Array.from(document.querySelectorAll(".svgcontainer"))
spans.forEach((s,i) =>{
let n = 0;
s.addEventListener("click",(e)=>{
n++;
let thisSvg = svgs[i].querySelector("svg")
if(n%2 == 1){thisSvg.style.display="none";
            }else{
             thisSvg.style.display="block";}
})
})
svg {
  display:block;
}
.defs {
  position: absolute;
  left: -200px;
}
span {
  display: inline-block;
  width: 2em;
  height: 1em;
  border: 1px solid;
  text-align: center;
  cursor: pointer;
}
.svgcontainer {
  height: 100px;
  width: 100px;
  border: 1px solid;
  display: inline-block;
}
<p id="commands"><span>1</span> <span>2</span> <span>3</span></p>

<svg class="defs" width="1" height="1">
  <defs>
  		<pattern id="striped-pain-1" width="4" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
  			<line x1="0" y1="0" x2="0" y2="2" style="stroke:#EABFD5; stroke-width:6"></line>
  		</pattern>
    
      <pattern id="striped-pain-2" width="4" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
    		<line x1="0" y1="0" x2="0" y2="2" style="stroke:#EABFD5; stroke-width:6"></line>
    	</pattern>
  	</defs>
</svg>

<div class="svgcontainer">
  <svg height="100" width="100">
  	<style>
  		/*  */
  		.striped-pain-1 {fill: url(#striped-pain-1);}
  		/*  */
  	</style>
    
    <circle class="striped-pain-1" cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
  </svg> 

</div>
<div class="svgcontainer">
  <svg height="100" width="100">
   <!-- <style>
    		/*  */
    		.striped-pain-1 {fill: url(#striped-pain-1);}
    		/*  */
    </style>-->
    <!--<defs>
    	<pattern id="striped-pain-1" width="4" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
    		<line x1="0" y1="0" x2="0" y2="2" style="stroke:#EABFD5; stroke-width:6"></line>
    	</pattern>
    </defs>-->
    <circle class="striped-pain-1" cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
  </svg> 
</div>
<div class="svgcontainer">
  <svg height="100" width="100">
    <style>
    		/*  */
    		.striped-pain-2 {fill: url(#striped-pain-2);}
    		/*  */
    </style>
    <circle class="striped-pain-2" cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
  </svg> 
</div>
like image 151
enxaneta Avatar answered Oct 21 '22 12:10

enxaneta


This problem appears because you've specified the same id for two different elements:

<pattern id="striped-pain-1" on lines 52 and 69.

In HTML id should be unique.

The id attribute specifies a unique id for an HTML element (the value must be unique within the HTML document).

Replacing the duplicated id with the unique one solves the problem. See the snippet below:

<!DOCTYPE html>
<html>

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="[email protected]" src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.11/angular.min.js" data-semver="1.5.11"></script>
    <script src="app.js"></script>
    <script>
      function show(id) {
        document.getElementById('circle-' + id).style.display = 'block';
      }
      function hide(id) {
        document.getElementById('circle-' + id).style.display = 'none';
      }
      function reset() {
        document.getElementById('circle-1').style.display = 'block';
        document.getElementById('circle-2').style.display = 'block';
      }
    </script>
  </head>

<body ng-controller="MainCtrl">
    
<div style="padding:10px">
  <input type='button' onclick="hide(1)" value="Step 1: Click here to hide first circle. Second will loose style." /> 
</div>

<div style="padding:10px">
  <input type='button' onclick="show(1)" value="Step 2: Click here to show first circle. Second will gain style." /> 
</div>

<div style="padding:10px">
  <input type='button' onclick="hide(2)" value="Step 3: Click here to hide second circle. First is not affected." /> 
</div>

<div style="padding:10px">
  <input type='button' onclick="reset()" value="Step 4: Reset." /> 
</div>

<div id="circle-1" style="padding:10px;border:solid grey 1px">
  Circle 1
  <svg height="100" width="100">
  	<style>
  		/*  */
  		.striped-pain-1 {fill: url(#striped-pain-1);}
  		/*  */
  	</style>
    <defs>
  		<pattern id="striped-pain-1" width="4" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
  			<line x1="0" y1="0" x2="0" y2="2" style="stroke:#EABFD5; stroke-width:6"></line>
  		</pattern>
  	</defs>
    <circle class="striped-pain-1" cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
  </svg> 
</div>

<div id="circle-2" style="padding:10px;border:solid grey 1px">
  Circle 2
  <svg height="100" width="100">
    <style>
    		/*  */
    		.striped-pain-2 {fill: url(#striped-pain-2);}
    		/*  */
    </style>
    <defs>
    	<pattern id="striped-pain-2" width="4" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
    		<line x1="0" y1="0" x2="0" y2="2" style="stroke:#EABFD5; stroke-width:6"></line>
    	</pattern>
    </defs>
    <circle class="striped-pain-2" cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
  </svg> 
</div>

<div style="padding:10px;border:solid grey 1px">
  Circle 3
  <svg height="100" width="100">
    <style>
    		/*  */
    		.striped-pain-3 {fill: url(#striped-pain-3);}
    		/*  */
    </style>
    <defs>
    	<pattern id="striped-pain-3" width="4" height="1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
    		<line x1="0" y1="0" x2="0" y2="2" style="stroke:#EABFD5; stroke-width:6"></line>
    	</pattern>
    </defs>
    <circle class="striped-pain-3" cx="50" cy="50" r="40" stroke="black" stroke-width="3" fill="red" />
  </svg> 
</div>

<div style="padding:10px;">
  These circles are SVGs. They have background applied as a pattern. The first two use clases with the same name, but the third not.
  Hidding the first SVG affects the second one, but hidding the second does not affect the first.
</div>


  </body>

</html>

Probably you might want to get rid of SVGs and use simpler way:

function show(id) {
  document.getElementById('circle-' + id).style.display = 'block';
}

function hide(id) {
  document.getElementById('circle-' + id).style.display = 'none';
}

function reset() {
  document.getElementById('circle-1').style.display = 'block';
  document.getElementById('circle-2').style.display = 'block';
}
#circle-1,
#circle-2,
#circle-3 {
  padding: 10px;
  border: solid grey 1px
}

.circle {
  display: inline-block;
  width: 80px;
  height: 80px;
  margin: 10px;
  border: solid 3px #000;
  border-radius: 55%;
  background: linear-gradient(135deg, #fff 20%, pink 21%, pink 50%, #fff 51%, #fff 71%, pink 72%) 0 0 / 8px 8px;
}


}
<div style="padding:10px">
  <input type='button' onclick="hide(1)" value="Step 1: Click here to hide first circle. Second will loose style." />
</div>

<div style="padding:10px">
  <input type='button' onclick="show(1)" value="Step 2: Click here to show first circle. Second will gain style." />
</div>

<div style="padding:10px">
  <input type='button' onclick="hide(2)" value="Step 3: Click here to hide second circle. First is not affected." />
</div>

<div style="padding:10px">
  <input type='button' onclick="reset()" value="Step 4: Reset." />
</div>

<div id="circle-1">Circle 1 <span class="circle"></span></div>
<div id="circle-2">Circle 2 <span class="circle"></span></div>
<div id="circle-3">Circle 3 <span class="circle"></span></div>
like image 24
Kosh Avatar answered Oct 21 '22 12:10

Kosh