Copyright © 1999 W3C
(MIT,
INRIA,
Keio), All Rights Reserved. W3C
liability,
trademark,
document
use and
software
licensing rules apply.
Behavioral Extensions provide ways for declaration of standalone behaviors that can be attached to any HTML or XML element, without modification of the DTD. Partly based on extensions to Cascading Style Sheets (CSS), these behaviors can be applied to multiple documents using the existing CSS linking mechanisms.
This document is a result of the discussions held in the CSS+FP Working Group and based on the submissions made to the W3C by Netscape Communications Corp. (Action Sheets [ACTIONS]) and Microsoft Corp. (HTML Components [HTC]).
This is a Working Draft of the W3C in the style activity. This draft may be updated or dropped as a work item of W3C at any time. It should not be referenced except as a work-in-progress. Experimental implementations are encouraged, as long as they are clearly marked as experimental. The working group would like to receive feedback: comments on this draft may be send to the editors, discussion takes place on the (archived) public mailing list www-style@w3.org. W3C Members can also send comments directly to the CSS & FP working group.
To learn about the current status of this working draft, see the W3C Technical Reports page or follow the "latest version" link above.
Web applications now use scripting languages on an intensive scale in order to add dynamism to web documents. In HTML [HTML], scripts are embedded into the document and a site editor has to duplicate a script in all documents that have to use it. The maintenance of large webs using scripts is then very complex and becomes a strong limiting factor.
External behaviors attached to a set of documents using the linking mechanism of CSS [CSS] styles to a HTML or XML [XML] document could provide a solution to this problem. Such behaviors would be easily reusable and combinable by site editors.
The current document provides two complementary ways of building reusable behaviors. The first one attaches to elements event handlers that trigger the execution of a piece of script. The second one attaches an external component to an element using a single declaration. In both cases, the declarations are contained in CSS stylesheets and use the power of the CSS selection mechanism.
Most of actual active documents implement script run in response to user events.
Events are actually triggered by HTML attributes (onclick
, onload
,
...) and script is always contained in the SCRIPT
element. There
is actually no way to add event-driven script to XML. It is then proposed to
add event handlers to CSS. The attachement of an event handler to a HTML or
XML element is declared using a property qualifying the triggering event and
a value for this property that contains the code to execute. For instance :
DIV.header > H1.TOCtitle { color : red ; onclick : "fold_or_unfoldTOC(event)" }
Because it is highly desirable for reusability reasons to extract script data from HTML documents and because it is not possible for the moment to embed script into a XML document, an embedding or linking mechanism of a piece of script into a CSS stylesheet is also proposed, using a new at-rule.
On the other hand, HTML Components (a.k.a. HTC) allow to build separate and
complex behaviors. These behaviors are described by a HTML document plus some
specific elements. Such a document can contain, like a common HTML document,
script code into a SCRIPT
element. It can also define, using the
HTC specific elements, properties and methods that can be exposed to the HTC
consumer and events that can be used for communication purposes by the HTC and
the HTC Consumer.
A HTC is attached to a HTML or XML element using a single declaration based on a new property :
DIV#eagle { behavior : url(http://www.w3.org/bhv/eagle.thc) }
It is then possible for a site editor to reuse an existing HTC just adding a single CSS declaration to his document and eventually using the methods, properties and events it defines.
Both mechanisms provide complementary and powerful extensibility functionalities
that will allow the componentization of large web applications and the separation
of structure and behaviors. Behavioral Extensions to CSS should be an important
element for the growth of the World Wide Web.
The HTML 4.0 Recommendation lists a set of intrinsic events that apply to HTML elements. Script event handlers can be associated with an element by specifying them as attributes of the element. For example, an event handler for a mouse click event can be set on a HTML INPUT element by specifying its onchange attribute:
<INPUT NAME="userName" onchange="validUserName(this.value)">
The HTML mechanism for specifying event handlers holds the restriction that handlers may only be associated with individual elements and not entire element classes. Furthermore, this mechanism can not be applied to XML documents without modifying the DTD for the document. This section specifies a set of new CSS properties that are similar to the HTML event handler attributes. As with all other CSS properties, these new properties may be applied to the elements specified by an arbitrary CSS selector in either a HTML or XML document.
This section does not attempt to exhaustively specify the list of all event properties applicable to XML or HTML elements. This list will be defined by the Document Object Model Level 2 [DOM2] specification. The property definition below is provided as an example of an event property. Note that some event properties may only apply to specific elements or to other media groups.
Value: | <script> |
Initial: | none |
Applies to: | all elements |
Inherited: | no |
Percentages: | N/A |
Media: | visual,interactive |
The event handler properties refer to the <script> value type. This type is a string and is governed by the same syntactic rules as the <string> value type. This string represents a script that is evaluated when the event occurs for the subject of the selector. The syntax of the script data depends on the scripting language.
@script
rule can be used to include blocks of script in a style sheet.
A @script
rule can take one of the following forms:
@script
followed by a script language content type but
no script content can be used to specify the default scripting language for
all event property values and script blocks in a style sheet:
@script "text/javascript";
If no explicit default scripting language is specified, the choice of scripting language is user agent dependent.
@script
followed by an optional script language content
type and script content can be used to include script inline in a style sheet.
The script content must be enclosed in braces and the entire script block
is governed by the syntactic
rules that apply to all CSS2 blocks.
@script { var count = 0; function checkCount() { if (document.getElementsById("radio").value == "add") { count++; } else { count--; } } }
While the contents of an @script
block is scripting language
dependent, parentheses (( )
), brackets ([ ]
),
braces ({ }
), single ('
) and double quotes ("
)
must always occur in matching pairs. Any unmatched occurrences of these
characters must be escaped.
Scripts within @script
rules are evaluated once per document
when the containing style sheet is first applied to the document.
@script
rule may be specified via
a URL. In this case, the contents of the linked file are loaded and evaluated
when the containing style sheet is first applied to the document.
@script "text/javascript" url("myscript.js");
An @script
rule can occur at any place in a style sheet where
a regular rule set is allowed. Media specific scripts may be placed in @script
rules nested within an @media
rule. Note, however, that @script
rules are evaluated for all media groups that are applicable to the user
agent.
Script blocks defined in a style sheet are scoped by it. It implies that a
given script function can be defined per media, using enclosing @media
rules. The script blocks defined by the document in a SCRIPT
element
belong to the same scope.
Value: | <uri> | none | inherit |
Initial: | none |
Applies to: | all elements |
Inherited: | no |
Percentages: | N/A |
Media: | all |
This property links an element to a component that may handle or cause dynamic behavior by accessing the element's object model, or may provide its own properties, methods and events exposed on the element. The value of the property is a URI, which links to an HTML Component (HTC). The 'none' value allows to override an already defined behavior.
<STYLE> /* Link all DIVs with a CLASS of "fly" to the "flyin" component */ DIV.fly { behavior: url("flyin.htc"); } </STYLE>
Components encapsulate the definition of properties, methods, and events for an element. This allows a Component author to expose custom methods and properties on an element in the document tree's object model, as well as defining custom events and event handlers and firing custom events on that element.
Note : this definition of Components is intended to be defined as an XML Schema that may apply to XML documents as well as HTML or XHTML.
An HTML Component is essentially just an HTML page wrapped up in some special descriptors that define what properties and methods are exposed by the Component, and what events are defined by and attached to by the HTC. In order to provide an easy path for HTML developers to move to developing components, an HTC is literally a normal HTML file with a few defined XML elements inserted in the document. This gives an easy and consistent migration path for existing HTML applications.
HTML Components are built on HTML for a number of reasons - HTML has a robust and well-known set of semantics, and already has widely supported scripting and object model semantics. HTML Components can contain regular HTML elements - but visual elements and text content are not by default to be rendered in place of any HTML element that the Component may be attached to.
NOTE: Although the descriptions in this document do not show them (since a
particular namespace would need to be chosen), all HTC elements need to be namespaced,
with a proper namespace definition in the HTML start tag. This set of
component-specific elements is not an addition to HTML; it is a set of new XML
elements that can be added to the document. The examples reflect this requirement.
The HTC elements should be found in the HEAD
of the HTML.
The COMPONENT
element serves as a container to identify an HTML
Component. It is not required; however, in many instances it may be useful
in order to help the consumer of the HTML Component determine that an event
was in fact fired from this particular component . The COMPONENT
element serves to bind together the properties, methods and events as well as
to provide a location for the identifier of this HTC.
Syntax
<COMPONENT URN=string>
Attributes
- URN
- Required. String, in Uniform Resource Name (URN) format, that uniquely identifies the component. This allows events to be uniquely identified when multiple behaviors may be firing events of the same name, by setting a srcURN property on the event object to the URN of the behavior that fired the event.
Element Information
Number of occurrences None or One per Component interface exposed Parent elements None Child elements ATTACH, EVENT, METHOD, PROPERTY
The PROPERTY
element defines a property of the HTML Component
to be exposed to the HTC consumer.
Syntax
<PROPERTY NAME = string ID = string GET = string PUT = string PERSIST = string >
Attributes
NAME
- Required. String that identifies the property to the containing document. By default, the
NAME
specified is used to refer to the property within the Component.ID
- Optional. String that can be used to identify this property element within the Component (similar to the HTML
ID
attribute).GET
- Optional. String that specifies the function to be called when the value of the property is retrieved.
PUT
- Optional. String that specifies the function to be called when the value of the property is set.
PERSIST
- Optional. Boolean that specifies whether or not the property should be persisted as part of the page if the HTC consumer supports persistence of HTML documents.
Object Model Methods
- propertyID.fireChange()
- The HTML Component can call this method on a
PROPERTY
element to inform the containing document that the value of the property has changed by causing the onpropertychange event to fire on the element to which the Component is attached.
Element Information
Number of occurrences Any number Parent elements COMPONENT or None Child elements None
Remarks
In XML, an empty element is denoted by the start tag ending with a "/>". Therefore, a
PROPERTY
element can be written as:<PROPERTY NAME="width" />By specifying a
NAME
attribute similar to a standard property already defined for the element that invokes it, a Component can override the element's normal property with that name.
Example
The following example implements an example of providing state properties from the mouse events through an HTML Component. The HTC exposes a 'hovering' property to the containing document to indicate whether the mouse cursor is currently positioned over the element or not.
<HTML xmlns:PUBLIC="urn:HTMLComponent"> <PUBLIC:PROPERTY NAME="hovering" GET="get_hovering"/> <PUBLIC:ATTACH EVENT="onmouseover" HANDLER="event_onmouseover" /> <PUBLIC:ATTACH EVENT="onmouseout" HANDLER="event_onmouseout" /> <SCRIPT LANGUAGE="JScript"> var mouseover = false; function event_onmouseover() { mouseover = true; } function event_onmouseout() { mouseover = false; } function get_hovering() { return mouseover; }
The METHOD
element defines a method of the HTML Component (HTC)
to be exposed to the HTC consumer.
Syntax
<METHOD NAME = string />
Attributes
NAME
- Required. String that specifies the name of the method that is exposed to the containing document. By default, the
NAME
attribute is also used to refer to the method within the component.
Element Information
Number of occurrences Any number Parent elements COMPONENT or None Child elements None
Remarks
By specifying a
NAME
attribute similar to a standard method name already defined for the element, a Component can override the element's implementation for that method..
Example
The following code demonstrates how to use the
METHOD
element to expose the startFlying() method from the HTC to the containing document.<HTML xmlns:PUBLIC="urn:HTMLComponent"> <PUBLIC:METHOD NAME="startFlying" /> <SCRIPT LANGUAGE="JScript" > function startFlying() { // insert flying code here } </SCRIPT>For more of this sample, see the Example section.
Defines an event that the HTML Component that will use to communicate with
the HTC consumer. This event may be a predefined HTML event (e.g. the
onmouseover event), or it may be a new "custom event" that the Component
defines here. In either case, the EVENT
element defines that the
Component wants to either fire or handle this event for the consumer.
Syntax
<EVENT NAME = string ID = string />
Attributes
NAME
- Required. String that specifies the name of the event that is exposed to the HTC consumer.
ID
- Optional. String that identifies the event tag within the component.
Methods
- this.fire([oEvent])
- Fires the event to the HTC consumer. This method has an optional oEvent parameter that specifies an event object containing context information. The details of the event object is to be defined by a future release of the W3C Document Object Model.
Element Information
Number of occurrences Any number Parent elements COMPONENT or None Child elements None
Remarks
By specifying the same
NAME
attribute as a standard event already defined for the element, a behavior can override the element's default event handler for that event.Events defined for a behavior do not bubble and only fire on the element to which the behavior is attached.
Example
The following example is derived from a calculator behavior sample. Whenever the result changes, the HTC fires a custom onResultChange event back to the page, passing the result as an additional property on the event object. This example presupposes a createEventObject() method - the Document Object Model does not yet cover events, so this usage may be temporary.
<HTML xmlns:PUBLIC="urn:HTMLComponent"> <PUBLIC:EVENT NAME="onResultChange" ID="eventOnResultChange" /> <SCRIPT LANGUAGE="JScript"> function doCalc() { : oEvent = createEventObject(); oEvent.result = sResult; eventOnResultChange.fire (oEvent); }Here's what the containing page looks like:
<HTML xmlns:LK="urn:com.microsoft.htc.samples.calc"> <HEAD> <STYLE> LK\:CALC { behavior:url(engine.htc); } </STYLE> </HEAD> <LK:CALC ID="myCalc" onResultChange="resultWindow.innerText=window.event.result"> <TABLE> <TR><DIV ID="resultWindow" STYLE="border: '.025cm solid gray'" ALIGN=RIGHT>0.</DIV></TR> <TR><TD><INPUT TYPE=BUTTON VALUE=" 7 "></TD> <TD><INPUT TYPE=BUTTON VALUE=" 8 "></TD> <TD><INPUT TYPE=BUTTON VALUE=" 9 "></TD> <TD><INPUT TYPE=BUTTON VALUE=" / "></TD> <TD><INPUT TYPE=BUTTON VALUE=" C "></TD> </TR> <TR><TD><INPUT TYPE=BUTTON VALUE=" 4 "></TD> <TD><INPUT TYPE=BUTTON VALUE=" 5 "></TD> <TD><INPUT TYPE=BUTTON VALUE=" 6 "></TD> <TD><INPUT TYPE=BUTTON VALUE=" * "></TD> <TD><INPUT TYPE=BUTTON VALUE=" % " DISABLED></TD> </TR> <TR><TD><INPUT TYPE=BUTTON VALUE=" 1 "></TD> <TD><INPUT TYPE=BUTTON VALUE=" 2 "></TD> <TD><INPUT TYPE=BUTTON VALUE=" 3 "></TD> <TD><INPUT TYPE=BUTTON VALUE=" - "></TD> <TD><INPUT TYPE=BUTTON VALUE="1/x" DISABLED></TD> </TR> <TR><TD><INPUT TYPE=BUTTON VALUE=" 0 "></TD> <TD><INPUT TYPE=BUTTON VALUE="+/-"></TD> <TD><INPUT TYPE=BUTTON VALUE=" . "></TD> <TD><INPUT TYPE=BUTTON VALUE=" + "></TD> <TD><INPUT TYPE=BUTTON VALUE=" = "></TD> </TR> </TABLE> </LK:CALC> </HTML>
The ATTACH
element is used to bind a function in the HTC to an
event so that the function gets called whenever the event fires on the component's
element.
Syntax
<ATTACH EVENT = sEvent FOR = "document" | "element" | "window" HANDLER = sEventHandler URN = sURN />
Attributes
EVENT
- Required. String that specifies the name of an Object Model event, or either of the HTC-specific events, oncontentchange or ondocumentready.
FOR
- Optional. String value that identifies the object for which the event is fired.
document
The component should attach events to and send events to the document object. element
The component should attach events to and send events to the element object. window
The component should attach events to and send events to the window object. HANDLER
- Required. String that specifies the name of the function to handle the event.
URN
- Optional. String that specifies the URN of the source of the event to attach to. This is useful when multiple sources of the event exist.
Element Information
Number of occurrences Any number Parent elements COMPONENT or None Child elements None
Remarks
When the specified event fires on the element to which the component is attached, the element's event handler is called first, before that of the component.
Example
The following example implements an expanding/collapsing table of contents using an HTC. The HTC attaches to the element's onclick event and expands or collapses the list every time the onclick event is received. In addition, it attaches to the element's onmouseover and onmouseout events to implement mouseover highlighting effect, as the expandable list items are hovered over. This example makes use of the 'element' object, described in section 7.
<HTML xmlns:PUBLIC="urn:HTMLComponent"> <PUBLIC:PROPERTY NAME="child" /> <PUBLIC:ATTACH EVENT="onclick" HANDLER="onclick_handler" /> <PUBLIC:ATTACH EVENT="onmouseover" HANDLER="onmouseover_handler" /> <PUBLIC:ATTACH EVENT="onmouseout" HANDLER="onmouseout_handler" /> <SCRIPT LANGUAGE="JScript"> function onmouseover_handler() { element.style.color = "red"; } function onmouseout_handler() { element.style.color = "black"; } function onclick_handler() { var i; var sDisplay; // Determine current state of the list (i.e. expanded or collapsed) // based on the current display property of the child. bCollapsed = (document.all(child).style.display == "none"); if (bCollapsed) { element.style.listStyleImage = "url('/workshop/graphics/blueminus.gif')"; element.style.display = ""; } else { element.style.listStyleImage = "url('/workshop/graphics/blueplus.gif')"; element.style.display = "none"; } } </SCRIPT>
HTML Components also have two events that are specific to Components. These events are specific to the relationship between a Component and its host.
The ondocumentready event fires when the behavior's containing document has been parsed.
Example
The following sample demonstrates basic usage of the ondocumentready event on a page.
<ATTACH EVENT=ondocumentready HANDLER=documentready_handler /> <SCRIPT LANGUAGE="JScript"> function documentready_handler() { window.alert ("The ondocumentready event fired."); } </SCRIPT>
The 'oncontentchange' event fires when the element to which the Component is attached has been parsed, or whenever the content of the element changes.
Example
The following sample code demonstrates basic usage of the oncontentchange event on a page.
<HTML xmlns:PUBLIC="urn:HTMLComponent"> <PUBLIC:ATTACH EVENT="oncontentchange" HANDLER="contentchange_handler" /> <SCRIPT LANGUAGE="JScript"> function contentchange_handler() { window.alert ("The oncontentchange event fired."); } </SCRIPT> </HTML>
In addition, HTCs expose an "element" object to object model consumers inside the HTML Component, which is the same Element object exposed in the original document's object model. This allows the HTC to direct access and affect the HTML element to which it is attached. See the section 6 for an example of how this can be used.
A common use of script event handling is for "roll-over" button behavior. Specifically,
the style of a button can be changed as the mouse cursor enters and leaves its
boundaries. While a similar effect can be achieved by using the :hover
psuedo-class, script event handlers can be used to change more than just the
style of the target. In this example, onmouseover
and onmouseout
event properties are used to change attributes of a HTML element and the contents
of a text node in the containing HTML document.
The HTML document contains the following elements:
<img class="RollOver" src="yes.gif" oversrc="hiliteyes.gif" outsrc="yes.gif" status="Press to answer Yes!"> <img class="RollOver" src="no.gif" oversrc="hiliteno.gif" outsrc="no.gif" status="Press to answer No!">
Note that oversrc
, outsrc
and status
are not specified as valid attributes for the IMG
element in the
HTML 4.0 DTD. However,
as per DOM Level 1, unrecognized
attributes of HTML elements are accessible through the DOM Element
interface.
One of style sheets linked to by the HTML documents contains the following rule:
.Rollover { border : thin solid blue; onmouseover : "this.src = this.getAttribute('oversrc'); this.style.borderColor = 'red'; statusText.data = this.getAttribute('status');" onmouseout : "this.src = this.getAttribute('outsrc'); this.style.borderColor = 'blue'; statusText.data = '';" }
When the mouse cursor moves over any image of the Rollover
class,
the onmouseover
event handler is invoked. In this example, the
event handler changes the src
attribute and the border-color
CSS property of the IMG
element, as well as the data associated
with a text element in the document.
The following code demonstrates a flying effect component, which allows a page author to easily add dynamic movement effects to their pages. This component makes use of an inline style object model that has not yet been approved by the Document Object Model Working Group.
This component makes use of most of the HTML Component features - it uses properties to allow the page author to control the type of flying effect, methods to start/stop the effect, and events to both set up the properties needed in the page and notify the page when the effect is finished (via a custom event).
<HTML xmlns:PUBLIC="urn:HTMLComponent"> <PUBLIC:METHOD NAME="start" /> <PUBLIC:METHOD NAME="stop" /> <PUBLIC:PROPERTY NAME="direction" GET="getDir" SET="setDir" /> <PUBLIC:EVENT NAME="onFlyFinished" /> <PUBLIC:ATTACH EVENT="onclick" HANDLER="onClick" /> <SCRIPT LANGUAGE="ECMAScript" > var direction = 0; // 0 = left, 1 = top, 2 = right, 3 = bottom var x = 0; var y = 0; var xScaler = 0; var yScaler = 0; var bFlying = false; var timer; function start() { switch ( direction ) { case 0: // left x = -100; xScaler = 5; y = 0; yScaler = 0; break; case 1: // top x = 0; xScaler = 0; y = -100; yScaler = 5; break; case 0: // right x = 100; xScaler = -5; y = 0; yScaler = 0; break; default: // bottom x = 0; xScaler = 0; y = 100; yScaler = -5; break; } bFlying = true; element.style.position = "relative"; tick(); } function stop() { if ( bFlying ) { window.clearTimeout( timer ); element.style.left = "0"; element.style.top = "0"; bFlying = false; var eventElem = document.getElementsByTagname( "public:event" )[0]; var oEvent = createEventObject(); eventElem.fire( oEvent ); } } function setDir( dir ) { if ( dir == "left" ) direction = 0; else if ( dir == "top" ) direction = 1; else if ( dir == "right" ) direction = 2; else direction = 3; } function getDir() { switch ( direction ) { case 0: return "left"; break; case 1: return "top"; break; case 0: return "right"; break; default: return "bottom"; break; } } function tick() { element.style.left = x; element.style.top = y; x += xScaler; y += yScaler; if ( x == 0 && y == 0 ) stop(); else timer = window.setTimeout( "tick()", 100 ); } function onClick() { alert( "x is '" + x + "%', y is '" + y + "%'." ); } </SCRIPT>
The HTML page that uses this might look like this:
<HTML> <HEAD> <STYLE> #flier { behavior: url( fly.htc ); text-align: center; } </STYLE> </HEAD> <BODY> <H1 ID=flier>Flying titles!</H1> <P><BUTTON onclick="document.getElementsByTagname( "H1" )[0].stop();">Stop </BUTTON></P> <SCRIPT> var flyingElem = document.getElementsByTagname( "H1" )[0]; flyingElem.onFlyFinished = "alert('finished flying!');" flyingElem.direction = "left"; flyingElem.start(); </SCRIPT> </BODY> </HTML>
http://www.w3.org/pub/WWW/TR/REC-CSS2
)http://www.w3.org/TR/REC-xml
)http://www.w3.org/TR/REC-html40/
)http://www.w3.org/TR/NOTE-AS
)http://www.w3.org/TR/NOTE-HTMLComponents-19981023
)http://www.w3.org/TR/REC-DOM-Level-1/
)http://www.w3.org/TR/WD-DOM-Level-2/events.html
)