Below is a proposal Ian Hickson made to replace CSSValue and co somewhere at the end of 2002. This email makes the proposal public. For reference the original Member-only proposal can be found here:

  http://lists.w3.org/Archives/Member/w3c-css-wg/2002OctDec/0264.html

===

I discussed possiblities for a replacement for CSSValues with some of
the web authors who have been the most vocal to me about the need for
a non-string-based API for complex manipulation of the CSSOM.

First, here are some examples showing what the API currently requires
to do simple tasks. [1]

1. Increasing the 'top' property of a style declaration by one pixel.

   Using the CSSValues API:

      var top = style.getPropertyCSSValue('top');
      top.setFloatValue(top.CSS_PX, top.getFloatValue(top.CSS_PX) + 1);


   Using the string-based API (assumes that the property is already
   set in pixels):

      style.top = parseInt(style.top) + 1 + 'px';


   Using the API proposed below:

      style.top.px++;


2. Getting the URL of the background image of an element.

   Using the CSSValues API:

      var style = document.defaultView.getComputedStyle(element, '');
var url = style.getPropertyCSSValue('background-image').getStringValue();


   Using the string-based API:

      var style = document.defaultView.getComputedStyle(element, '');
      var url = style.backgroundImage;
      url.replace(/^url\(['"]?/, '');
      url.replace(/['"]?\)$/, '');
      url.replace(/\\(.)/, '$1');


   Using the API proposed below:

      var url = element.computedStyle.backgroundImage.url;


3. Increasing the red component of color by 1 part in 255 (typically
   this would really be changing two or three values, or the hue, this
   is just a simple example).

   Using the CSSValues API:

      var red = style.getPropertyCSSValue('color').getRGBColorValue.red;
red.setFloatValue(red.CSS_NUMBER, red.getFloatValue(red.CSS_NUMBER) + 1);


   Using the string-based API:

      var color = style.color;
      var red, green, blue;
      if (color.match(/^rgb\(([0-9]+), ?([0-9]+), ?([0-9]+)\)$/)) {
        red = RegExp.$1;
        green = RegExp.$2;
        blue = RegExp.$3;
} else if (color.match(/^rgb\(([0-9.]+)%, ?([0-9.]+)%, ?([0-9.]+)%\)$/)) {
        red = 255 * RegExp.$1 / 100;
        green = 255 * RegExp.$2 / 100;
        blue = 255 * RegExp.$3 / 100;
      } else {
       // XXX incomplete
throw "#rrggbb, #rgb, and named constants not supported (received '" + color + "')";
     }
     red++;
     style.color = 'rgb(' + red + ',' + green + ',' + blue + ')';


   Using the API proposed below:

      style.color.red++;


I maintain that there is definitely a need for DOM-based manipulation
of a CSSOM, because SMIL, while great for simple or even moderately
complex animations, is not able to do things such as a Pacman game.
The existence of sites such as http://www.javascript-games.org/ is
pretty clear evidence that there is a demand for programmatic access
to the DOM and style-related issues.

The complexity of the examples given above speaks for itself. The
existing interface is just not suitable.

What follows is a straw-man proposal based on feedback I have received
from Mozilla's CSSOM implementors and dynamic content authors.


Requirements Summary:

   Being able to manipulate style data through a scriptable object
   model without an intermediate string representation.

   Being able to do so without going through pointless theoretical
   methods and attributes.


Examples using the proposal below:

   // increase font-size by 1px
   element.style.fontSize.px++;

   // set width to x%, where x is a variable
   rule.width.percent = x;

   // desaturate the colour of an element
for (property in ['color', 'background-color', 'border-top-color' /* ... */]) { document.getOverrideStyle(element, null).getProperty(property).saturation = 0;
   }

   // set content to 'url(example) counter(headers) "."'
   style.content.clear();
   style.content.append(CSSItemURL.create('example'));
   style.content.append(CSSItemCounter.create('headers', 1));
   style.content.append(CSSItemString.create('.'));

   // get the background-image URL (possibly relative)
   var url = style.backgroundImage.url;

   // get the background-image URL (resolved to an absolute URL)
   var url = style.backgroundImage.absoluteUrl;


Interfaces for this Straw Man Proposal:

   interface CSSStyleDeclaration {
     CSSPropertyValue getProperty(in DOMString propertyName);
     // (plus everything in DOM2 except getPropertyCSSValue)
   }

// Available using binding-specific casting mechanisms on CSSStyleDeclaration
   interface CSSStyleDeclarationProperties {
     attribute CSSBackgroundAttachmentPropertyValue backgroundAttachment;
     attribute CSSColorPropertyValue backgroundColor;
     attribute CSSURLPropertyValue backgroundImage;
     attribute CSSBackgroundPositionPropertyValue backgroundPosition;
     // ...
     attribute CSSContentPropertyValue content;
     // etc...
     // Each longhand property has an attribute on this interface.
     // Initially we might want to skip properties which only take
     // keywords, as string manipulation is not a problem with those.
   }

   interface CSSPropertyValue {
     // Note. On bindings with string coercion (such as
     // ECMAScript) the string representation of this value must
     // be the same as its cssText attribute. This allows the
     // CSSStyleDeclarationProperties and CSS2Properties
     // interfaces to be used interchangeably without casting in
     // some environments.
     attribute DOMString cssText;

     const unsigned short CSS_UNSET   = 0;
     const unsigned short CSS_INHERIT = 1; // CSS2
     const unsigned short CSS_INITIAL = 2; // CSS3
     const unsigned short CSS_DEFAULT = 3; // CSS3
     const unsigned short CSS_ATTR    = 4; // CSS3
     const unsigned short CSS_VALUE   = 16;
     attribute unsigned short value; // takes one of the CSS_* constants

     // This will be null unless value == CSS_ATTR
     // setting it sets value to CSS_ATTR
     attribute AttrExpression attr;
   }

   // initially we might want to avoid mentioning attr altogether
   interface AttrExpression {
     const unsigned short CSS_STRING = 0;
     attribute DOMString attribute;
     attribute unsigned short type;
     attribute CSSPropertyValue default;
     AttrExpression create(in DOMString attribute,
                           in unsigned short type,
                           in CSSPropertyValue default);
   }

   // initially we might want to skip out on the keyword-only properties
   // since string manipulation for those is no big deal
   interface CSSBackgroundAttachmentPropertyValue {
     const unsigned short CSS_SCROLL = CSS_VALUE + 0;
     const unsigned short CSS_FIXED  = CSS_VALUE + 1;
   }

   interface CSSColorPropertyValue : CSSPropertyValue {
     const unsigned short CSS_COLOR         = CSS_VALUE + 0;
     const unsigned short CSS_ACTIVEBORDER  = CSS_VALUE + 1;
     const unsigned short CSS_ACTIVECAPTION = CSS_VALUE + 2; // etc..

     // These will be 0 unless value >= CSS_COLOR
     attribute short          red;
     attribute float          redPercent; // 100% = 255
     attribute short          green;
     attribute float          greenPercent; // 100% = 255
     attribute short          blue;
     attribute float          bluePercent; // 100% = 255
     attribute float          alpha; // float in range 0.0..1.0
     attribute short          hue; // integer in range 0..360
     attribute float          saturation; // percentage
     attribute float          lightness; // percentage
     attribute unsigned long  hex3;
     attribute unsigned long  hex6;

     // If the given values match a named colour, then namedColor will
     // have the appropriate value from the following list. If not, it
     // will have the value UNNAMED.
     const unsigned short UNNAMED = 0;
     const unsigned short BLACK   = 1;
     const unsigned short SILVER  = 2; // etc... (all CSS3 named colours)
     attribute unsigned short namedColor;
   }

// useful, because the string representation would have to be CSS escaped
   interface CSSURLPropertyValue : CSSPropertyValue {
     // The following is only defined if value == CSS_VALUE
     attribute DOMString url;
     attribute DOMString absoluteUrl;
   }

   // this shows how multi-values properties would be done
   interface CSSBackgroundPositionPropertyValue : CSSPropertyValue {
     // the value of x and y are the same as this interface's
     // if this interface's value is less than CSS_VALUE, else
     // they are greater than or equal to CSS_VALUE.
     attribute DOMLengthAndPercentagePropertyValue x;
     attribute DOMLengthAndPercentagePropertyValue y;
   }

   interface CSSLengthPropertyValue : CSSPropertyValue {
     const unsigned short LENGTH_EM = CSS_VALUE + 100;
     const unsigned short LENGTH_EX = CSS_VALUE + 101;
     const unsigned short LENGTH_PX = CSS_VALUE + 102;
     const unsigned short LENGTH_IN = CSS_VALUE + 103;
     const unsigned short LENGTH_CM = CSS_VALUE + 104;
     const unsigned short LENGTH_MM = CSS_VALUE + 105;
     const unsigned short LENGTH_PT = CSS_VALUE + 106;
     const unsigned short LENGTH_PC = CSS_VALUE + 107;
     attribute float length; // length in units specified by value
     // the following set the units on setting, and
     // perform conversions on getting. If conversion is not
     // possible (e.g. relative to absolute without a view)
     // then an exception is thrown.
     attribute float em;
     attribute float ex;
     attribute float px;
     attribute float cm;
     attribute float mm;
     attribute float pt;
     attribute float pc;
   }

   interface CSSLengthAndPercentagePropertyValue : CSSLengthPropertyValue {
     const unsigned short LENGTH_PERCENT = CSS_VALUE + 200;
     attribute float percent;
   }

   interface CSSPercentagePropertyValue : CSSLengthPropertyValue {
     const unsigned short LENGTH_PERCENT = CSS_VALUE + 200;
     attribute float length;
     attribute float percent;
   }


   // The following is for 'content', probably the most complicated
   // CSS2 property.

   interface CSSContentPropertyValue : CSSPropertyValue {
     const unsigned short CSS_NONE = CSS_VALUE + 1;
     // value == CSS_VALUE iff length > 0
     readonly attribute unsigned long length;
     CSSItem item(in unsigned long index);
     // if index == length, acts like appendItem()
     // if index > length, throws an exception
     void setItem(in unsigned long index, in CSSItem value);
     // if index == length, acts like appendItem()
     // if index > length, throws an exception
     void insertItem(in unsigned long index, in CSSItem value);
     // if value != CSS_VALUE, appendItem sets it to CSS_VALUE
     void appendItem(in CSSItem value);
     // if deletion would result in a zero length, set value to CSS_NONE
     void removeItem(in CSSItem value);
     // if deletion would result in a zero length, set value to CSS_NONE
     void deleteItem(in unsigned long index);
     // raises an exception if item is not there
     unsigned long indexOf(in CSSItem value);
     // sets value to CSS_NONE
     void clear();
   }

   interface CSSItem {
     attribute DOMString cssText;
   }

   interface CSSItemKeyword : CSSItem {
     const unsigned short CSS_NONE = 0;
     const unsigned short CSS_NORMAL = 1;
     const unsigned short CSS_OPEN_QUOTE = 100;
     const unsigned short CSS_CLOSE_QUOTE = 101;
     const unsigned short CSS_NO_OPEN_QUOTE = 102;
     const unsigned short CSS_NO_CLOSE_QUOTE = 103;
     attribute unsigned short value;
     CSSItemKeyword create(in unsigned short value);
   }

   interface CSSItemAttrExpression : CSSItem {
     // this could do with just inheriting from AttrExpression instead
     attribute attrExpression attr;
     CSSItemAttrExpression create(in unsigned short value);
   }

   interface CSSItemString : CSSItem {
     attribute DOMString value;
     CSSItemString create(in DOMString value);
   }

   interface CSSItemURL : CSSItem {
     attribute DOMString url;
     attribute DOMString absoluteUrl;
     CSSItemURL create(in DOMString url);
   }

   interface CSSItemCounter : CSSItem {
     attribute DOMString name;
     attribute long increment;
     CSSItemCounter create(in DOMString name, in long increment);
   }

   interface CSSItemCounters : CSSItem {
     attribute DOMString name;
     attribute long increment;
     CSSItemCounters create(in DOMString name, in long increment);
   }


-- Footnotes --
[1] See http://www.damowmow.com/playground/demos/animation/

===


--
Anne van Kesteren
http://annevankesteren.nl/

Reply via email to