Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

SVG and XForms: Rendering Custom Content

Using the new SVG 1.2 XML-based extension mechanism

Antoine Quint (antoine.quint@fuchsia-design.com), SVG Consultant and Research Scientist, Fuchsia Design
Antoine Quint is an independent SVG consultant and research scientist participating in the W3C SVG Working Group as an invited expert. He also enjoys teaching, presenting at conferences held in nice places, and doing outsourcing work, all of this SVG-oriented. When at home in Paris, he lives with his über-cat Stig Elmer. Antoine also has a much-delayed book in the works with his old pal Robin Berjon.

Summary:  The first Scalable Vector Graphics specification (SVG 1.0) laid the standard for XML-expressed two-dimensional interactive and animated graphics. Since then, the W3C SVG Working Group has made efforts to take SVG a step further with a strong focus on enhancing features that ease the work of using SVG for Web and desktop application development. One of the most promising features introduced in SVG 1.2 is Rendering Custom Content (RCC) -- it offers a clean XML-centric extension mechanism to mix and match different XML namespaces within an SVG document. This article takes you through the creation of a simple push-button widget while introducing the RCC mechanism.

Date:  25 Nov 2003
Level:  Intermediate
Also available in:   Japanese

Activity:  9161 views
Comments:  

For the past couple of years, the SVG community has investigated new ways of leveraging the XML basis of SVG to mix and match namespaces in an SVG document. Among the main focuses, user-interface work emerged as a primary use-case for the usage of custom XML markup inline in an SVG document. Rendering Custom Content (RCC), a new technology featured in the latest drafts of SVG 1.2, offers a new framework for allowing seamless integration of custom XML grammars in SVG documents as well-defined extensions. As mentioned in the primer for this series, RCC plays a key role in SVG and XForms integration as it provides the foundation for mixing and matching the two markups. In this article, I will introduce the basics of RCC, walk through the implementation of a first user-interface widget (a push-button), and provide a demo of a richer UI application.

RCC basics

Before I dive into coding this simple button widget using RCC, I will go through the basics and the newly-introduced family of RCC elements.

Binding XML to graphics

Using RCC, you can specify how a given foreign-namespaced element should behave in an SVG document. Here's the most basic SVG code you'll have to write to start hacking with RCC:


Listing 1. Setting up an RCC environment
<extensionsDefs namespace="http://ns.example.org/MyCustomNamespace/">
  <elementDef name="myCustomElement">
    <prototype>
      <!-- SVG markup specifying rendering here -->
    </prototype>
  </elementDef>
</extensionsDefs>

In Listing 1, two new SVG 1.2 elements are introduced. <extensionDefs> is the top-level element in which all your element definitions in a given namespace will be enclosed. The namespace is specified in the aptly-named namespace attribute which in a very straightforward manner takes a URI as a value. Just to avoid confusion further down the road, this attribute does not act at all like the XML xmlns attribute -- the namespace here is always SVG; the namespace attribute here simply denotes the namespace in which the custom elements are described. Conceptually, <extensionDefs> helps you define a library. Now, you need to define what lives in this namespaced library. To register an element, you need to use the <elementDef> element and specify its name with the name attribute.

Basically, RCC is a binding mechanism that matches a non-SVG XML element to an SVG tree. However, RCC was not designed with every kind of binding in mind; instead the mechanism focuses on allowing binding of custom XML markup that should have some interactive rendering -- hence the mapping to an SVG fragment. As I mentioned in the primer, this SVG fragment in the RCC world is actually a shadow tree. I cannot emphasize enough the various degrees of magic that the shadow trees bring in, and I will get back to this later on as it is crucial to understand this feature. To link a custom element with a pre-set shadow tree, you need to use the new <prototype> element. The contents of this element are automatically copied into the shadow tree of each instance of the element for which it is defined at binding time.

Binding can happen at two different times in the life of the document. The first is at load time: The SVG implementation initially checks the document for existing <extensionDefs> elements and figures out what foreign elements are known; then while parsing the rest of the tree it triggers the RCC binding mechanism every time it encounters one of these known elements. The binding can also occur when one of these known elements is added to the tree, for instance through an insertChild() DOM call. This makes the whole binding mechanism live, which is one important difference between this process and the way XSLT binds an XML element to another XML output using templates (the latter is essentially a run-once, batch process operation).

