Testing Environment
Operating System: Mac OS X 10.8.5
Desktop-Browser: Safari 6.1, Chrome 31.0.1650.57, Firefox 25.0.1
Notes
This tutorial was written for pure demonstration purposes. An optimization for mobile devices wasn't
made. Because this is my first tutorial, I will go into detail a bit more. Therefore following tutorials will have a reference
onto my first one, that you can check again more exactly in case of obscurities. I do this, to avoid explaining reiterative sections
again and because in my early days I searched a lot for tutorials, which showed me the solution, but which I not really understood.
Should JavaScript be deactivated, this dock cannot be used in this version. Redress is accomplished by copy
and paste the listed list for mirroring. So, the cloning don't needs to be done by JavaScript. By appending a title
to the icons, its name is being shown near the mouse pointer. The two rows in the JavaScript file regarding the cloning can be deleted in that moment.
This hint is actually still confusing, but after reading the tutorial it should be cleared what I mean with it.
HTML (HyperText Markup Language)
A generall HTML file consists basically of the following parts. First, the document type declaration comes. It defines the used HTML
version. Since HTML5 it has distinctly decreased and as a consequence it contributes to the clearness.
After that the root element <html> follows wherein the head section <head>
is located, with different data to the character set, data for search engines, references to external stylesheets or
JavaScript files, internal style data <style> or else the title of the document <title>,
which appears in the head or tab of of the browser after loading the website. I will dwell on the style data in the head section in more detail
later. But there are a lot other possibilities for such data.
After closing the head section (every HTML element has to be closed again), the body of the document <body> follows,
in which the whole displayed content, like texts with headlines, references, tables, image references and much more, is written.
123456789101112133334 |
<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><meta name="robots" content="noindex, nofollow"><link rel="stylesheet" type="text/css" href="css/style.css"><title>HTML5, CSS3, jQuery – OS X Dock</title><style type="text/css">nav > div:last-child > ul > li img { filter:url(#blur-effect) }</style></head><body><!-- Bereich der darzustellenden Daten --></body></html> |
The <body>
To further promote the semantics, new elements were added in HTML5 (structure semantic as well as text semantic). Here the element <section> simply serves to center the navigation <nav> and also to fix it on the bottom of the screen. The subdivision is made by an unordered list <ul> and its list elements <li>. Embedded into the list elements there are the references to further websites or anchors inside the document itself. They surround on the other hand references to image files. Should an image file not being loaded, there will be shown an alternative text alt="".
1314151617181920212223242526272829303132 |
<!-- Firefox svg blurring --><svg id="svg-image-blur"><filter id="blur-effect"><feGaussianBlur stdDeviation="2"></filter></svg><section id="icon-dock"><nav><div><ul><li><a href=""><img src="img/icon-1.png" alt="Finder"></a></li><li><a href=""><img src="img/icon-2.png" alt="Launchpad"></a></li><li><a href=""><img src="img/icon-3.png" alt="Utilities"></a></li>... <!-- weitere Elemente in der Liste --></ul></div></nav></section><script type="text/javascript" src="js/jquery-1.10.2.min.js"></script><script type="text/javascript" src="js/animation.js"></script> |
The meaning of the section regarding the vector garphic <svg>, I will take up again at a later date.
To obtain a faster loading time of the document and to execute the markup and style data first, the JavaScript part <script>
is prefered to be listed at the end of the document.
The first external script is the JavaScript library jQuery, which works across
a multitude of browsers. It offers quantities of methods for DOM traversal (Document Object Model = platform and language indepenedent interface for
dynamic access to content, structure and layout of HTML documents) and manipulation, event handling, animation and for AJAX (Asynchronous JavaScript And
XML = asynchronous data transfer between browsers and web servers, so that new content can be integragted without full loading of a HTML document).
The second script serves to the implementation of own code for the animations.
CSS (Cascading StyleSheets)
Stylesheets form an extension of HTML elements. First, extended by companies like Netscape und Microsoft the incompatibility increased fast, so that in
1994 a standard was established by the W3C. The latest version of CSS is modular structured. There are subdivisions in 2D and 3D transformations,
animations, transitions, background, text effects and others. A higher version than version 3 shall not be released, because now the single modules
can be updated separately.
Hereafter the stylesheet of the "OS X Dock" will be explained step-by-step.
1234567891011121314151617 |
html, body{width: 100%;height: 100%;margin: 0;padding: 0;background: url(../img/mountainLion.jpg) no-repeat center center;background-size: cover;font-family: Arial, Helvetica, sans-serif;font-size: 16px;font-style: normal;}svgposition: absolute;opacity: 0.0;} |
First, under the point html, body the basic settings of the document will be be made, like definition of width and
height. The background gets an image, which is nonrecurrent and will be centered horizontally as well as vertically. The preference
background-size adjusts the image in its ratio optimally for every possible browser resolution. Furthermore different
preferences will be made regarding the font, which is bequeathed to descendants.
The preferences for svg should just mask the container and position the one independently from other
elements in the document.
13141516171819202122232425262728 |
#icon-dock {position: fixed;width: 100%;bottom: 0;text-align: center;}nav {position: relative;display: inline-block;height: 28px;border-bottom: 32px solid rgba(255, 255, 255, 0.6);border-left: 27px solid transparent;border-right: 27px solid transparent;box-shadow: 0px 5px rgba(255, 255, 255, 0.8);} |
Like mentioned before the element with the ID #icon-dock is adjusted to the maximum width and fixed to the bottom of the
screen width position and bottom, so that it moves along while scrolling. The setting
text-align (horizontal alignment) adjusts inline elements in block elements. Block elements will be adjusted by
margin, whereby the same value of margin-left and margin-right
results in a centering.
HTML elements can be nested into each other, as seen. Inside a body-element there can be p-elements, inside a p-element can be a-elements. Not every
HTML element can be inside every HTML element. Opening and closing tags are not allowed to be crossed. Important for the structuring and the layout
of websites is a clean nesting.
Block elements always size the whole width of the ancestor without a formatting by CSS; the block's height is defined by its content and a new block
always begins in a new row. However, inline elements occupy just that space, which their content needs and doesn't begin in a new row. Furthermore they
aren't allowed to have block elements inside.
The element nav, is, by its behaviour without formatting, a block element, which sizes the whole ancestor's width
and which isn't centered. The change is made by display: inline-block. The element itself becomes an inline element;
the content will be display as block element.
The data for the borders define the look of the shelf. Optionally the border can be entered for all sides or, like here, partly for every side. The first
value is for the border's width, the second one for the style and the last one for the color. The borders left and right can be transformed to an oblique
by the color value transparent. The height of the shelf will be defined by the contained
elements, so the single icons. They normally lie on the bottom border. By the declaration of a height, smaller than the height of the icons, they slide down
onto the border. To enhance the 3D look of the shelf, the element itself gets an additional shadow, with a vertical declaration of 5 pixels and an
arris (data for a soft edge omitted).
29303132333435363738394041424344454647484950 |
nav > div:first-child > p {position: absolute;top: -120px;padding: 3px 10px;background: rgba(0, 0, 0, 0.5);color: #FFF;text-shadow: 0px 1px #000;border-radius: 15px;font-weight: bold;display: none;}nav > div:first-child > p:after {position: absolute;content: "";top: 24px;left: 50%;margin-left: -3px;border-top: 6px solid rgba(255, 255, 255, 0.5);border-left: 6px solid transparent;border-right: 6px solid transparent;} |
To display the icon's name in the dock's style, you need the two upper entries. The text element is positioned absolutely and consequently, it isn't
in a relation with other elements inside the ancestor any longer. It's slid up by a negative value, so that it's still above an activated icon.
The inner distance is set, the background color can be changed independently in its opacity by using rgba.
The value opacity would have resulted in an inheritance. The font is set to white and bold and gets a small text shadow
with an arris.
The second style data works with a pseudo-element. Here, a new visible element is created independently of the DOM, which has no content. It's slid down
directly below the name und gets centered horizontally by left and margin-left. The
concept ofborder is the same like before with nav.
515253545556575859606162636465666768 |
nav > div > ul {margin: 0;padding: 0;list-style: none;}nav > div > ul > li {display: inline;margin-right: -4px;}nav > div > ul > li:last-child {margin-right: 0px;}nav > div > ul > li img {width: 50px;} |
Next, there are the unordered lists ul. They are block elements and arrange themselves among each other. The second list
in the HTML markup isn't visible yet. The first list gets cloned dynamically by JavaScript and the clone is inserted below the first list. More on this later.
To avoid ugly formattings, the inner and outer spacings are set to 0, the bullets are deactivated by list-style: none.
To display the single list elements next to each other (they are block elements and so among each other), display comes
again into action; it's set to inline. Because there's still a small space between the list elements, everyone gets
a negative side clearance to the right – the elements move closer to each other.
The last element looses its side clearance again, so that the shelf's measures to the left and right icon correspond.
After that, the icons get an absolute width, the height is changed automatically by the ratio. The image files have a resolution of 100x100 pixel and each is
shrinked to 50px. While an enlargement, there will be no blur because of that.
69707172737475767778798081828384 |
nav > div:last-child {height: 12px;margin-top: -7px;overflow: hidden;}nav > div:last-child > ul > li img {vertical-align: top;transform: rotateX(180deg);-webkit-transform: rotateX(180deg);filter: blur(2px);-webkit-filter: blur(2px);-ms-filter: blur(2px);-o-filter: blur(2px);opacity: 0.3;} |
The second last point is the cloned section. Because the space to the first list shows a small gap, it's closed by a value of margin-top.
The height also has to be reduced, so that directly after that, the part of the cloned section gets invisible with overflow,
which stands out of the box because of the smaller height.
Therewith, the whole thing looks like a mirroring, the icons of the second list are rotated by a 3D transformation on the x-axis. To use this effect on multiple
browsers, browser prefixes have to be used actually. With time and the induction of such things in different browsers, these prefixes will disappear
step-by-step. The same counts for filter. At this, a light blur is superimposed onto the icons. Despite of the
prefix, Firefox ignores this preference. That's why we manage it with a small trick and use svg filter methods. This is written into the HTML markup
(see section HTML). That this filter performs, it has to be made a declaration in the document regarding the
style. A declaration in the external stylesheet doesn't bring a visible result. The mirror effect gets more
effective, by lowering the opacity.
The preference vertical-align: top holds all icons at the top edge, while moving the mouse over a single icon. By doing
this, the basic layout of the OS X Dock is reached.
Finally, the CSS3 transition have to be build as classes, so that they can be assigned to the right icons by JavaScript/jQuery.
858687888990919293949596979899100101102103104105106107108109 |
.obenNormal {width: 50px;margin-top: 0px;transition: width 0.4s, margin-top 0.4s;-webkit-transition: width 0.4s, margin-top 0.4s;}.oben-0 {width: 100px;margin-top: 0px;transition: width 0.4s, margin-top 0.4s;-webkit-transition: width 0.4s, margin-top 0.4s;}.untenNormal {width: 50px;transition: width 0.4s;-webkit-transition: width 0.4s;}.unten-0 {width: 100px;transition: width 0.4s;-webkit-transition: width 0.4s;} |
Alltogether, 8 classes were created. The classes .obenNormal and .untenNormal return the icons to its initial state. The classes .oben-0 to .oben-2 serve the podium formation (see Picture 2/3). The negative values of margin-top hold the icons on the same baseline at different sizes. Thus, a value of at least the half is necessary. In the classes .unten-0 to .unten-2 it behaves similarly, except that the value margin-top is omitted. The transitions are finally used for the animation. Webkit browsers still need their prefix.
JavaScript/jQuery
In this final part I show you how you use the jQuery library in an easy way, to get the previously built transitions working and that dynamic comes into the dock. Here are the first lines of code.
123973456789101112131415161718192021222324252627 |
$(document).ready(function(){// Bereich für Quellcode});var cloned = $("nav > div").clone(true);$("nav > div").after(cloned);var anzahl = $("nav > div:first-child > ul > li").length;$("nav > div:first-child").append("<p></p>");function daten(){array2d = new Array();array2d[0] = new Array();array2d[1] = new Array();for(var n = 0; n < anzahl; n++){var name = $("nav > div:first-child > ul > li:eq("+n+") > a > img").attr("alt");$("nav > div:first-child > p").html(name);array2d[0][n] = name;var breite = $("nav > div:first-child > p").width() + parseInt($("nav > div:first-child > p").css("padding-left")) * 2;array2d[1][n] = breite;}return array2d;}icon-daten = new Array();icon-daten = daten();abstufung = new Array(100, 86, 60, 50); |
At the beginning the JavaScript code has to get the start sign. This is achieved by the expression in the first line. JavaScript uses the load event
for this sign. It is only triggered when the whole document is loaded (so texts, images, etc.). Most times the script can be triggered after the DOM is loaded.
Exactly at this point ready() from jQuery grips. After that the whole code for the animation follows. First, the whole
div-container is cloned and its clone is inserted directly after the container. This one we need, like mentioned before, for the
mirroring. After that a text element is placed on the end of the container. This is for the name of the icons or the references. So that the name is centered
on top of the current icon, the widths of every name and the name itself are saved in a 2-dimensonal array with the help of a for-loop by the function
daten(). First, you have to create a normal array in which two other arrays (arrays are 0-indexed) are created on the first
and second position. After that you can count over the loop and the count variable n from 0 to the end of the list elements.
You get the number of list elements by .length. The width is calculated from the values of the width of the texts
.width() (value without "px") and the inner left and right spacings from the style data.
parseInt() lets the "px" disappear, which stands on the end of the value, so that you can work with the pure integer value. The
attribute alt which is declared in the HTML document is read and saved on position n in the
first array. Its width accordingly at the same position in the second array. Then the 2-dimensional array is returned and saved in the global array
iconDaten.
And as if these arrays weren't enough, I create another one (abstufung[ ]). This one is for the storage of 4 values. These
4 values state the size of the icons, which are enlarged while a mouseenter event and also they serve the centering of the
icon names. Would you try to read them while the event, you would have to wait till the end of the transition to enable a correct centering and so the display
of the name would be delayed – therefore the array.
2829303132333435363738394041424344454647484950515253545556575859606162636465666768697071 |
$("nav > div:first-child > ul > li > a > img").hover(function(){var i = $(this).parent().parent().index(), a, y;for(a = 0; a <= 3; a++){y = i + a;var oben = $("nav > div:first-child > ul > li:eq("+y+") > a > img"), unten = $("nav > div:last-child > ul > li:eq("+y+") > a > img"),if(y > anzahl) break;if(a < 3){oben.removeClass().addClass("oben-"+a);unten.removeClass().addClass("unten-"+a);}else{oben.removeClass().addClass("obenNormal");unten.removeClass().addClass("untenNormal");}}for(a = 1; a <= 3; a++){y = i - a;var oben = $("nav > div:first-child > ul > li:eq("+y+") > a > img"), unten = $("nav > div:last-child > ul > li:eq("+y+") > a > img"),if(y < 0) break;if(a < 3){oben.removeClass().addClass("oben-"+a);unten.removeClass().addClass("unten-"+a);}else{oben.removeClass().addClass("obenNormal");unten.removeClass().addClass("untenNormal");}} |
After reading and saving the necessary data, you can deal with the actual mouseenter and mouseleave
events. These 2 are combined by the jQuery method .hover(). When you move the mouse pointer over an icon, first, the index is read.
The index stands for the position in the ancestor. Would you read the index of the images it would be 0 everytime (note the 0-index), because there is no
other element inside the ancestor. You find the actual position to each other over the list elements li. To get there,
the method .parent() is called two times. A chaining with the dot operator is possible.
The first for-loop is used for the assignment of the class to the element, which triggers the hover event and to the two elements, which are on its right.
First, all existing classes are taken away from the DOM object and then added a certain class. Should the loop count higher than elements exist, it's
stopped by the condition if(y > anzahl) break;. The second loop refers to the two elements on the left side of the
chosen element. Here, the termination condition behaves upside down; here, the aim is to obtain a break, when the value is lower than 0. The mirroring
is also taken into consideration in both loops.
72737475767778798081828384858687888990919293949596 |
var breite = iconDaten[1][i] / 2;switch(i){case 0:var pos = abstufung[0] / 2 - breite;break;case 1:var pos = abstufung[0] / 2 + abstufung[1] - breite;break;case 2:var pos = abstufung[0] / 2 + abstufung[1] + abstufung[2] - breite;break;case default:var x = i - 2, pos = abstufung[0] / 2 + abstufung[1] + abstufung[2] + x * abstufung[3] - breite;}$("nav > div:first-child > p").html(iconDaten[0][i]).css({"left": pos, "display": "block"});},function(){$("nav > div:first-child > ul > li > a > img").removeClass().addClass("obenNormal");$("nav > div:last-child > ul > li > a > img").removeClass().addClass("untenNormal");$("nav > div:first-child > p").css("display", "none");}); |
That the name is also displayed, the last step in the mouseenter event is processed. In the beginning the width of the respective name is saved in a
variable. Name and icon are on the same vertical line at the moment. With the conditional statement switch there will be
also reacted to the index. Should you be over the first icon with the mouse, it's enough to read and halve the first value of the array
abstufung[ ]. If you move the name with this value to the right, the text element would start on the half of the icon.
Because of that reason the width of the text element is halved as well. When you subtract this value from the middle of the array value, the name moves
a bit to the left and centers itself over the icon. When the mouse is over the second icon, the second value of the array is added to the middel of the first
one and so on. Maybe a short example at this point:
The mouse is over the 6th icon. This means that the first icons in front of it (4 and 5) are also enlarged. The icons 1, 2 and 3 keep their default size.
Because the index of the 6th icon is 7, the default value of the conditional statement is used.
Thus you calculate
50 (6th icon → 100 / 2) + 86 (5th icon) + 60 (4th icon) + 150 (3rd, 2nd, 1st icon → 3 * 50) – half width of the text element = centered position over the 6th icon
Now it should have become a bit clearer.
Important! The space between the icons has to be 0 for the calculation. Furthermore you get a seemless transition with the mouse.
When the calculation is finished, the name is transfered to the text element and also the distance to the left border of the ancestor and the text element is
faded in over the preference display.
The last step in the animation is the mouseleave event, so the second function of .hover(). Here it is responded as soon as
the mouse pointer leaves the icon. The name is faded out, the icons loose the classes for enlargement and recieve the class of the initial state.
Final Remark
I hope you like my first tutorial. It has become quite extensive. But that was my intention. So also beginners, who stumble over this, can learn
several things. In any case, I'm going to change my website in the near future and integrate a blog, with the
possibility to write comments. But this still needs some time. Until then you can contact me over
Facebook or Twitter
or over my contact form.
Try out the demonstration