In a previous post we introduced the GeometryUtils interface and the getBoxQuads()
API for retrieving the CSS box geometry of a DOM node. GeometryUtils also takes care of another important problem: converting coordinates reliably from one DOM node to another. For example, you might want to find the bounding-box of one element relative to another element, or you might want to convert event coordinates from the viewport to some arbitrary element.
Existing APIs
Until now, simple cases could be handled using getBoundingClientRect()
and some math, but complex cases (e.g. involving CSS transforms) were almost impossible to handle using standard APIs. The nonstandard APIs webkitConvertPointToPage
and webkitConvertPageToPoint
are a big improvement, but apart from not being standardized, they’re not as powerful as they need to be. In particular it’s more convenient and more robust to provide an API for directly converting coordinates from one element to another.[1]
New APIs
GeometryUtils
introduces three new methods for coordinate conversion:
to.convertPointFromNode(point, from)
converts a a point relative to the top-left of the first border-box of “from” to a point relative to the top-left of the first border-box of “to”. The point is aDOMPointInit
, which means you can pass aDOMPoint
or a JS object such as{x:0, y:0}
.to.convertRectFromNode(rect, from)
converts a aDOMRect
relative to the top-left of the first border-box of “from” to a DOMQuad relative to the top-left of the first border-box of “to” by converting the vertices of theDOMRect
. It converts to aDOMQuad
to ensure that the result is accurate even if it needs to be rotated or skewed by CSS transforms.to.convertQuadFromNode(quad, from)
converts aDOMQuad
from “from” to “to”. It’s just likeconvertRectFromNode
except for taking aDOMQuad
.
As with getBoxQuads
, a node can be an Element
, TextNode
or Document
; when a Document
is used, the coordinates are relative to the document’s viewport.
Example:
var p1 = document.convertPointFromNode({
x:0, y:0
}, document.getElementById("e")
);
// p1.x == 100, p1.y == 100
var p2 = document.convertPointFromNode({
x:0, y:0
}, document.getElementById("d")
);
// p2.x == 150, p2.y == 150 - 50*sqrt(2) (approx)
p2 = document.getElementById("e").convertPointFromNode({
x:0, y:0
}, document.getElementById("d")
);
// p2.x == 50, p2.y == 50 - 50*sqrt(2) (approx)
var q1 = document.convertRectFromNode(
new DOMRect(0, 0, 50, 50),
document.getElementById("e")
);
// q1.p1.x == 100, q1.p1.y == 100
// q1.p2.x == 150, q1.p2.y == 100
// q1.p3.x == 150, q1.p3.y == 150
// q1.p4.x == 100, q1.p4.y == 150
var q2 = document.convertQuadFromNode(
new DOMQuad({
x:60, y:50
}, {
x:90, y:50
}, {
x:100, y:100
}, {
x:50, y:100
}),
document.getElementById("e")
);
// q2.p1.x == 100, q2.p1.y == 100
// q2.p2.x == 150, q2.p2.y == 100
// q2.p3.x == 140, q2.p3.y == 150
// q2.p4.x == 110, q2.p4.y == 150
Sometimes it’s useful to convert to or from an element’s CSS content-box, padding-box or margin-box. This is supported via an optional ConvertCoordinateOptions
dictionary with the following options:
fromBox
: one of"content"
,"padding"
,"border"
or"margin"
, selecting which CSS box of the first fragment of thefrom
node the input point(s) are relative to.toBox
: selects which CSS box of the first fragment of theto
node the returned point(s) are relative to.
As a special case, this makes it easy to convert points between different
CSS box types of the same element. For example, to convert a point from an
element’s border-box to be relative to its content-box, use
element.convertPointFromNode(point, element, {toBox:"content"})
.
Example:
var p1 = document.convertPointFromNode({
x:0, y:0
}, document.getElementById("e"),
{fromBox:"content"}
);
// p1.x == 120, p1.y == 120
p1 = document.getElementById("e").convertPointFromNode({
x:120, y:120
}, document,
{toBox:"content"}
);
// p1.x == 0, p1.y == 0
p1 = document.getElementById("e").convertPointFromNode({
x:0, y:0
}, document.getElementById("e"),
{fromBox:"content"}
);
// p1.x == 20, p1.y == 20
p1 = document.getElementById("e").convertPointFromNode({
x:20, y:20
}, document.getElementById("e"),
{toBox:"content"}
);
// p1.x == 0, p1.y == 0
These APIs are available in Firefox nightly builds and should be released in Firefox 31. Firefox is the first browser to implement these APIs.
Footnote
[1] Consider the following example:
...<>
...<>
In this case, converting a point relative to a
to be relative to b
by converting first to page coordinates and then back to b
doesn’t work, because the scale(0)
maps every point in a
to a single point in the page.
About
roc
Robert O'Callahan is a distinguished engineer at Mozilla Corporation. Prior to joining MoCo he was a volunteer Mozilla contributor for several years (since 2000).
About
Robert Nyman [Editor emeritus]
Technical Evangelist & Editor of Mozilla Hacks. Gives talks & blogs about HTML5, JavaScript & the Open Web. Robert is a strong believer in HTML5 and the Open Web and has been working since 1999 with Front End development for the web - in Sweden and in New York City.
He regularly also blogs at http://robertnyman.com and loves to travel and meet people.