Build fantastic visual SVG Widgets.

Svidget.js is a JavaScript framework for creating immersive and fully interactive SVG widgets. By componentizing your data visualizations as widgets, you can quickly and easily embed them on any website while keeping a clean separation between SVG and HTML.

Current Version: 0.3.4
Released: 2015-Jul-4

Getting Started

Features

Widgetize
Most data visualization JavaScript libraries work by injecting SVG content directly into the page DOM. With Svidget you create your data visualization widgets in separate .svg files.
Consume with Ease
Simply reference your widget from your page using an <object> tag. In addition, you use <param> tags to pass data to the widget.
Plays Nice
Want to use jQuery, D3, or some other JavaScript library in your widgets? No problem. Svidget provides you the widget framework then gets out of your way.

Interact
You manipulate your widget from a page through its params, actions, and events. That is, you can set param values, invoke actions, and handle events from a widget.
Standalone
You can use Svidget to build data visualizations as standalone SVG pages. Svidget even enables you to pass params as query strings to the widget.
Cross-Domain
You can build widgets on one website and consume them from any another domain.

Getting Started

1

Create a Widget

Let's say I want to create a donut gauge to display progress on an action. Something that looks like this:
A plain donut gauge.
And here is its raw, static SVG code:
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svidget="http://www.svidget.org/svidget"
		 width="200" height="200" viewBox="0 0 200 200" style="background:transparent">
	<title>Donut Gauge</title>
	<desc>Visualizes a donut-style gauge with text value in the middle.</desc>

	<style>
		#textLabel { font-size: 16px; font-family: Helvetica; alignment-baseline: text-top; }
		#spanContent { font-size: 28px; font-weight: bold; margin: 0px; }
	</style>

	<defs>
		<mask id="donutMask">
			<rect x="0" y="0" width="100%" height="100%" fill="white" />
			<circle r="48" cx="100" cy="100" fill="black" />
		</mask>
	</defs>

	<g>
		<circle id="backCircle" r="98" cx="100" cy="100" fill="#ddd" mask="url(#donutMask)" />
		<path id="foreArc" fill="none" stroke="#7f7fdf" stroke-width="50" d="M 100,27 A 73,73 0 1,1 27,100" />
		<text id="textLabel" x="100" y="110" text-anchor="middle" fill="#3f3f3f">
			<tspan id="spanContent">75</tspan> %
		</text>
	</g>

</svg>
[View Code]
By itself, it looks nice and all, but not really useful. We can make this useful by turning it into an SVG widget. To widgetize this, the first thing we need to think about is what are its properties. In svidget, we refer to these as params. For our donut gauge our params will be:
  • data - this is the primary data. Let this be a value between 0 and 1 representing a percentage.
  • color - The color of the foreground on the circle.
  • backColor - The color of the background on the circle.
  • textColor - The color of the text in the middle.
  • showText - A bool flag whether to show the text at all.
  • width - The width of the donut. Can be a value between 1 and 98.

Svidget uses a declarative syntax to add parameters to a widget. Since SVG is structured XML we can extend it with the svidget namespace.
<svidget:params>
	<svidget:param name="data" shortname="d" type="number" description="The percentage to fill. Value should be between 0 and 1." />
	<svidget:param name="color" shortname="color" type="string" subtype="color" binding="#foreArc@stroke" description="The circle foreground color." />
	<svidget:param name="backColor" shortname="bcolor" type="string" subtype="color" binding="#backCircle@fill" description="The circle background color." />
	<svidget:param name="textColor" shortname="tcolor" type="string" subtype="color" binding="#textLabel@fill" description="The text color." />
	<svidget:param name="showText" shortname="st" type="bool" description="Whether to show the text in the middle." />
	<svidget:param name="width" shortname="w" type="number" description="The width of the donut portion of the circle." />
</svidget:params>

			
[View Code] One key feature is the binding attribute. Here we can use CSS selector syntax (+ attributes) to map a parameter directly to an element or attribute in the SVG body. For more complex operations we can hook into events for the parameters when they are set.

