The APIs introduced by this specification provide authors with a way to inspect and manipulate the visual view of a document. This includes getting the position of element layout boxes, obtaining the width of the viewport through script, and also scrolling an element.
This section describes the status of this document at the time of its publication. Other documents may supersede this document. A list of current W3C publications and the latest revision of this technical report can be found in the W3C technical reports index at http://www.w3.org/TR/.
This is the [DATE: 3 August 2002] [LONGSTATUS] of CSSOM View. Please send comments to www-style@w3.org (archived) with [cssom-view] at the start of the subject line.
This document was produced by the CSS Working Group (part of the Style Activity).
This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.
Publication as a Working Draft does not imply endorsement by the W3C Membership. This is a draft document and may be updated, replaced or obsoleted by other documents at any time. It is inappropriate to cite this document as other than work in progress.
Many of the features defined in this specification have been supported by browsers for a long period of time. The goal of this specification is to define these features in such a way that they can be implemented by all browsers in an interoperable manner. The specification also defines a couple of new features that will hopefully be useful to authors. (If they are not you can bug us!)
All diagrams, examples, and notes in this specification are non-normative, as are all sections explicitly marked non-normative. Everything else in this specification is normative.
The key words "MUST", "MUST NOT", "REQUIRED", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in the normative parts of this document are to be interpreted as described in RFC2119. For readability, these words do not appear in all uppercase letters in this specification. RFC2119
Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and terminate these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.
Conformance requirements phrased as algorithms or specific steps may be implemented in any manner, so long as the end result is equivalent. (In particular, the algorithms defined in this specification are intended to be easy to follow, and not intended to be performant.)
User agents may impose implementation-specific limits on otherwise unconstrained inputs, e.g. to prevent denial of service attacks, to guard against running out of memory, or to work around platform-specific limitations.
When a method or an attribute is said to call another method or attribute, the user agent must invoke its internal API for that attribute or method so that e.g. the author can't change the behavior by overriding attributes or methods with custom properties or functions in ECMAScript.
Unless otherwise stated, string comparisons are done in a case-sensitive manner.
A conforming user agent implements all the requirements made by this specification.
The IDL fragments in this specification must be interpreted as required for conforming IDL fragments, as described in the Web IDL specification. WEBIDL
Terminology used in this specification is from DOM, CSSOM and HTML. DOM CSSOM HTML
MouseEvent and MouseEventInit are defined in …
The HTML body element is the first
body
HTML element child of the root
HTML element html.
Content edge, padding edge, border edge, and canvas are defined by CSS.
Viewport and
initial containing block are
defined by CSS 2.1 unless there is an ancestor foreignObject
element in the http://www.w3.org/2000/svg namespace in which
case that element acts as viewport and initial containing block.
The term content refers to the dimensions of the element's content area, including overflown content.
The term document content refers to the area on the canvas that is rendered upon, excluding content on negative axis.
The term CSS layout box refers to the same term in CSS. For the purpose
of the requirements in this specification, elements that have a computed value of the
'display' property that is table-column or
table-column-group must be considered to have an associated CSS
layout box (the column or column group, respectively).
All coordinates and dimensions for the APIs defined in this specification are in CSS pixels.
This does not apply to e.g.
matchMedia() as the units
are explicitly given there.
Window Interfacepartial interface Window { MediaQueryList matchMedia(DOMString query); readonly attribute Screen screen; // viewport readonly attribute double innerWidth; readonly attribute double innerHeight; // viewport scrolling readonly attribute double scrollX; readonly attribute double pageXOffset; readonly attribute double scrollY; readonly attribute double ppageYOffset; void scroll(double x, double y); void scrollTo(double x, double y); void scrollBy(double x, double y); // client readonly attribute double screenX; readonly attribute double screenY; readonly attribute double outerWidth; readonly attribute double outerHeight; };
When the matchMedia(query) method is invoked these steps must be run:
Let parsed media query list be the result of parsing query.
Return a new MediaQueryList object,
associated with the Window object, with
parsed media query list as its associated
media query list.
The screen attribute must return the Screen object
associated with the Window object. It always returns the same
object.
Accessing screen through a WindowProxy object might yield different results
when the Document is navigated.
The innerWidth attribute must return the viewport width including the size of a rendered scroll bar (if any), or zero if there is no
viewport.
The following snippet shows how to obtain the width of the viewport:
var viewportWidth = innerWidth
The innerHeight attribute must return the viewport height including the size of a rendered scroll bar (if any), or zero if there is no
viewport.
The scrollX attribute attribute must return the x-coordinate, relative to the initial containing block origin, of the left of the
viewport, or zero if there is no viewport.
The pageXOffset attribute must return the value returned by the scrollX attribute.
The scrollY attribute attribute must return the y-coordinate, relative to the initial containing block origin, of the top of the
viewport, or zero if there is no viewport.
The pageYOffset attribute must return the value returned by the scrollY attribute.
When the scroll(x, y) method is invoked these steps must be run:
If there is no viewport, abort these steps.
Let x be max(0, min(x, content width - content edge width)).
Let x be min(0, max(x, content edge width - content width)).
Let y be max(0, min(y, document content height - viewport height excluding the size of a rendered scroll bar (if any))).
Align the x-coordinate x of the document content with the left of the viewport and align the y-coordinate y of the document content with the top of the viewport.
If the aligning caused content to move queue a task to
fire an event
named scroll that bubbles at the
Document object, unless a
task to fire that event at the Document object
was already queued.
When the scrollTo(x, y) method is invoked, the user agent must act as if the
scroll() method was invoked with the same arguments.
When the scrollBy(x, y) method is invoked, the user agent must act as if the
scroll() method was invoked with
x plus scrollX
as first argument and y plus
scrollY as second argument.
The screenX attribute must return the x-coordinate,
relative to the origin of the screen of the output device, of the left of
the client window as number of pixels, or zero if there is no such
thing.
The screenY attribute must return the y-coordinate,
relative to the origin of the screen of the output device, of the top of
the client window as number of pixels, or zero if there is no such
thing.
The outerWidth attribute must return the width of the
client window. If there is no client window this
attribute must return zero.
The outerHeight attribute must return the height of the
client window. If there is no client window this
attribute must return zero.
MediaQueryList InterfaceA MediaQueryList object has an associated media query list set on creation and an associated
list of media query list listeners, which is initially empty.
If the associated media query list changes in evaluation then, for each
listener in the list of media query list listeners — in
appending order, queue a task that invokes the listener,
passing as argument the MediaQueryList object.
A simple piece of code that detects changes in the orientation of the viewport can be written as follows:
function handleOrientationChange(mql) {
if(mql.matches) // landscape
…
else
…
}
var mql = matchMedia("(orientation:landscape)")
mql.addListener(handleOrientationChange)
interface MediaQueryList {
readonly attribute DOMString media;
readonly attribute boolean matches;
void addListener(MediaQueryListListener listener);
void removeListener(MediaQueryListListener listener);
};
callback MediaQueryListListener = void (MediaQueryList list);
The media attribute must return the
serialized form of the associated media query list.
The matches attribute must return true if the associated media query list
matches the state of the rendered Document and false if it does not.
When the addListener(listener) method is invoked listener must be
appended to the list of media query list listeners, unless
it is already in the list of media query list listeners.
When the removeListener(listener) method is invoked listener must be
removed from the list of media query list listeners.
Screen InterfaceAs its name suggests, the Screen interface represents information about the screen of the output device.
interface Screen {
readonly attribute double availWidth;
readonly attribute double availHeight;
readonly attribute double width;
readonly attribute double height;
readonly attribute unsigned long colorDepth;
readonly attribute unsigned long pixelDepth;
};
The availWidth attribute must return the available width of the rendering surface of the output device.
The availHeight attribute must return the available height of the rendering surface of the output device.
The width attribute must return the width of the output device.
The height attribute must return the height of the output device.
The colorDepth attribute must return the number of bits allocated to
colors (i.e. excluding the alpha channel) in the output device. If no
bits are allocated to colors in the output device this attribute
must return zero.
The pixelDepth attribute must return the value returned by the colorDepth attributed.
Document Interfacepartial interface Document {
Element? elementFromPoint(double x, double y);
CaretPosition? caretPositionFromPoint(double x, double y);
};
The elementFromPoint(x, y) method must return the element at coordinates x,y in the viewport. The element to be returned is determined
through hit testing. If either argument is negative, x is greater than the viewport width excluding the size of a rendered scroll
bar (if any), or y is greater than the viewport height excluding the size of a rendered scroll bar (if any), the method must
return null. If there is no element at the given position the method must return the root element, if any, or null otherwise. If there is no
viewport associated with the document, the method must return null.
The caretPositionFromPoint(x, y) method must return the result of running these steps:
If there is no viewport associated with the document, return null.
If either argument is negative, x is greater than the viewport width excluding the size of a rendered scroll bar (if any), y is greather than the viewport height excluding the size of a rendered scroll bar (if any) return null.
If at the coordinates x,y in the viewport no text insertion point indicator would have been inserted return null.
If at the coordinates x,y in the viewport a text insertion point indicator would have been inserted in a text entry widget which is also a replaced element return a caret position with its properties set as follows:
The node corresponding to the text entry widget.
The amount of 16-bit units to the left of where the text insertion point indicator would have inserted.
null
Otherwise, return a caret position where the
caret range is a collapsed
Range object for the position
where the text insertion point indicator would have been inserted and
the other properties are set as follows:
The startContainer
of the caret range.
The startOffset of
the caret range.
The specifics of hit testing are out of scope of this
specification and therefore the exact details of
elementFromPoint() and
caretPositionFromPoint()
are therefore too. Hit testing will hopefully be defined in a future
revision of CSS or HTML.
CaretPosition InterfaceA caret position gives the position of a text insertion point indicator. It always has an associated
caret node, caret offset, and caret range. It is represented by a CaretPosition object.
interface CaretPosition {
readonly attribute Node offsetNode;
readonly attribute unsigned long offset;
ClientRect? getClientRect();
};
The offsetNode attribute must return the caret node.
The offset attribute must return the caret offset.
The getClientRect() method must follow these steps, aborting on the first step that
returns a value:
If caret range is not null:
Let list be the result of invoking the
getClientRects() method on the
range.
If list is empty, return null.
Return the ClientRect object in list at index 0.
If caret node is a text entry widget that is a replaced element,
and that is in the document, return a ClientRect object for the caret
in the widget as represented by the caret offset value.
Return null.
Element Interfacepartial interface Element { ClientRectList getClientRects(); ClientRect getBoundingClientRect(); void scrollIntoView(optional boolean top); attribute double scrollTop; attribute double scrollLeft; readonly attribute double scrollWidth; readonly attribute double scrollHeight; readonly attribute double clientTop; readonly attribute double clientLeft; readonly attribute double clientWidth; readonly attribute double clientHeight; };
The getClientRects() method, when invoked, must return the result of the following algorithm:
If the element on which it was invoked does not have an associated
CSS layout box and is not in the http://www.w3.org/2000/svg
namespace return an empty ClientRectList object and stop
this algorithm.
If the element does not have an associated CSS layout box and is
in the http://www.w3.org/2000/svg namespace return a
ClientRectList object containing a single
ClientRect object that describes the bounding box of the
element as defined by the SVG specification.
SVG
Return a ClientRectList object containing a list of
ClientRect objects in content order describing the border
boxes (including those with a height or width of zero) with the
following constraints:
If the element on which the method was invoked has a computed
value for display property of table or
inline-table include both the table box and the caption
box, if any, but not the anonymous container box.
Replace each anonymous block box with its child box(es) and repeat this until no anonymous block boxes are left in the final list.
The getBoundingClientRect() method, when invoked, must return the result of the following algorithm:
Let list be the result of invoking
getClientRects() on the
same element this method was invoked on.
If the list is empty return a ClientRect
object whose top,
right,
bottom and
left members are zero.
Otherwise, return a ClientRect object describing the
smallest rectangle that includes the first rectangle in list
and all of the remaining rectangles of which the height or width is not
zero.
The following snippet gets the dimensions of the first
div element in a document:
var example = document.getElementsByTagName("div")[0].getBoundingClientRect();
var exampleWidth = example.width;
var exampleHeight = example.height;
The scrollIntoView(top) method must run these steps:
If the element does not have any associated CSS layout box terminate these steps.
Scroll the element into view with the align to top flag set if top is true or omitted.
The scrollTop attribute must return the result of running these steps:
If the element does not have any associated CSS layout box or the
element is the root element and the Document is in
quirks mode return zero and terminate these steps.
If the element is the root element return the value of
scrollY.
If the element is the HTML body element,
the Document is in quirks mode, and the element
does not have any overflow, return the value of
scrollY.
Return the y-coordinate of the content at the alignment point with the top of the content edge of the element.
When setting the scrollTop attribute these steps must be run:
Let y be the given value.
If the element does not have any associated CSS layout box, the
element is the root element and the Document is in
quirks mode, or the element has no overflow, terminate these
steps.
If the element is the root element invoke
scroll() with zero as first
argument and y as second.
If the element is the HTML body element,
the Document is in quirks mode, and the element
does not have any vertical overflow, invoke
scroll() with
scrollX as first
argument and y as second.
Scroll the element to
scrollLeft,y.
The scrollLeft attribute must return the result of running these steps:
If the element does not have any associated CSS layout box or the
element is the root element and the Document is in
quirks mode return zero and terminate these steps.
If the element is the root element return the value of
scrollX.
If the element is the HTML body element,
the Document is in quirks mode, and the element
does not have any overflow, return the value of
scrollX.
Return the x-coordinate of the content at the alignment point with the left of the content edge of the element.
When setting the scrollLeft attribute these steps must be run:
Let x be the given value.
If the element does not have any associated CSS layout box, the
element is the root element and the Document is in
quirks mode, or the element has no overflow, terminate these
steps.
If the element is the root element invoke
scroll() with
x as first argument and zero as second.
If the element is the HTML body element,
the Document is in quirks mode, and the element
does not have any vertical overflow, invoke
scroll() with
x as first argument and
scrollY as second.
Scroll the element to
x,scrollTop.
The scrollWidth attribute must return the result of running these steps:
If the element does not have any associated CSS layout box return zero and terminate these steps.
If the element is the root element and the
Document is not in
quirks mode
return max(document content width, value of innerWidth).
If the element is the HTML body element
and the Document is in
quirks mode
return max(document content width, value of innerWidth).
Return the computed value of the 'padding-left'
property, plus the computed value of the 'padding-right',
plus the content width of the element.
The scrollHeight attribute must return the result of running these steps:
If the element does not have any associated CSS layout box return zero and terminate these steps.
If the element is the root element and the
Document is not in
quirks mode
return max(document content height, value of innerHeight).
If the element is the HTML body element
and the Document is in
quirks mode
return max(document content height, value of innerHeight).
Return the computed value of the 'padding-top'
property, plus the computed value of the 'padding-bottom',
plus the content height of the element.
The clientTop attribute must run these steps:
If the element has no associated CSS layout box or if the CSS layout box is inline, return zero.
Return the computed value of the 'border-top-width' property plus the height of any scrollbar rendered between the top padding
edge and the top border edge.
The clientLeft attribute must run these steps:
If the element has no associated CSS layout box or if the CSS layout box is inline, return zero.
Return the computed value of the 'border-left-width' property plus the width of any scrollbar rendered between the left padding
edge and the left border edge.
The clientWidth attribute must run these steps:
If the element has no associated CSS layout box or if the CSS layout box is inline, return zero.
If the element is the root element and the element's document's browsing context is a top-level browsing context, return the viewport width excluding the size of a rendered scroll bar (if any).
Return the width of the padding edge excluding the width of any rendered scrollbar between the padding edge and the border edge.
The clientHeight attribute must run these steps:
If the element has no associated CSS layout box or if the CSS layout box is inline, return zero.
If the element is the root element and the element's document's browsing context is a top-level browsing context, return the viewport height excluding the size of a rendered scroll bar (if any).
Return the height of the padding edge excluding the height of any rendered scrollbar between the padding edge and the border edge.
getClientRects() and
getBoundingClientRect() methodsThe getClientRects() and getBoundingClientRect()
methods provide information about the position of the border box edges of
an element relative to the viewport. The objects these methods return
must be static. That is, changes to the underlying
document are not reflected in the objects.
Element Scrolling MembersTo scroll an element into view, optionally with an align to top flag set, means to run these steps for each ancestor element or viewport that establishes a scrolling box, in order of innermost to outermost scrolling box:
If the Document associated
with the element to be
scrolled into view is not same origin with the
Document associated with the element or
viewport associated with the scrolling box, terminate these
steps.
If the align to top flag is set align the top of the border box of the element to be scrolled into view with the top of the scrolling box.
Otherwise, if the align to top flag is not set align the bottom of the border box of the element to be scrolled into view with the bottom of the scrolling box.
Align the left of the border box of the element to be scrolled into view with the left of the scrolling box.
If the aligning did not cause content to move terminate these steps.
Queue a task to fire an event
named scroll at the element
associated with the scrolling box, unless a task to fire
that event at that element was already
queued.
Queue a task to fire an event
named scroll that bubbles at the
Document object associated with the viewport,
unless a task to fire that event at that
Document object was already
queued.
To scroll an element to x,y means to:
Let x be max(0, min(x, content width - content edge width)).
Let x be min(0, max(x, content edge width - content width)).
Let y be max(0, min(y, content height - content edge height)).
Align content x-coordinate x with the left of the content edge of the element and align content y-coordinate y with the top of the content edge of the element.
If the aligning caused content to move queue a task
to fire an event named
scroll at the element, unless a
task to fire that event at that element was already
queued.
HTMLElement Interfacepartial interface HTMLElement { readonly attribute Element offsetParent; readonly attribute double offsetTop; readonly attribute double offsetLeft; readonly attribute double offsetWidth; readonly attribute double offsetHeight; };
The offsetParent attribute must return the result of running these steps:
If any of the following holds true return null and terminate this algorithm:
body element.position' property is fixed.Return the nearest ancestor element of the element for which at least one of the following is true and terminate this algorithm if such an ancestor is found:
position' property is not static.body element.position' property of
the element is static and the ancestor is one of the
following HTML elements:
td, th, or table.Return null.
The offsetTop attribute must return the result of running these steps:
If the element is the HTML body element
or does not have any associated CSS layout box return zero and terminate
this algorithm.
If the offsetParent of the element is null return the
y-coordinate of the top border edge of the first
CSS layout box associated with the element, relative to the
initial containing block origin, and terminate this
algorithm.
Return the result of subtracting the y-coordinate of the top
padding edge of the first CSS layout box associated with
the offsetParent of the element from the y-coordinate of
the top border edge of the first CSS layout box associated
with the element, relative to the initial containing block
origin.
An inline element that consists of multiple line boxes will only have its first CSS layout box considered.
The offsetLeft attribute must return the result of running these steps:
If the element is the HTML body
element or does not have any associated CSS layout box return zero
and terminate this algorithm.
If the offsetParent of the element is null return the
x-coordinate of the left border edge of the first
CSS layout box associated with the element, relative to the
initial containing block origin, and terminate this
algorithm.
Return the result of subtracting the x-coordinate of the left
padding edge of the first CSS layout box associated with
the offsetParent of the element from the x-coordinate of
the left border edge of the first CSS layout box associated
with the element, relative to the initial containing block
origin.
The offsetWidth attribute must return the result of running these steps:
If the element does not have any associated CSS layout box return zero and terminate this algorithm.
Return the border edge width of the first CSS layout box associated with the element.
The offsetHeight attribute must return the result of running these steps:
If the element does not have any associated CSS layout box return zero and terminate this algorithm.
Return the border edge height of the first CSS layout box associated with the element.
Range InterfaceThe objects the methods described below return must be static.
partial interface Range {
ClientRectList getClientRects();
ClientRect getBoundingClientRect();
};
The getClientRects() method, when invoked, must return an empty
ClientRectList object if the range is not in the document and
otherwise a ClientRectList object containing a list of
ClientRect objects in content order that matches the
following constraints:
getClientRects() on the
element.Text node selected or partially selected by the
range (including when the boundary-points are identical), include a
ClientRect object (for the part that is selected, not the
whole line box). The bounds of these ClientRect objects are
computed using font metrics; thus, for horizontal writing, the vertical
dimension of each box is determined by the font ascent and descent, and
the horizontal dimension by the text advance width.The getBoundingClientRect() method, when invoked, must return the result of the following algorithm:
Let list be the result of invoking
getClientRects() on the
same range this method was invoked on.
If list is empty return a
ClientRect object whose
top,
right,
bottom and
left members are zero.
Otherwise, return a ClientRect object describing the
smallest rectangle that includes the first rectangle in list
and all of the remaining rectangles of which the height or width is not
zero.
MouseEvent InterfaceThe object IDL fragment redefines some members. Can we resolve this somehow?
partial interface MouseEvent { readonly attribute double screenX; readonly attribute double screenY; readonly attribute double pageX; readonly attribute double pageY; readonly attribute double clientX; readonly attribute double clientY; readonly attribute double x; readonly attribute double y; readonly attribute double offsetX; readonly attribute double offsetY; }; partial dictionary MouseEventInit { double screenX = 0; double screenY = 0; double pageX = 0; double pageY = 0; double clientX = 0; double clientY = 0; double x = 0; double y = 0; double offsetX = 0; double offsetY = 0; };
The screenX attribute must return the x-coordinate of
the position where the event occurred relative to the origin of the
screen.
The screenY attribute must return the y-coordinate of
the position where the event occurred relative to the origin of the
screen.
The pageX attribute must return the horizontal coordinate of
the position where the event occurred relative to
the origin of the initial containing block.
The pageY attribute must return the y-coordinate of the
position where the event occurred relative to the origin of the
initial containing block.
The clientX attribute must return the x-coordinate of
the position where the event occurred relative to the origin of the
viewport.
The clientY attribute must return the y-coordinate of
the position where the event occurred relative to the origin of the
viewport.
The x attribute must return the value of clientX.
The y attribute must return the value of clientY.
The offsetX attribute must return the x-coordinate of
the position where the event occurred relative to the origin of the
padding edge of the target node.
The offsetY attribute must return the y-coordinate of
the position where the event occurred relative to the origin of the
padding edge of the target node.
ClientRectList InterfaceThe ClientRectList interface consists of an ordered list of ClientRect objects.
interface ClientRectList {
readonly attribute unsigned long length;
getter ClientRect item(unsigned long index);
};
The length attribute must return the total number of ClientRect objects associated with the object.
The item(index) method, when invoked, must throw an IndexSizeError exception when index is
greater than the number of ClientRect objects associated with the object. Otherwise, the ClientRect object at
index must be returned.
ClientRect InterfaceObjects implementing the ClientRect interface represent a rectangular box. The type of box is specified by the method that returns a ClientRect object.
interface ClientRect {
readonly attribute double top;
readonly attribute double right;
readonly attribute double bottom;
readonly attribute double left;
readonly attribute double width;
readonly attribute double height;
};
The top attribute must return the y-coordinate, relative to the viewport origin, of the top of the rectangle box.
The right attribute must return the x-coordinate, relative to the viewport origin, of the right of the rectangle box.
The bottom attribute must return the y-coordinate, relative to the viewport origin, of the bottom of the rectangle box.
The left attribute must return the x-coordinate, relative to the viewport origin, of the left of the rectangle box.
The width attribute must return the width of the rectangle box.
This is identical to right minus left.
The height attribute must return the height of the rectangle box.
This is identical to bottom minus top.
Whenever a viewport has its width or height changed (e.g. as a result of the user resizing the browser window, or zooming in, or an
iframe element's dimensions are changed), the user agent must queue a task to
fire a trusted event named
resize that does not bubble and is not cancelable at the Window object associated with that
viewport.
Whenever a viewport gets scrolled in response to user interaction, the user agent must queue a task to
fire a trusted event named
scroll that bubbles but is not cancelable at the Document object associated with that viewport.
Do viewports have associated documents?
Whenever an element gets scrolled in response to user interaction, the user agent must queue a task to
fire a trusted event named
scroll that does not bubble and is not cancelable at the element.
Should we specify that scroll events can be throttled (e.g. in a smooth scroll)?
The editors would like to thank Alexey Feldgendler, Björn Höhrmann, Dan Bates, David Vest, Garrett Smith, Hallvord R. M. Steen, Leif Arne Storset, Luiz Agostini, Maciej Stachowiak, Michael Dyck, Mike Wilson, Morten Stenshorne, Peter-Paul Koch, Rachel Kmetz, Robert O'Callahan, Sam Weinig, Scott Johnson, Sylvain Galineau, Tarquin Wilton-Jones, Thomas Moore, and Xiaomei Ji for their contributions to this document.
Special thanks to the Microsoft employees who first implemented many of the features specified in this draft, which were first widely deployed by the Windows Internet Explorer browser.