Shadow trees

Now that the binding mechanism is clearer, it is very important for you to fully understand the importance and the usage of shadow trees in an RCC context. As I said in my previous article, one huge advantage of shadow trees for the rendering of custom elements is that they leave a healthy and un-cluttered document tree. Writing a component, such as a user-interface widget, using RCC may well involve some clever script work with the SVG representation of your abstract markup. With shadow trees this code logic can rely on an SVG sub-tree that is not easily available to external code, though it can still be accessed if needed. In parallel to the aforementioned elements being added for RCC support, SVG 1.2 adds a DOM interface SVGShadowable implemented by all instances of custom elements. Using this interface you can gain access to a custom element's shadow tree with a call such as myCustomElement.shadowTree.

RCC and DOM events

Since your custom element's shadow tree is always accessible to you through the DOM, you can use a live binding when hacking a component. You are constantly notified of what goes on with your custom element if you listen to the right DOM events that it can trigger. The RCC binding mechanism provides you with two new bind-specific events:

  • SVGBindBegin corresponds to the initial binding phase, when the custom element has been parsed but the shadow tree has not yet been built and rendered.
  • SVGBindEnd notifies you of the completion of the binding phase, when your element has been bound and rendered.

If you want your custom element to continue to be live, you have to rely on DOM mutation events, such as DOMAttrModified for attribute modifications and DOMSubTreeModified when you work with a custom container that should react to changes in its sub-tree.

Modularity

I briefly touched on the concept of a library definition using <extensionDefs>. This concept is also greatly justified by the fact that you don't actually have to re-define your extensions inline for every single document that may use them. You can actually import a library using the xlink:href attribute to point to where your library is defined. So, for instance, you could define and use an RCC-based XForms implementation extension as follows:


Listing 2. RCC extension architecture across multiple files
XFormsImplementation.svg

<svg version="1.2">
  <extensionDefs id="xforms" namespace="http://www.w3.org/2002/xforms">
    <!-- implementation -->
  </extensionDefs>
</svg>

SomeFileUsingSVGandXFormsWithYourSpiffyRCCImplementation.svg

<svg version="1.2">
  <extensionDefs xlink:href="XFormsImplementation.svg#xforms" />
  <!-- some code that can use both XForms and SVG elements -->
</svg>

With this simple but elegant library architecture, nearly anyone can create an RCC library, put it online, and make it available to others on the Web. This simplifies development of SVG applications using RCC, and fosters some community-driven work aimed at offering free libraries in the near future. Another thing worth noting is that the contents of a <prototype> element are not limited to good old SVG graphics elements, but can also include RCC-defined custom elements too. So RCC is also re-entrant and this recursive aspect is a great advantage for creating components that re-use and build atop other components. Putting this all together creates an exciting framework to work with. Now you can get going and do some work.


Hacking with RCC: <ui:button>

As I said in my introduction, a common use case for RCC is creating UI widgets. Creating a good UI widget, even a simple one, requires elaborate work. Here, I will show you how to design a very simple (in both graphical and programmatic respects) push button.

The markup

In an RCC architecture, the markup really serves as the blueprint of the functionality you need to implement. When you have a component in mind that you want to design using SVG and the RCC mechanism, the first thing that you will likely do is jot down some ideas on paper and figure out how your markup should look. Here, I am designing a simple push button, which will feature a text label. Staying in a simple mood, I create a simple element with geometry and position attributes and a text label as a child node:

<ui:button x="10" y="40" width="150">ui:button</ui:button>

... and this is how it looks:


Figure 1. A rendered <ui:button> element
A rendered <ui:button> element

I have my <button> element. If I were a good citizen, I would provide a matching piece of schema to document it. But I'll focus on the SVG side instead and let you make the right decisions when you design your own component. My <button> element is not an SVG element, and as such it needs to live in a different namespace. You probably noticed that I used the ui prefix in the snippet above, and this prefix is defined on the root <svg> element of my SVG file:


Listing 3. Declaring the namespaces in my document
<svg  xmlns="http://www.w3.org/2000/svg"
      xmlns:ui="http://xmlns.fuchsia-design.com/ui/"
      xmlns:ev="http://www.w3.org/2001/xml-events"
      version="1.2">

My own ui namespace is now defined and I can safely use my <ui:button> later on in the file when actually placing different instances of the button. Don't bother trying to reach my namespace's URI, it points to nothing; here again I recommend a better approach for your own impending and more elaborate work, and hope you'll use an RDDL file or something similar to provide some information about your components. I have also defined a couple more namespaces and prefixes which feature, of course, the SVG namespace (made default) and the XML Events namespace (with the ev prefix). Incidentally, the XML Events recommendation is now a mandated part of SVG 1.2, so now you have both a design and a namespaced environment. This allows you to go to the next phase, which involves markup again. This time it will be the meat of your component definition:


Listing 4. First steps of my foreign element's implementation
<extensionDefs namespace="http://xmlns.fuchsia-design.com/ui/">
  <elementDef name="button">
    <prototype>
      <svg id="root" width="0" height="26">
        <rect id="rect" style="stroke: black; fill: lightgray;"
              x=".5" y=".5" rx="5" ry="5" width="0" height="25" />
        <text x="50%" y="65%" 
              style="pointer-events: none; text-anchor: middle;">
          <refContent />
        </text>
      </svg>
    </prototype>
    <script ev:event="SVGBindEnd" type="text/ecmascript">
      new SimpleRCCButton(evt.target);
    </script>
    <script type="text/ecmascript">
      <!-- SimpleRCCButton class -->
    </script>
  </elementDef>
</extensionDefs>

I start with a wrapping <extensionDefs> element to notify the start of my library definition, described in the ui-prefixed namespace as defined on the root <svg> element. Next up is the <elementDef> element initiating the definition of my <ui:button> element. Now that I can notify the SVG implementation of a new custom element, I define how it looks with the <prototype> element. As I said earlier, this is where you place all the SVG that is copied into the shadow tree of each custom element instance. As you saw in Figure 1, my poor-man-graphics button is a rounded rectangle with text in the center. SVG's powerful graphics primitives and text features make writing the SVG prototype for my button simple.

You can use the <rect> element with the rx and ry attributes for the rounded corners. Its style attributes specify the CSS properties: a light-gray fill and black stroking. It has a fixed height of 25 and a width of 0.... zero!? Well, the width of the rectangle is tightly connected to the width of the <ui:button> itself, and as you'll see later on, this programming will update the rectangle's width at the right time. The x and y attributes both have a value of 0.5 so that the stroke isn't clipped by the parent <svg> element. Then comes the <text> element, which has its text-anchor placed in the middle of the string, doesn't receive mouse events (the rectangle suffices to catch them all and it will avoid the nasty visual effect of text-selection if the user clicks on the text itself when pushing the button), and is located in the middle of the current viewport as established by the parent <svg> element.

You might have noticed an oddity though: <svg> and <rect> both have id attributes on them. How do these ids remain unique if they're replicated in each generated shadow tree? RCC solves this issue with local-scoped ids. The reason for having these ids around becomes clearer when I get to the code. You might also have noticed another oddity -- the <refContent/> element. This is another new element introduced in SVG 1.2 for RCC support, and its magic lies in the fact that it allows direct referencing of the DOM tree of the element instance. So here my <refContent/> element pastes the text label in the shadow tree, and stays in sync with the label at all times.

