An SVG Button Test Page

Or SVG Integration in HTML

This page is an investigation into how to use simple SVG graphics in HTML. It ignores scaling issues, which are tested elsewhere. The plan is to start off with a very simple button and then to add functionality to the button a step at a time. The goal is to keep things as simple as possible (avoiding JavaScript where possible).

Topics on this page were discussed at the Feb. 9th, 2011 and Feb. 16th, 2011 SVG Working Group Teleconferences. The page has been updated as a result of feedback from the group.

An SVG button used via <img>

The simplest thing to do is to use <img> to replace a PNG by an SVG.

	<a href="http://www.w3.org/Graphics/SVG/">
	  <img src="buttonA.png" alt="A sample PNG button."/>
	</a>
	<a href="http://www.w3.org/Graphics/SVG/">
	  <img src="buttonA.svg" alt="A sample SVG button."/>
	</a>
      
A sample PNG button. A sample SVG button.

Notes:

Cameron suggests either catching the error:

        <a href="http://www.w3.org/Graphics/SVG/" target="_blank">
	  <img src="buttonA.svg" alt="A sample SVG button."
	       onerror="this.removeAttribute('onerror');
			this.src='buttonA.png'"/>
	</a>
      
A sample SVG button.

this works in IE8!

Cameron also suggested using a JavaScript library like Modernizr. I have not tested this.

  <script src="modernizr.js"></script>
  <style>
    html.svg .png-fallback { display: none }
    html.no-svg .svg-image { display: none }
  </style>
  <a href="http://www.w3.org/Graphics/SVG/">
    <img class="svg-image" src="buttonA.svg">
    <img class="png-fallback" src="buttonA.png">
  </a>
      

An SVG button used via <object>

