Sunday, October 26, 2014

SVG fallback pure CSS: Can I use Part 5

Previous: External SVG: Can I use Part 4


SVG is old technology, but there are even older browsers in use that do not support SVG.
The idea of supporting such pure browsers is to provide PNG (JPG or GIF) image in background.

After making big research the best solution without using java-script is found with SVG trick:
  • switch tag, which takes first supported element
  • foreignObject tag, that gives ability to insert object from alien namespace
For inline SVG this works as:
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40">
  <switch>
     <circle cx="20" cy="20" r="18" stroke="grey" stroke-width="2" fill="#99FF66" />
     <foreignObject>
         <div class="nicolas_cage fallback"></div>
     </foreignObject>
  </switch>
</svg>
If browser supports SVG it will take <circle> as first child, otherwise <div class="nicolas_cage fallback"> is shown.

For external SVG we can use standard object fallback:
    <object type="image/svg+xml" data="circle_orange.svg">
     <div class="nicolas_cage fallback"></div>
    </object>

And inside <HEAD> just add a class with background image for fallback:
  <head>
    <style type="text/css">
     .nicolas_cage {
         background: url('nicolas_cage.jpg');
         width: 20px;
         height: 15px;
     }
     .fallback {
     }
    </style>
  </head>

For "No SVG" Nicolas Cage will show up

The <div> is chosen instead of <img> because image is mostly always downloaded by browsers, though {display: none} is set for <img>.

However test also showed that IE and Firefox still download background images for <div>, while Chrome and Safari works great with it. The twice download problem only occurs with
<foreignObject>
   <div class="nicolas_cage fallback"></div>
</foreignObject>
and is not a trouble for <object> fallback. At first I tried to set {display: none;} for <fallback> element in <head> style, but Android 2.x and Mobile Safari 4.0.4 parses it as object and hides hole object.

The solution is to set {display: none; background: none; } for <div>, then IE and Firefox stops download <div> background images. Finally the question appears:

How can we update <div> for SVG supporting browsers and hide it while leave it unchanged for those browsers that do not support SVG?

One solution is found, though it can not feet for all.
The trick is with ability of SVG to take a style from another SVG object.
 <svg id="SVG_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  
      width="20" height="20">
      <style>
        <![CDATA[
        .orange {
          fill: #FF9900;
        }
        ]]>
      </style>
 </svg>
 <svg id="SVG_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  
  width="20" height="20">
 <circle cx="10" cy="10" r="8" class="orange" />
</svg>

SVG_2 takes .orange style from SVG_1 even though they are different objects in one HTML file.

The solution is to paste special SVG inside <body> element with clear image style:
.fallback { background: none; background-image: none; display: none; }

And the result test page:
<!DOCTYPE html>
<html>
  <head>
    <title>HTML5 SVG demo</title>
    <style type="text/css">
     .nicolas_cage {
         background: url('nicolas_cage.jpg');
         width: 20px;
         height: 15px;
     }
     .fallback {
     }
    </style>
  </head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" style="width:0; height:0; display:0">
 <style>
 <![CDATA[ 
  .fallback { background: none; background-image: none; display: none; }
 ]]>
 </style>
</svg>

<!-- inline svg -->
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40">
  <switch>
     <circle cx="20" cy="20" r="18" stroke="grey" stroke-width="2" fill="#99FF66" />
     <foreignObject>
         <div class="nicolas_cage fallback"></div>
     </foreignObject>
  </switch>
</svg>
<hr/>
<!-- external svg -->
    <object type="image/svg+xml" data="circle_orange.svg">
     <div class="nicolas_cage fallback"></div>
    </object>
</body>
</html>

Starting tests in different browsers:

All renders great: small Cage for "No SVG" browsers and circles for those that support SVG.

What about fallback image downloading for SVG supporting browsers? 

Gladly double downloading does not occur with latest versions of browsers (IE 11, Chrome 38, Safari 8, Firefox 33).
As a plus this solution pushes strong hint to avoid downloading fallback images with
.fallback { background: none; background-image: none; display: none; },  
and it with a high degree will work for new SVG browsers that are hard to test (Kindle, Noble, etc).


UPD: eliminated error in CDATA closing tag ]]>, added style="width:0; height:0; display:0" to SVG fallback.

6 comments: