What is Element.getBoundingClientRect() function?
getBoundingClientRect() is a method on Element. It returns an object that represents the position of the element in the viewport and the width and height of the element.
For example, to get the size of the body element:
> document.body.getBoundingClientRect()
> DOMRect {
x: 0,
y: 0,
width: 908,
height: 722,
top: 0,
bottom: 722,
left: 0,
right: 0
}
The nice thing about this method is that it returns the position and size after CSS properties and layouts have been applied.
It may seem weird that there is a x and a left property (and a y and top property) that seem to point to the same position. But unlike x and y, left and top can be negative if the element has a negative width or height.
These properties are also read only:
// Has no effect on the position of the element:
element.getBoundingClientRect().left = 10
Using getBoundingClientRect, we can get the center of a element in the viewport:
const pos = element.getBoundingClientRect()
const centerX = pos.left + (pos.width / 2);
const centerY = pos.top + (pos.height / 2);
For styling and layouts, it is way more performant to use CSS. But there are places where CSS does not have a feature. One such feature may be drawing a arrow between two elements, possibly to show a connection between them. Trying to do this in CSS would be impossible as there is no way to set the position based off another elements position. But it is possible with JavaScript and getBoundingClientRect():
function centerOfElement(element) {
const pos = element.getBoundingClientRect();
return [
pos.left + (pos.width / 2),
pos.top + (pos.height / 2)
]
}
function drawArrowBetweenElements(element1, element2) {
// Create svg element
const svgElem = document.createElementNS("http://www.w3.org/2000/svg", "svg");
// Set its position to absolute and to be rendered on top of everything (using "z-index: 9999;")
svgElem.setAttribute("style", "position: absolute; top: 0; left: 0; overflow: visible; z-index: 9999;");
// Add a marker definition (causes the line to end in a chevron/arrow head)
svgElem.innerHTML = `<defs>
<marker id="arrow-head" markerWidth="13" markerHeight="13" refx="2" refy="6" orient="auto">
<path d="M2,2 L2,11 L10,6 L2,2"/>
</marker>
</defs>`;
// Create a line
const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
// Get the centres of the elements
const [center1x, center1y] = centerOfElement(element1);
const [center2x, center2y] = centerOfElement(element2);
line.setAttribute("x1", center1x);
line.setAttribute("y1", center1y);
line.setAttribute("x2", center2x);
line.setAttribute("y2", center2y);
// Set the line to be red and to use the arrow head
line.setAttribute("style", "stroke:red; stroke-width: 1.25px; fill: none; marker-end: url(#arrow-head);");
// Append line to svg, append svg to document body
svgElem.append(line);
document.body.append(svgElem);
}