Introduction
I have been developing websites since 2010. One reoccurring problem I faced on almost every project was centering some HTML element, either relative to the window or to some other element. This is not such a difficult concept so for each website I managed to implement an effective solution. Nonetheless, I realized that each time I am faced with this problem, I waste time re-coding the solution. I never spent time making a solution I could reuse throughout my websites. So today, I have decided to make a solution that can be reused.
The Problem
The first step in making a reusable solution is to properly define the problem and to outline all the requirements. Now the problem may seem relatively simple at first: we must center one HTML element inside or over another, but it is not. For example, suppose we implement a solution that centers an HTML element over another. Then something happens - either the window is resized or the later element is repositioned. Then our HTML element will no longer be centered and our solution has accomplished nothing.
So what is our real problem? First of all, we must position any visible HTML element (from now on called elm2
) inside or over another (from now on called elm1
) so that the following conditions are met:
elm2
appears to be inside or over elm1
, but in markup elm2
is not necessarily a child of elm1
. elm2
is positioned so that the space inside elm1
and outside elm2
is equally spaced horizontally and vertically.
Furthermore, we must maintain this position throughout the lifetime of the page. Lastly, we should be able to center more than one HTML element on any one page.
Requirements
From this more substantial problem definition, I developed the following requirements:
-
The solution should take two given elements (called elm1
and elm2
) and position elm2
so that:
- The space between the left borders of
elm1
and elm2
is equal to the space between the right borders of elm1
and elm2
, - and so that the space between the top borders of
elm1
and elm2
is equal to the space between the bottom borders of elm1
and elm2
.
When conditions a
and b
are true
for any element, it will be referred to as “centered”.
- Handle the
onresize
event of the window, and reposition any elements that must be centered so that conditions a
and b
are true
for such an element. - The solution should provide an interface so that users can specify which elements they want to be centered and over which elements.
- The solution should provide an interface so that users can re-center any elements if they resized or repositioned an element that would cause a centered element to no longer be centered.
- The solution should be able to center up to 20 elements and maintain centering for all of them.
- The solution should consider the window to also be an element so that other HTML elements can be centered inside it.
Implementation
Now it is possible to code a solution using only HTML and CSS. In such a case, the solution will be very efficient in terms of loading time of the webpage. Nonetheless, such a solution is not reusable. That is because a solution using only CSS requires that elm1
and elm2
have statically defined dimensions. So when you move the solution to a new page to center a different elm2
with different dimensions, you will have to change the CSS rules. A true reusable solution must be defined so that it does not have to be changed when implemented on a different web page.
The only solution that offers such reusability is one that uses JavaScript. However, such a solution does have a slight performance drawback because the code will have to be executed after the page loads; thus, extending the loading time of the page (but only by milliseconds).
The solution should keep track of up to twenty elements. It should know which elements must be centered over which elements so that it can re-center them when the window is resized. This requires the use of an array. But rather than placing the array in the global scope along with all the functions needed for the solution, I decided to create an object that will contain the array as a private
member. This object will then have only two public
members to implement the interfaces defined in points 3 and 4 of the requirements.
(function () {
function Utility() {
code elements = new Array(0);
}
util = new Utility();
}());
As can be seen from the code, util
(short for utility) is a onetime only object because it is not necessary to have more than one instance.
The next step is to define the function that will do all the hard work.
function centerElements(index) {
for (code l_1 = index?index:0; l_1 < elements.length; l_1++) {
code anchor;
if (elements[l_1].elm1.offsetLeft) {
elements[l_1].elm2.style.position = "absolute";
anchor = { left: elements[l_1].elm1.offsetLeft, top: elements[l_1].elm1.offsetTop };
}
else {
elements[l_1].elm2.style.position = "fixed";
anchor = { left: 0, top: 0 };
}
elements[l_1].elm2.style.marginTop = "0px";
elements[l_1].elm2.style.marginLeft = "0px";
code elm1Size = getSize(elements[l_1].elm1), elm2Size = getSize(elements[l_1].elm2);
anchor.left += (elm1Size.width - elm2Size.width) / 2;
anchor.top += (elm1Size.height - elm2Size.height) / 2;
elements[l_1].elm2.style.left = anchor.left + "px";
elements[l_1].elm2.style.top = anchor.top + "px";
if (index) { break;}
}
}
First of all, notice that the function takes an argument index
. It uses this index
to center only the specified element in the array. But if the index
is not specified, then it loops through the array of elements and centers all of them.
The first step to center an element is to specify its position. This is the first required step otherwise inline elements like the anchor does not have width or height. Notice that when elm1
is the window object then elm2
will be positioned “fixed” and if elm1
is an element then elm2
will be positioned “absolute”. This is so that when the user scrolls, elm2
will remain centered inside the window.
At this point, it is important to note that the code uses the top and left style rules of elm2
to position it. The first step in calculating the pixel values for the top and left style rule of elm2
is to find the top and left position of elm1
. This is given by the offsetTop
and offsetLeft
properties of elm1
, or in the case of the window object, they are both zero. The two values are combined into an object and assigned to the anchor
variable.
The next step is to obtain the widths and heights of elm1
and elm2
, these are assigned to elm1Size
and elm2Size
respectively. The widths and heights are obtained by calling getSize
. This function is useful in more than just the util
object so it is defined in the global scope.
Finally, after the sizes have been acquired, it is time for some math. To calculate the top position of elm2
, the height of elm2
is subtracted from that of elm1
and divided into two. The result is then added to the top position of elm1
. The same steps are taken to calculate the left position but this time using the widths instead of the heights.
After all that, the top and left style rules of elm2
are finally assigned.
Once the backbone of the solution has been implemented, the interface methods can be defined.
this.center = function (elm1, elm2) {
code index = elements.length;
elements[index] = { elm1: elm1, elm2: elm2 };
centerElements(index);
return index;
}
this.reCenter = function (index) { centerElements(index); }
The center
method takes references for elm1
and elm2
, and adds them to the array. It then calls the centerElements
method and returns the index of the elements in the array. The reason it returns the index is so that the user can later on call reCenter
and specify which element should be re-centered.
The last thing needed is to handle the onresize
event of the window.
window.addEventListener("resize", function () { centerElements(); }, false);
Note that when the event is raised, no index is passed to centerElements
so it will re-center all elements.
Use
To center an element now is very easy.
var elm1 = document.getElementById("elm1");
var elm2 = document.getElementById("elm2");
util.center(elm1,elm2);
util.reCenter();
First get references to both elements, then call the center method of the util
object. If any changes are made in the size or position of either element, simply call reCenter
to maintain the centeredness of the elements.