The next step is to try to use the SVG button via the <object> tag. This is in order that a PNG can be used as a fallback (for browsers that don't support SVG). The SVG serves only as a better (scalable) graphic than a PNG. The action (linking to a web page) is left to the HTML. Notice how this works when using the <object> tag with a PNG but not an SVG.

	<a href="http://www.w3.org/Graphics/SVG/">
	  <object type="image/png" data="buttonA.png">
	     Your browser does not support PNG! Impossible!
	  </object>
	</a>
	<a href="http://www.w3.org/Graphics/SVG/">
	  <object type="image/svg+xml" data="buttonA.svg">
	    <img src="buttonA.png" alt="A sample button."/>
	  </object>
	</a>
      
Your browser does not support PNG! Impossible! A sample button.

This does not work in most browsers as SVG does not pass the mouse event to the HTML file, even if pointer-events="none" is set in the SVG root element. Firefox (4beta) will actually show the href value in the HTML address bar but doesn't respond to mouse clicks. It appears that an SVG referenced via <object> creates a nested browsing context as SVG is an XML format while a PNG does not according to the WhatWG HTML spec. Ironically, the link works in IE8 since the PNG fallback is shown.

One can try to use a usemap but it still doesn't work with SVG (it does with a PNG, note how the map restricts the input to the left half of the PNG button). It seems that SVG despite the mime type image/svg+xml isn't interpreted as an image and thus the map is ignored.

        <object type="image/png" data="buttonA.png" usemap="#ButtonMap">
        </object>
        <object type="image/svg+xml" data="buttonA.svg" usemap="#ButtonMap">
	  <img src="buttonA.png" alt="A sample button."/>
        </object>
        <map name="ButtonMap">
	  <area shape="rect" coords="0,0,120,100" href="http://www.w3.org/Graphics/SVG/" alt="Button"/>
        </map>
      
A sample button. Button

It was suggested at the SVG WG meeting that one could use an event handler directly on the object. This works for PNGs but not for SVGs in most browsers.

	<object type="image/png" data="buttonA.png"
		onclick="alert('PNG Button Pressed')"
		onmouseover="this.style.cursor='crosshair'">
	  Your browser does not support PNG! Impossible!
	</object>
	<object type="image/svg+xml" data="buttonA.svg"
		onclick="alert('SVG Button Pressed')"
		onmouseover="this.style.cursor='crosshair'">
	  <img src="buttonA.png" alt="A sample button."/>
	</object>
      
Your browser does not support PNG! Impossible! A sample button.

An HTML file referenced via <object> or <iframe>

At the SVG WG meeting, it was asked how are HTML pages handled when included via <object> or <iframe>.

	<a href="http://www.w3.org/Graphics/SVG/" target="_blank">
          <object type="text/html" data="dummy.html" style="color: green">
	    Your browser does not support HTML. C'est dommage.
	  </object>
	</a>
	<a href="http://www.w3.org/Graphics/SVG/" target="_blank">
	  <iframe src="dummy.html" style="color: purple" seamless="seamless"/>
	</a>
      
Your browser does not support HTML. C'est dommage.

An SVG button with CSS styling

The next step would be to style the color of the button via CSS from HTML. SVG has the property color which seems to allow this but I can't find an example. One could then change the color via a script in the HTML or style multiple buttons differently. Setting fill="currentColor" does work with internal style sheets. You can also have the HTML and SVG refer to the same external style sheet... but this still doesn't help in referencing the same SVG file multiple times but applying different styles.

From SVG 1.1F2, section 11.2: "currentColor: Indicates that painting is done using the current animated value of the color specified by the ‘color’ property. This mechanism is provided to facilitate sharing of color attributes between parent grammars such as other (non-SVG) XML. This mechanism allows you to define a style in your HTML which sets the ‘color’ property and then pass that style to the SVG user agent so that your SVG text will draw in the same color."

As clarified at the Feb. 9th, 2010 SVG WG meeting, styling does not work across document boundaries. This is the expected behavior. The <iframe> tag has an attribute @seemless that allows styling to work across boundaries, see: HTML WG document. This does not appear to work either.

	<a href="http://www.w3.org/Graphics/SVG/">
	  <img src="buttonB.svg" alt="A sample SVG button." style="color: orange"/>
	</a>
	<a href="http://www.w3.org/Graphics/SVG/">
	  <object type="image/svg+xml" data="buttonB.svg" style="color: green">
	    Your browser does not support SVG. C'est dommage.
	  </object>
	</a>
        <iframe src="buttonB.svg" style="color: purple" width="240" height="100" seamless="seamless"/>
      
	Inside SVG:
	<rect
	   width="220"
	   height="80"
	   ry="40"
	   x="10"
	   y="10"
	   style="fill:currentColor;stroke:none" />
      
A sample SVG button. Your browser does not support SVG. C'est dommage.

Styling via currentColor does works with inlined SVG.

        svg.Orange {
          color: orange;
        }
        svg.Purple {
          color: purple;
        }
        ...
	<a href="http://www.w3.org/Graphics/SVG/">
          <svg class="Orange" ... >
 	    <use xlink:href="buttonB.svg#Root"/>
	  </svg>
	</a>
	<a href="http://www.w3.org/Graphics/SVG/">
          <svg class="Purple" ... >
	  <defs>
	    <text id="TheColor">Purple</text>
	  </defs>
	  <use xlink:href="buttonBtext.svg#Root"/>
        </svg>
      
Purple

See below for a way to style external SVG from HTML (using simple JavaScript).

An SVG button with internal events

Clicking on the SVG button generates an alert. This step gives up on having a PNG fallback as the SVG handles mouse events internally, something a PNG can't do. We also cannot use <img> as it cannot handle mouse events.

        <object type="image/svg+xml" data="buttonC.svg">
	  Your browser does not support SVG. C'est dommage.
	</object>
      
	Inside SVG:
	cursor="pointer"
	onclick="alert('SVG Button Pressed')"
      
Your browser does not support SVG. C'est dommage.

An SVG button with two states and with ARIA

Now we are forced to use JavaScript to keep track of the two states. The current state is kept in the value aria-pressed. One should note that using aria-pressed results in a non-valid SVG file according to the W3C validator.

Your browser does not support SVG. C'est dommage.

Reusing external SVG buttons

At first it appeared to be quite a pain to use the same SVG button multiple times on a web page as it appeared that the JavaScript needed to be in the HTML. This is a demo of my original method using JavaScript in the HTML. It uses inline SVG referencing SVG fragments from an external file.

Click on buttons to change state.

The button is OFF.

The button is OFF.

Referencing external SVG fails in Chrome 11, works in Opera 11 and Firefox 4beta.

A suggestion by Erik at the Feb 16th SVG WG meeting gave me the key ingredient that allows the JavaScript to be kept in the external SVG (and also to style the SVG from the HTML). The key is to use window.frameElement.id to grab the id of the object that is referencing the SVG file. The color from the objects style attribute can be grabbed via window.frameElement.style.color, and then JavaScript can be used to set the button's color in an init() function. And while we are at it, custom text can be grabbed from <param>s inside the <object>.

Style from <object>:

The button is OFF.

Style from <object> and custom text from <param>s:

The button is text OFF.

Style from CSS (in SVG file):

The button is OFF.

Styling from CSS works in Chrome 11, Opera 11 and Firefox 4beta.

Further issues to investigate

  1. How to give SVG buttons different text from HTML. At the SVG WG meeting, Chris suggested <tref> could work here. But this doesn't actually work when an SVG is reused since there is no way to change what the <tref>'s HREF (without using JavaScript). Doug's Parameters will take care of this in the future. SOLVED, see above.
  2. Using inlined SVG it is possible to reference external SVG and style the external SVG from the HTML. However, it means that the JavaScript that controls the state of a button cannot be kept in the external file. SOLVED, see above.
  3. How to use <use> with button scripts. (Firefox, Opera, Chrome return different values for event.target when a butten is cloned using <use>, SVGUseElement vs. SVGElementInstance.)