@@ -99,6 +99,13 @@ <h2>Cool</h2>
9999 padding-top : .8em ;
100100 }
101101
102+ /*
103+ The perspective CSS property determines the distance
104+ between the z=0 plane and the user in order to give a
105+ 3D-positioned element some perspective.
106+
107+ https://developer.mozilla.org/en-US/docs/Web/CSS/perspective
108+ */
102109 nav {
103110 position : relative;
104111 perspective : 600px ;
@@ -129,6 +136,15 @@ <h2>Cool</h2>
129136 justify-content : center;
130137 }
131138
139+
140+ /*
141+ If the position property is absolute, the containing block
142+ is formed by the edge of the padding box of the nearest
143+ ancestor element that has a position value other than static
144+ (fixed, absolute, relative, or sticky).
145+
146+ https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_Block
147+ */
132148 .dropdown {
133149 opacity : 0 ;
134150 position : absolute;
@@ -220,6 +236,56 @@ <h2>Cool</h2>
220236</ style >
221237
222238< script >
239+ let triggers = document . querySelectorAll ( '.cool li' ) ;
240+ let background = document . querySelector ( '.dropdownBackground' ) ;
241+ let nav = document . querySelector ( '.top' ) ;
242+
243+ function handleEnter ( ) {
244+
245+ // .trigger-enter sets display to block
246+ this . classList . add ( 'trigger-enter' ) ;
247+
248+ // .trigger-enter-active is added 150 ms later (if still active)
249+ // .tigger-enter-active sets the opacity from 0 to 1
250+ // This two step process allows the browser to animate the transition
251+ // This 2-step is how React & Angular handle animation
252+ setTimeout ( ( ) => {
253+ if ( this . classList . contains ( 'trigger-enter' ) ) {
254+ this . classList . add ( 'trigger-enter-active' ) ;
255+ }
256+ } , 150 ) ;
257+
258+ // Set the backgrounds opacity to 1
259+ background . classList . add ( 'open' ) ;
260+
261+ // Get dropdown for the active trigger (li)
262+ let dropdown = this . querySelector ( '.dropdown' ) ;
263+
264+ // Calculate coordinates for the nav bar and the active dropdown
265+ let navCoords = nav . getBoundingClientRect ( ) ;
266+ let dropdownCoords = dropdown . getBoundingClientRect ( ) ;
267+
268+ // Calculate coordinates for the background
269+ const coords = {
270+ height : dropdownCoords . height ,
271+ width : dropdownCoords . width ,
272+ top : dropdownCoords . top - navCoords . top ,
273+ left : dropdownCoords . left - navCoords . left
274+ }
275+
276+ // Set background styles
277+ background . style . setProperty ( 'width' , `${ coords . width } px` ) ;
278+ background . style . setProperty ( 'height' , `${ coords . height } px` ) ;
279+ background . style . setProperty ( 'transform' , `translate(${ coords . left } px, ${ coords . top } px` ) ;
280+ }
281+
282+ function handleLeave ( ) {
283+ this . classList . remove ( 'trigger-enter' , 'trigger-enter-active' ) ;
284+ background . classList . remove ( 'open' ) ;
285+ }
286+
287+ triggers . forEach ( trigger => trigger . addEventListener ( 'mouseenter' , handleEnter ) ) ;
288+ triggers . forEach ( trigger => trigger . addEventListener ( 'mouseleave' , handleLeave ) ) ;
223289</ script >
224290
225291</ body >
0 commit comments