It is possible to position elements with JavaScript. A simple example of such positioning is a tooltip which follows mouse.
This section describes how to get/calculate coordinates of elements and their position.
Prerequisite: CSS box model
The CSS box model is painted below:
It is described in the CSS Box model specification.
Knowing it’s components is a preliminary knowledge to going any further.
Example document
The example document is at tutorial/browser/dom/metric.html.
Before you continue reading, it would be a good idea to open it.
We’ll use the following box in demonstrations:
<div id="example"> ## Introduction The contents. </div>
The box is positioned absolutely, has borders, paddings, margins, and so scrollbar:
#example { position: absolute; width: 300px; height: 200px; left: 160px; top: 160px; padding: 20px; margin: 20px; overflow: auto; border: 25px solid #F0E68C; }
The CSS picture:
Box metrics
- CSS
width/height
- Size of the content area, which lies inside the padding. CSS properties can be set using
element.style
property and retrieved usinggetComputedStyle()/currentStyle
. Read more in the article Styles and classes, getComputedStyle.
Next we’ll learn more about other times of width and height available in JavaScript.
All JavaScript metrics are in pixels and don’t have 'px'
at the end.
clientWidth/Height
scrollWidth/Height
-
Content area width and height including the scrolled out part.
scrollHeight = 723
- full height with scrollable areascrollWidth = 324
- full width with scrollable area
scrollWidth/Height
is same asclientWidth/Height
, but includes full scrollable area.The following code changes the vertical size of an
element
to show all contents:
element.style.height = element.scrollHeight+'px'
scrollTop/scrollLeft
- Size of scrolled out part: vertical and horizontal. The value is always in pixels.
The picture below illustrates
scrollHeight
andscrollTop
for a vertically scrollable box.scrollLeft/scrollTop are writeableUnlike other properties, which are read-only, you can change
scrollLeft/scrollTop
, and the browser scrolls the element.In standards mode, the scroll of the document is in
document.documentElement
. The following code scrolls the document 10px down: offsetWidth/Height
- Outer box width/height, full size with borders, but without margins.
offsetWidth = 390
- outer box widthoffsetHeight = 290
- outer box height
This is how the box looks from outside.
clientTop/Left
- The indent of client area from box outer corner.
In other words, the width of top/left border in pixels.
clientLeft = 25
- left border widthclientTop = 25
- top border width
There are two exceptions to the general border-width meaning:
-
In case of a right-to-left document (arabic, hebrew), the
clientLeft
property also includes the width of a right scrollbar. - In IE<8 and IE8 compat. mode:
document.documentElement
(ordocument.body
if in quirksmode) is shifted a bit from left-upper corner of the document. There is no border, butdocument.body.clientLeft/clientTop
is not zero (usually 2) in this case.
offsetParent
,offsetLeft/Top
- Properties
offsetLeft
andoffsetTop
reflect a relative shift of an element from itsoffsetParent
.The
offsetParent
is the parent element in the sense of layout. For example, if an element is positioned absolutely, theoffsetParent
is not it’s DOM parent, but a nearest positioned element (orBODY
).The full rule for
offsetParent
:- For static positioning - the nearest table cell or
BODY
(in standards mode). - For other types of positioning - a closest positioned element.
- For static positioning - the nearest table cell or
Size of the client area: content area with paddings, but without scrollbars.
The sizes can be calculated as:
clientWidth = 300(width) + 40(paddings) - 16(scrollbar) = 324 clientHeight = 200(height) + 40(paddings) = 240
If there is no padding, and the box is scrollable, clientWidth/Height show the real content area size:
On the picture above, CSS width
is with the scrollbar. You can’t actually insert something of 300px in the box. The real available width is clientWidth
.
JavaScript coordinates and sizes are set for attached and displayed elements only.
They equal 0
for elements with display:none
or out of DOM. The offsetParent
is also null
for such elements.
We could use this to check if an elem
is hidden:
function isHidden(elem) return !elem.offsetWidth && !elem.offsetHeight }
- Works even if parent element has
display:none
. - Works for all elements except
TR
, on which it bugs in some browsers. But usually we check other elements thanTR
, so it’s ok. - Doesn’t work if the element has
visibility:hidden
or is positioned off-screen. Empty elements will also be hidden.
Practice
There was a green-bordered div
in the text:
A programmer John of your team wrote the code to shift the div
to right-top with position: absolute
:
var div = document.getElementById('moving-div') div.style.position = 'absolute' div.style.right = div.style.top = 0
Naturally, the text after DIV
shifted up:
Enhance the code, make the text keep it’s place even after the DIV
is shifted.
Hint: Create a helper DIV
with the same size as the green-bordered DIV
and insert it in the document. This is called making a placeholder.
Should be like this (placeholder got background for demo purposes):
The source document: tutorial/browser/dom/replaceDiv/2.html.
P.S… Do it without any additional CSS.
What we need is to create a div
with same height and insert it instead of the moving one.
var div = document.getElementById('moving-div') var placeHolder = document.createElement('div') placeHolder.style.height = div.offsetHeight + 'px'
offsetHeight
is outer box height includig borders, but margins are not counted.'px'
is required for CSS property.
But we’re not done yet. The placeHolder
doesn’t have a margin, so the text will shift.
Let’s use JavaScript to copy the margin:
var div = document.getElementById('moving-div') var placeHolder = document.createElement('div') placeHolder.style.height = div.offsetHeight + 'px' // IE || other browser var computedStyle = div.currentStyle || getComputedStyle(div, null) placeHolder.style.marginTop = computedStyle.marginTop // full prop name placeHolder.style.marginBottom = computedStyle.marginBottom
Full property name marginTop
is used. It guarantees that for any combination of margin-top, margin-bottom, margin
, the computed value is correct.
The final result (see SCRIPT
):
<!DOCTYPE HTML> <html> <head> <style> #moving-div { border: 5px groove green; padding: 5px; margin: 10px; background-color: yellow; } </style> </head> <body> Before Before Before <div id="moving-div"> Text Text Text<br> Text Text Text<br> </div> After After After <script> var div = document.getElementById('moving-div') var placeHolder = document.createElement('div') placeHolder.style.height = div.offsetHeight + 'px' var computedStyle = div.currentStyle || getComputedStyle(div, null) placeHolder.style.marginTop = computedStyle.marginTop // full prop name placeHolder.style.marginBottom = computedStyle.marginBottom // highlight it for demo purposes placeHolder.style.backgroundColor = '#C0C0C0' document.body.insertBefore(placeHolder, div) div.style.position = 'absolute' div.style.right = div.style.top = 0 </script> </body> </html>
Place a ball in the center of the field.
Source:
Use JavaScript to place the ball in the center:
The source document: tutorial/browser/dom/ball-source/index.html.
The field has no padding. So, it’s width and height are clientWidth/Height
.
The center is (clientWidth/2, clientHeight/2)
.
If we set ball.style.left/top
of the ball to the center, then the left-upper corner of the ball will be at the center, not the ball itself:
var ball = document.getElementById('ball') var field = document.getElementById('field') ball.style.left = Math.round(field.clientWidth / 2)+'px' ball.style.top = Math.round(field.clientHeight / 2)+'px'
To align the center of the ball against the field center, we need to shift the ball. Half of it’s width left, half of it’s height up.
var ball = document.getElementById('ball') var field = document.getElementById('field') ball.style.left = Math.round(field.clientWidth/2 - ball.offsetWidth/2)+'px' ball.style.top = Math.round(field.clientHeight/2 - ball.offsetHeight/2)+'px'
Unfortunately, there will be a bug, because IMG
has no width/height:
<img src="ball.gif" id="ball">
Width/height of an image is unknown to the browser until it loads, if not set explicitly. So, we’ll have ball.offsetWidth = 0
.
A reasonable fix is to provide width/height:
<img src="ball.gif" width="40" height="40" id="ball">
Now we’re done.
Full solution code: tutorial/browser/dom/ball/index.html
P.S. Using offsetHeight/offsetWidth
instead of clientHeight/clientWidth
would be wrong, because positioning takes place inside the borders.
Summary
There are following properties:
clientWidth/clientHeight
- width/height of the visible in-border area (can be called a client area.
The client area includes padding and doesn’t include scrollbars.clientLeft/clientTop
- left/top border width or, more generally, a shift of the client area from the top-left corner of the box.
Also used in IE, becausedocument.body
may be shifted there.scrollWidth/scrollHeight
- width/height of the scrollable in-border area. Includes padding. Doesn’t include scrollbars.scrollLeft/scrollTop
- the width/height of the scrolled out part of the document, starting from the top-left corner.offsetWidth/offsetHeight
- the “outer” width/height of the box as seen from outside, excluding margins.offsetParent
- the nearest table-cell, body for static positioning or the nearest positioned element for other positioning types.offsetLeft/offsetTop
- the position in pixels of top-left corner of the box related to it’soffsetParent
.
The summarizing picture for all properties except scrolls: