Developer Guide

This page discusses building your first widget, adding and interacting with it on a web page. It also gives hints, tips, and best practices to making your widgets awesome.

Introduction

Svidget.js was conceived as a solution to building complex, infinitely customizable visualizations as widgets, and allow users with just some basic HTML knowledge to quickly consume and display these widgets on their web pages with ease.

Currently with data visualization on the web, you have two options. Go with a high level charting library, which in many cases have their own limitations, making customization difficult or impossible. The other option is go with a low level visualization library like D3, which comes with a steep learning curve and a time cost to implement.

Svidget bridges the gap between those two options by allowing you to express your visualizations as simplistic or as specialized as your application requires. The beauty of Svidget is you can use these libraries in your widgets. You want a pretty pink bar chart with flowers instead of bars? Not a problem. You can combine Svidget with D3 or any charting library, or both, and produce stunning visualizations, then package them as nice friendly widgets!
Architecture

The stated goal for Svidget.js is to cleanly separate visualization SVG from its host HTML page. This means that the page has a DOM, and a Svidget widget has its own DOM. Contrast this with the fact that most JavaScript visualization libraries manipulate the DOM directly. There are advantages and disadvantages in both approaches.

The primary advantage of a single DOM is perceived performance and ease of manipulation. However, due to JavaScript's single threaded nature any performance gains are minimal. Coming soon, there will be some sample pages to demonstrate Svidget's performance capabilities.

Design Goals

The design goals of the Svidget framework are:

Clean Separation. You can build and test your widgets as standalone SVG files before integrating them with a page.
Ease of Use. Consuming a widget on a web page only requires minimal HTML and JavaScript knowledge.
Encapsulation. Neatly encapsulting data visualization components making reuse clean and easy to use.
Isolation. JavaScript errors within the widget do not interfere with the page in most cases, and vice versa.
Customization. Working within an SVG file allows you make as specialized a visualization as you need.
Library Design
For faster loading and caching, Svidget is maintained as one unified library for both pages and widgets.
Some Uses
  • A blog or content-oriented site that wants to quickly integrate a chart without the hassle of picking and coding against a charting library.
  • A live infographics page that contains multiple charts that is constantly refreshing its data.
  • An interactive game, puzzle, or activity that reuses a lot of similar graphical components (like ABC blocks for example).

 

Building a Widget

This section describes building a widget.

A Simple Hello World Widget

Here is a simple widget with a single text element. This illustrates the basic layout of a widget.

<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="100" viewBox="0 0 200 100" style="background:transparent">
	<title>Hello World Example</title>
	<desc>The most basic example showing the features of params, actions, and widgets.</desc>

	<svidget:params>
		<svidget:param name="data" shortname="d" type="string" subtype="color" binding="#textLabel" />
		<svidget:param name="color" shortname="c" type="string" subtype="color" binding="#textLabel@fill" />
		<svidget:param name="size" shortname="s" type="number" binding="#textLabel@font-size" />
	</svidget:params>

	<svidget:actions>
		<svidget:action name="spin" external="true" binding="spinIt" description="Spins the star." />
	</svidget:actions>

	<svidget:events>
		<svidget:event name="spinComplete" description="Triggered when the spinning has completed." />
	</svidget:events>

	<g>
		<text id="textLabel" x="100" y="55" text-anchor="middle" font-size="24" fill="#3f3f3f" font-family="Arial" class="pulse"></text>
	</g>

	<script type="application/javascript" xlink:href="../scripts/svidget.js"></script>
	<script type="application/javascript">
		<![CDATA[
	
		// bound to spin action
		function spinIt() {
			// perform the spin
			// (some code that does that here)
			// invoke spinComplete event
			svidget.$.event("spinComplete").trigger();
		}
	
	  ]]>
	</script>

</svg>
Widget Properties

Communication with a widget is comprised of these primary three properties.

Params - Params are the data endpoints for a widget. They can be read from and set at any point during the widget's lifecycle.
Actions - Actions are the functional endpoints (i.e. methods). They are abstractions to underlying functionality in the widget.
Events - Events represent notifications from the widget.
Declarative Syntax
Within a widget, you can use xml declarative syntax to define the params, actions, and events (as seen above). Having this simple yet powerful expressive syntax simplifies development of a widget. For more information see the API documentation for xml elements. The svidget.xsd schema is provided and can be viewed in the GitHub repo so you can validate your widget in any xml code editor.
Types and Subtypes

The Svidget framework defines a series of types and subtypes that mostly correspond to JavaScript native types. These types and subtypes are referenced in params and actionparams. When using a widget, it is important (although not critical) to adhere to the types.

Type Description Subtypes
string Represents a JavaScript string. The default type in most cases. regex, color, choice
number Represents a JavaScript numeric value. integer
bool Represents a JavaScript boolean. Can also be expressed as 0 or 1.
object Represents a JavaScript object. Can be expressed as JSON.
array Represents a JavaScript Array type. Can be expressed as a JSON array.

The object and array types can be represented as JSON when declaratively setting a param using a <param> tag inside of a <object> tag. Svidget has a bit more relaxed approach to parsing JSON syntax than you would see when using JSON.parse(). Specifically, Svidget allows the use of single quotes in JSON. This is so you can specify JSON in the value attribute without the need to escape double quotes.