Now that I've defined the graphics template, the <script> element uses the XML Events' event attribute to define a handler for the SVGBindEnd event in ECMAScript. The single line of enclosed script is called when the binding of the element instance is complete and simply creates a new instance of the SimpleRCCButton class, passing its constructor a pointer to the element instance (the event's target) as the sole parameter. The created ECMAScript object is responsible for all the things that cannot be done declaratively with the markup, so basically it acts as a controller. The next <script> element encloses all the code for the SimpleRCCButton class that was snipped from the sample -- I will be describing that process next. Now that the markup part is done, it's time to get down to business and write some code!

The code

The SimpleRCCButton class's job is simple: Do what it takes to keep the button alive by reacting to both mutation and user interface events. The first task is to write the constructor for this class and make sure that you can easily keep references to several elements that you need later on:


Listing 5. The SimpleRCCButton class constructor
function SimpleRCCButton (element) {
  // keep pointers to useful bits
  this.element = element;
  var shadow = this.element.shadowTree;
  this.root = shadow.getElementById('root');
  this.rect = shadow.getElementById('rect');
  // initialize the shadow tree
  this.init();
};

The instance of the <ui:button> is passed as a parameter to the constructor function and I keep a reference to it as this.element. I then register a pointer to the instance element's shadow tree in the shadow variable through the shadowTree; shadowTree is a member of the SVGShadowable interface, which is inherited by all instances of RCC-defined custom elements. Additionally, the shadow tree includes two SVG elements that I need to track. Using the ids that were carefully set back in the <prototype>, the parent <svg> element is kept in store as this.root while the rounded-rectangle is kept in store as this.rect. Now that useful references have been established, you can call the init() method to initialize the element's shadow tree:


Listing 6. The SimpleRCCButton initialization method
SimpleRCCButton.prototype.init = function () {
  // register mutation events on the element instance
  this.element.addEventListener('DOMAttrModified', this, false);
  // register UI events on the shadow tree's rectangle
  this.rect.addEventListener('mousedown', this, false);
  this.rect.addEventListener('mouseup', this, false);
  this.rect.addEventListener('click', this, false);
  // update the position and width of the button
  this.fixButtonPosition();
  this.fixButtonWidth();
};

The first task is to register an event listener for the DOMAttrModified mutation event -- one that corresponds to an attribute value change -- on the instance element so that you can keep track of modification made through the DOM to the button. Then it's time to ask for listening for various mouse events (mousedown, mouseup, and click) on the shadow tree's rectangle -- you'll see how to react to these later. All this time you are using the essential addEventListener() method off the DOM Events EventTarget interface. Then you need to make your first fix to the position and size of the button, which involves some tweaking of the shadow tree, with the fixButtonPosition and fixButtonWidth methods:


Listing 7. The SimpleRCCButton class refresh methods
SimpleRCCButton.prototype.fixButtonPosition = function () {
  this.root.setAttribute('x', this.element.getAttribute('x'));
  this.root.setAttribute('y', this.element.getAttribute('y'));
};

SimpleRCCButton.prototype.fixButtonWidth = function () {
  var width = parseInt(this.element.getAttribute('width'));
  this.root.setAttribute('width', width + 1);
  this.rect.setAttribute('width', width);
};

Both methods involve very straightforward DOM coding. Basically, the attribute values are read off the instance element and copied to the right places in the shadow tree. For the button position, set the x and y attributes of the shadow tree's <svg> element. Fixing the button's width is only slightly more complicated: The width is copied as is on the rectangle, but incremented by one unit on the <svg> element to leave room for the rectangle's stroke.

When I registered event listeners for events, you probably noticed that this was the instance of the DOM Events' EventListener interface that I passed as a parameter (the second). I did this because the EventListener interface features a single handleEvent() method that's meant to be implemented by whichever object is the event listener. In this case, each time one of the registered events is fired, handleEvent() is called on the controller object. Now take a look at what this method looks like and how events are handled to make the button interactive:


Listing 8. The SimpleRCCButton class event-handling turnpike
SimpleRCCButton.prototype.handleEvent = function (evt) {
  var type = evt.type;
  if (type == 'DOMAttrModified') {
    this.fixButtonPosition();
    this.fixButtonWidth();
  } else if (type == 'mousedown') {
    this.rect.style.setProperty('fill', 'pink');
    window.captureMouse(evt, this.rect);
  } else if (type == 'mouseup') {
    this.rect.style.setProperty('fill', 'lightgray');
  } else if (type == 'click') {
    var event = document.createEvent('ButtonPush');
    this.element.dispatchEvent(event);
  }
};

The function obviously receives an event as the sole parameter. But first things first -- I need to figure out what type of events are received. If it's a mutation event on one of the attributes of the instance element, I'll call both of the methods responsible for fixing the button's appearance. Calling both methods each time is a little clumsy. I could have taken the time to check which attribute was modified before calling all the refresh methods, but for the sake of simplicity I went this route and trust you'll know better when writing your own code. Next up is handling of the mouse events.

Since I am implementing a push-button, I better do something so the user has the visual impression of pushing a button when the mouse is clicked over the button graphic. So when the shadow tree's rectangle receives a mousedown event, I change the CSS fill property to something a little flashier -- say pink. Then I capture the mouse so that all events from now on are captured by the rectangle, which is a classic UI trick for preventing other mouse interactivity from occurring while the user is in the process of activating the button. So when the mouse button is released (on mouseup) after being pushed, I have to make sure to revert the rectangle's fill to its original value.

So far I've shown you how to handle visual feedback of mouse interaction with the button. But the whole point of a push-button is to trigger an action when it is activated. I've decided to have my custom element fire a custom event when it is activated, so that it not only behaves like an SVG element in terms of reacting to mouse events and DOM mutations, but it also can fire the event with its own semantics. Creating and dispatching a custom event can be done with just two DOM calls. The first one creates the event with the createEvent() method called on my DOM document, which gives it a custom type -- ButtonPush. Then all that remains is to actually fire the event by calling the dispatchEvent method, defined in the incredibly useful EventTarget DOM Events interface, from my custom element.


The result

Now that the implementation of my push-button is complete, I can create a very simple application, the guts of which (other than the RCC definitions and other required SVG markup) look like this:


Listing 9. Usage of my extension element
<ui:button x="10" y="10" width="300">
  <ui:button>
  <script ev:event="ButtonPush">
    var button = evt.target;
    var width = parseInt(button.getAttribute('width'));
    button.setAttribute('width', width - 10);
  </script>
</ui:button>

<ui:button x="10" y="40" width="150">
  Another <ui:button>
  <script ev:event="ButtonPush">
    var button = evt.target;
    var width = parseInt(button.getAttribute('width'));
    button.setAttribute('width', width + 10);
  </script>
</ui:button>

Here I have two instances of my custom <ui:button> element, each with an independent shadow tree, a controller object, interactivity processing, states -- you name it. The only thing they share is that they are based on the same prototype-based RCC definition. This demo is quite simple: It has one large button, the width of which is decreased when clicked, and a small one which has an increasing width. As you can see, I can listen to my button's custom event using the same XML Events mechanism that I used for listening to SVG events back in my RCC definition. Opening the file, enclosed in the demos .zip archive in Resources, will show this:


Figure 2. Two independent push-buttons
Two independent push-buttons

In the same archive, you will find a much more advanced sample application based on RCC. This application is meant to show a more advanced usage of RCC custom elements. I have a more powerful push-button that is skinnable, and a stylable combo box that leverages the previously defined push-button and highlights the RCC's reentrancy. While this article hasn't detailed all skinnability or CSS styling of a custom element, for brevity's sake you should now have the ground cleared to stroll around the various files and figure out implementation tricks. Figure 3 shows a screenshot of this application, featuring a mock-up of the Windows XP widget look and feel, albeit 100% SVG instead of procedural GDI calls:


Figure 3. The stylable combo box application
The stylable combo box application

This article has detailed the inner workings of RCC, taken you through a simple widget's implementation, and provided the basics for understanding a more complex application. I have now covered the relevant parts of SVG that will allow you to mix XForms together with SVG in the next article. In the last part of this series, I will show you how to wrap my existing RCC-based UI widgets in some higher-level XForms controls and re-code the stylable combo box demo with XForms. So until next time, take it easy.



Download

NameSizeDownload method
x-svgxf2resources.tgz13KB HTTP

Information about download methods


Resources

About the author

Antoine Quint is an independent SVG consultant and research scientist participating in the W3C SVG Working Group as an invited expert. He also enjoys teaching, presenting at conferences held in nice places, and doing outsourcing work, all of this SVG-oriented. When at home in Paris, he lives with his über-cat Stig Elmer. Antoine also has a much-delayed book in the works with his old pal Robin Berjon.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=XML
ArticleID=12347
ArticleTitle=SVG and XForms: Rendering Custom Content
publish-date=11252003
author1-email=antoine.quint@fuchsia-design.com
author1-email-cc=dwxed@us.ibm.com