When we put it all together, our final widget code looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svidget="http://www.svidget.org/svidget"
		 width="200" height="200" viewBox="0 0 200 200" style="background:transparent">
	<title>Donut Gauge</title>
	<desc>Visualizes a donut-style gauge with text value in the middle.</desc>

	<style>
		<![CDATA[
		#textLabel { font-size: 16px; font-family: Helvetica; alignment-baseline: text-top; }
		#spanContent { font-size: 28px; font-weight: bold; margin: 0px; }
		]]>
	</style>
	
	<svidget:params>
		<svidget:param name="data" shortname="d" type="number" description="The percentage to fill. Value should be between 0 and 1." />
		<svidget:param name="color" shortname="color" type="string" subtype="color" binding="#foreArc@stroke" description="The circle foreground color." />
		<svidget:param name="backColor" shortname="bcolor" type="string" subtype="color" binding="#backCircle@fill" description="The circle background color." />
		<svidget:param name="textColor" shortname="tcolor" type="string" subtype="color" binding="#textLabel@fill" description="The text color." />
		<svidget:param name="showText" shortname="st" type="bool" description="Whether to show the text in the middle." />
		<svidget:param name="width" shortname="w" type="number" description="The width of the donut portion of the circle." />
	</svidget:params>

	<defs>
		<mask id="donutMask">
			<rect x="0" y="0" width="100%" height="100%" fill="white" />
			<circle r="48" cx="100" cy="100" fill="black" />
		</mask>
	</defs>

	<g>
		<circle id="backCircle" r="98" cx="100" cy="100" fill="#ddd" mask="url(#donutMask)" />
		<path id="foreArc" fill="none" stroke="#7f7fdf" stroke-width="50" d="M 100,27 A 73,73 0 1,1 27,100" />
		<text id="textLabel" x="100" y="110" text-anchor="middle" fill="#3f3f3f" visibility="visible">
			<tspan id="spanContent">75</tspan> %
		</text>
	</g>

	<script type="application/javascript" xlink:href="../scripts/svidget.js"></script>
	<script type="application/javascript">
		<![CDATA[
	
		var _data = 0;
		var _width = 50;
		var FULL_RADIUS = 100;
		var DONUT_RADIUS = FULL_RADIUS - 2; // 98
		
		function init() {
			//setArcPath(0.75);
			//debugger;
			var widget = svidget.current();
			widget.param("data").on("set", onParamDataSet);
			widget.param("width").on("set", onParamWidthSet);
			widget.param("showText").on("set", onParamShowTextSet);
			setParamData(widget.param("data").value());
			setParamWidth(widget.param("width").value());
			setParamWidth(widget.param("showText").value());
		}
		
		function onParamDataSet(e) {
			//alert('onParamDataSet');
			var val = parseFloat(e.value.value); // { value: val }
			setParamData(val);
		}
				
		function onParamWidthSet(e) {
			var w = parseInt(e.value.value); // { value: val }
			setParamWidth(w);
		}
		
		function onParamShowTextSet(e) {
			var show = e.value.value;
			setParamShowText(show);
		}
		
		// range: 0 to 1
		function setParamData(val) {
			val = rangify(val, 0, 1);
			// set donut arc
			setArcPath(val);
			// set text
			setContentLabel(val);
			// set global data
			_data = val;
		}

		// range: 1 to 98
		function setParamWidth(w) {
			w = rangify(w, 1, DONUT_RADIUS);
			// set donut mask - i.e. empty hole in middle aka the donut hole
			var rad = DONUT_RADIUS - w;
			setMaskRadius(rad);
			setArcWidth(w);
			// set global width
			_width = w;
			// re-set arc path
			setArcPath(_data);
		}
		
		function setParamShowText(show) {
			//debugger;
			var textLabel = document.getElementById("textLabel");
			var v = !show ? "hidden" : "visible";
			textLabel.setAttribute("visibility", v);
		}

		function setMaskRadius(rad) {
			var maskCir = document.querySelector("#donutMask > circle");
			maskCir.setAttribute("r", rad);
		}

		function setContentLabel(pct) {
			var val = parseInt(pct * 100);
			var spanContent = document.getElementById("spanContent");
			spanContent.textContent = val + "";
		}

		function setArcPath(pct) {
			var path = generateArcPath(pct);
			var foreArc = document.getElementById("foreArc");
			foreArc.setAttribute("d", path);
		}
		
		function setArcWidth(w) {
			var foreArc = document.getElementById("foreArc");
			foreArc.setAttribute("stroke-width", w);
		}
	
		// pct == 0 to 1
		function generateArcPath(pct) {
			var baseY = 2;
			var halfWidth = _width / 2.0;
			var startY = baseY + halfWidth;
			var arcRadius = FULL_RADIUS - startY;
			var largeArc = pct > 0.5 ? 1 : 0; // if greater than 50 we need to use large arc in path
			var pctRadians = (Math.PI * 2) * pct;
			var endX = round3((Math.sin(pctRadians) * arcRadius) + FULL_RADIUS);
			var endY = round3((-Math.cos(pctRadians) * arcRadius) + FULL_RADIUS);
			if (endX == 100 && pct > 0) endX = 99.999; // this corrects issue with path if start and end are same it won't draw an arc
			//debugger;
			
			// M 100,27 A 73,73 0 1,1 27,100
			var path = "M 100," + startY + " "; // move to start
			path += "A " + arcRadius + "," + arcRadius + " 0 " + largeArc + ",1 " + endX + "," + endY;
			
			return path;
		}
		
		function rangify(val, min, max) {
			if (isNaN(val) || val < min) val = min;
			if (val > max) val = max;
			return val;
		}
		
		function round3(val) {
			return parseInt(val * 1000) / 1000.0;
		}
	
		window.addEventListener('load', init, false);
	
	  ]]>
	</script>
</svg>
[View Code]

2

Embed on a Page

Now the fun part, using our donut gauge widget on a web page. Play around with the controls to interact with the widget.
data (0-1)
color
backColor
textColor
showText
width (1-98)

The primary way to embed a widget on a web page is using an <object> tag. The framework looks for all <object> tags with role="svidget" and instantiates the widget.
<object id="myDonutGauge" role="svidget" data="widgets/donut.svg" type="image/svg+xml" width="200" height="200">
	<param name="data" value="0.55" />
	<param name="color" value="#da3333" />
	<param name="backColor" value="#ffac33" />
	<param name="textColor" value="#da3333" />
	<param name="showText" value="true" />
	<param name="width" value="40" />
</object>

				
[View Code] You can also load a widget programmatically using svidget.load():
svidget.load("#widgetContainer", "widgets/donut.svg", { 
	data: 0.55, 
	color: "#da3333",
	backColor: "#ffac33",
	textColor: "#da3333",
	showText: true,
	width: 40,
});

				
[View Code]
Fork me on GitHub