<object id="myStar" role="svidget" data="widgets/donut.svg" type="image/svg+xml" width="200" height="200">
	<param name="data" value="[['banana', 7], ['apple', 6], ['cherry', 2], ['orange', 10], ['grape', 3.5]]" />
</object>
CSS Selectors + Attributes Syntax

Svidget uses CSS selector syntax to bind a param to one or more elements or attributes within the SVG document. However, the W3C spec for CSS selectors does not currently provide support for selecting attributes directly. Because of this, Svidget as added an additional capability on top of the standardized selector syntax, the use of @attr to select the attribute.

Consider this example:

<svidget:param name="backgroundColor" type="string" subtype="color" binding="#rect1@fill" />
<rect id="rect1" width="100" height="100" fill="yellow" />

The backgroundColor param is bound to the fill attribute of the <rect id="rect1"> element. We normally couldn't select the fill attribute directly, but using Svidget's +Attributes syntax, we now can. Note that this syntax may change in the future if the W3C spec is updated to allow direct attribute selection.

Working with Other Libraries

Svidget plays nice with almost any charting or other visualization library that allows itself to be embedded in an SVG file. These libraries have been tested and proven to play nice with Svidget.js:

  • jQuery 2.x and above
  • D3.js
  • svg.js
  • snap.svg
Making Your Widget Mobile Friendly
Here are some simple yet powerful tips to make your widget mobile friendly.

1. Handle click and tap/touchstart/touchend separately. Since a user can't typically hover over an element on a touch-only device, using tap/touch to simulate this behavior is the best approach.

2. Use Chrome's built in emulator to emulate different mobile devices. The Chrome developer tool allows you to test your widget in different resolutions and orientations, and includes emulating tap/touch capabilities, so you can for example handle click and tap events separately.
Google Chrome developer tool, emulating a mobile device.

Best Practices
1. Always support continous updating of params. Params should be able to be updated at any time during the life of the widget. Be sure to listen and handle to the param set event if the param requires custom functionality beyond using a binding.
2. Test performance. Always test performance by navigating to your widget svg file directly in the browser, then again when embedding the widget on a page.
Tips
You can use the SVG viewBox attribute to make your widget scale the size of the <object> container. If you don't use viewBox, the <object> can still be resized but only the viewport into the widget is resized, causing scrollbars to appear as necessary.

 

Using a Widget

This section discuss embedding a widget on a page.

Loading on a Page

The best way to add a widget to a page is to use the <object> tag.

<object id="hwWidget" role="svidget" data="widgets/helloworld.svg" type="image/svg+xml" width="200" height="100">
	<param name="data" value="Hello World" />
	<param name="color" value="#da3333" />
	<param name="size" value="20" />
</object>

You can also add a widget programatically using svidget.load().

svidget.load("#widgetContainer", { url: "widgets/helloworld.svg", id: "hwWidget" }, { 
	data: "Hello World", 
	color: "#da3333",
	size: 20
});
Interacting with the Widget

With our widget up and running, the next things to think about are how to interact with the widget. A widget is a living component, which means that its params can be set at any time.

Here is an example of setting the data param on our Hello World widget:

var hwWidget = svidget.widget("hwWidget");
var dataVal = hwWidget.param("data").value(); // gets the value
hwWidget.param("data").value("Goodbye World"); // sets the value

The next thing you can do is invoke actions on the widget. Our Hello World widget defines a spin action that doesn't take any params.

var hwWidget = svidget.widget("hwWidget");
hwWidget.action("spin").invoke();

The final thing you can do is listen for events on the widget. Our Hello World widget defines a spinComplete event.

var hwWidget = svidget.widget("hwWidget");
hwWidget.event("spinComplete").ontrigger(function (e) {
	document.getElementById("someDivTag").innerHTML = "Spin has completed.";
});

Note: For a live example of params, actions, and events in action, see the Spinning Star example.

Lifecycle

Because of the asynchronous nature of loading web pages and assets, sometimes the widget completes its loading before the page, and vice versa. Svidget handles this by attempting to communicate with the widget as soon as its loaded. When the widget finally responds, the bidirectional relationship between the page and widget is established, and the params are passed to the widget. If the widget is instantiated in standalone mode, then the relationship is terminated after the params are passed. This is discussed in the next section.

Connected vs Standalone

By default, widgets are loaded in connected mode. This means that the communication between page and widget is always on, and the page can send action commands and receive events, and the widget can trigger events. A widget can be declared as standalone using the data-standalone attribute. When true, once the params are passed to the widget from the page, communication is terminated. This is useful for widgets that have no actions or events, or for scenarios when the page doesn't have any need to invoke actions or handle events from the widget.

Cross Domain

Widgets can be loaded cross domain. However, Svidget has to take a couple extra steps to make that happen. A widget declared as an <object> element has to be converted into an <iframe>. This is so it can access the widget's window in order to communication with the widget and vice versa via cross domain messaging. If you know that you are loading a widget in another domain, you can speed up this process by setting the data-crossdomain attribute to true.

Best Practices

Load a widget from the same domain as the page. Use the data-crossdomain attribute to hint to Svidget that the widget is on another domain. This will speed up the loading process.

Downgrading

Downgrading for non HTML5 browsers is easy, and is the same as using the <object> tag for other purposes such as a Flash object.

Fork me on GitHub