JavaScript/CSS/HTML || SlidePanel.js – How To Create An Animated Sidebar Navigation Menu Panel Using Vanilla JavaScript
The following is a module with functions that demonstrates how to create an animated sliding sidebar navigation ‘hamburger’ menu panel using Vanilla JavaScript.
Using Vanilla JavaScript and CSS, by default the menu panel swipes in and out from left to right. The panel can also be opened from right to left, top to bottom, and bottom to top. All of this behavior, as well as its look and feel can be adjusted via CSS.
Contents
1. Basic Usage
2. Navigation SlidePanel Menu HTML
3. SlidePanel.js & CSS Source
4. More Examples
1. Basic Usage
Syntax is very straightforward. The following demonstrates the JavaScript used to setup the sidebar navigation slide panels and menu event listeners.
Calling this function sets up all of the slide panels and buttons on the page.
1 2 3 4 5 6 7 8 |
<!-- Basic Usage --> <script> document.addEventListener("DOMContentLoaded", function(eventLoaded) { // Setup navigation slide panels and event listeners SlidePanel.init(); }); </script> |
2. Navigation SlidePanel Menu HTML
The following is the HTML used to setup the sidebar navigation slide panel. The placement of the ‘hamburger’ button, as well as the direction that the sidebar menu opens can easily be adjusted via CSS.
Below is a stripped down simple example demonstrating the basic html used to setup a sidebar panel. In this example, the panel is opened from left to right.
Navigation slide panel menus can also open from right to left, top to bottom, and bottom to top. Check out the end of the page for a full example!
Open Left To Right
The following demonstrates how to open the navigation slide panel menu from right to left.
By default, the menu opens on the left side of the page. Adding ‘right‘ to the class list places the menu on the right side of the page.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
<!-- SlidePanel Menu HTML --> <!-- Top Nav --> <nav> <!-- SlidePanel menu 'hamburger' button. -- The property 'data-for' specifies the element id of the slide panel the button should open --> <button class="slide-panel-button open" data-for="nav-menu"></button> <div style="margin: auto;"> <h1>My Programing Notes</h1> </div> </nav> . . . <!-- This is the SlidePanel. The opening behavior of the panel is defined here. -- The property 'data-includeBackground' determines whether a background overlay should be displayed behind the menu panel -- The property 'data-closeOnBackgroundClick' determines whether the menu panel should close when clicking on the background overlay --> <section class="slide-panel" id="nav-menu" data-includeBackground="true" data-closeOnBackgroundClick="true"> <!-- Close button --> <button class="slide-panel-button close"></button> <a href="#">About</a> <a href="#">Services</a> <a href="#">Clients</a> <a href="#">Contact</a> </section> |
Open Right To Left
The following demonstrates how to open the navigation slide panel menu from right to left.
By default, the menu opens on the left side of the page. Adding ‘right‘ to the class list places the menu on the right side of the page.
1 2 3 4 5 6 |
<!-- SlidePanel Menu HTML - Open Right To Left --> <section class="slide-panel right" id="nav-menu"> <!-- Close button --> <button class="slide-panel-button close"></button> </section> |
Open Top To Bottom
The following demonstrates how to open the navigation slide panel menu from top to bottom.
By default, the menu opens on the left side of the page. Adding ‘right‘ to the class list places the menu on the right side of the page.
1 2 3 4 5 6 |
<!-- SlidePanel Menu HTML - Open Top To Bottom --> <section class="slide-panel top" id="nav-menu"> <!-- Close button --> <button class="slide-panel-button close"></button> </section> |
Open Bottom To Top
The following demonstrates how to open the navigation slide panel menu from bottom to top.
By default, the menu opens on the left side of the page. Adding ‘right‘ to the class list places the menu on the right side of the page.
1 2 3 4 5 6 |
<!-- SlidePanel Menu HTML - Open Bottom To Top --> <section class="slide-panel bottom" id="nav-menu"> <!-- Close button --> <button class="slide-panel-button close"></button> </section> |
3. SlidePanel.js & CSS Source
The following is the SlidePanel.js Namespace & CSS Source. Include this in your project to start using!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 |
// ============================================================================ // Author: Kenneth Perkins // Date: Jan 9, 2021 // Taken From: http://programmingnotes.org/ // File: SlidePanel.js // Description: Module that opens/closes a slide panel // Example: // // Open SlidePanel // SlidePanel.open(element) // // // Close SlidePanel // SlidePanel.close(element) // ============================================================================ /** * NAMESPACE: SlidePanel * USE: Handles SlidePanel related functions */ var SlidePanel = SlidePanel || {}; (function(namespace) { 'use strict'; // -- Public data -- // Property to hold public variables and functions let exposed = namespace; // Set class names and other shared data const settings = { // Element class names classNameSlidePanel: '.slide-panel', classNameSlidePanelOpen: '.open', classNameSlidePanelClose: '.close', classNameSlidePanelButton: '.slide-panel-button', classNameSlidePanelBackground: '.slide-panel-background', // Element data names dataNamePanelFor: 'data-for', dataNameCloseOnBackgroundClick: 'data-closeOnBackgroundClick', dataNameIncludeBackground: 'data-includeBackground', cleanClassName: (str) => { return str ? str.trim().replace('.', '') : ''; }, }; exposed.settings = settings; /** * FUNCTION: init * USE: Adds click events for the slide panel menus * @param element: JavaScript element to search for slide panel buttons * @return: N/A */ exposed.init = (element = document) => { // Setup panels let panels = getPanels(element); for (let panel of panels) { // Include background if needed if (shouldIncludeBackground(panel)) { let background = getBackground(panel) || createBackground(panel); if (shouldCloseOnBackgroundClick(panel)) { background.addEventListener('click', backgroundCloseEvent); } } } // Register open button events let openButtons = getOpenButtons(element); for (let openButton of openButtons) { // Get the slide panel panel for the button let panel = getPanel(openButton); // Make sure panel exists if (isNull(panel)) { console.error(`Unable to open: SlidePanel element id '${getPanelFor(openButton)}' does not exist`); continue; } // Add click event openButton.addEventListener('click', openEvent); } // Register close button events let closeButtons = getCloseButtons(element); for (let closeButton of closeButtons) { // Add click event closeButton.addEventListener('click', closeEvent); } } /** * FUNCTION: open * USE: Opens the slide panel * @param panel: JavaScript element of the slide panel * @return: N/A */ exposed.open = (panel) => { let background = getBackground(panel); if (!isNull(background)) { addClass(background, settings.classNameSlidePanelOpen); } addClass(panel, settings.classNameSlidePanelOpen); } /** * FUNCTION: close * USE: Closes the slide panel * @param panel: JavaScript element of the slide panel * @return: N/A */ exposed.close = (panel) => { let background = getBackground(panel); if (!isNull(background)) { let duration = getTransitionDuration(panel); setTimeout(() => { removeClass(background, settings.classNameSlidePanelOpen); }, duration - 30); } removeClass(panel, settings.classNameSlidePanelOpen); } // -- Private data -- let openEvent = (event) => { let panel = getPanel(event.target); exposed.open(panel); } let closeEvent = (event) => { let panel = getPanel(event.target); exposed.close(panel); } let backgroundCloseEvent = (event) => { if (event.target != event.currentTarget) { return; } let panel = getPanel(event.target); exposed.close(panel); } let getPanel = (element) => { let panel = null; // Button if (hasClass(element, settings.classNameSlidePanelButton)) { // Open button if (hasClass(element, settings.classNameSlidePanelOpen)) { let navFor = getPanelFor(element); if (isEmpty(navFor)) { console.error(`'${settings.dataNamePanelFor}' is not specified for a SlidePanel 'open' button`); } else { panel = document.querySelector(`#${navFor}`); } // Close button } else if (hasClass(element, settings.classNameSlidePanelClose)) { panel = element.closest(settings.classNameSlidePanel) } // Background } else if (hasClass(element, settings.classNameSlidePanelBackground)) { panel = element.querySelector(settings.classNameSlidePanel) } return panel; } let getPanelFor = (element) => { return element.getAttribute(settings.dataNamePanelFor); } let getBackground = (panel) => { return panel.closest(settings.classNameSlidePanelBackground); } let getPanels = (element = document) => { return element.querySelectorAll(settings.classNameSlidePanel); } let getOpenButtons = (element = document) => { return element.querySelectorAll(`${settings.classNameSlidePanelButton}${settings.classNameSlidePanelOpen}`); } let getCloseButtons = (element = document) => { return element.querySelectorAll(`${settings.classNameSlidePanelButton}${settings.classNameSlidePanelClose}`); } let shouldCloseOnBackgroundClick = (element) => { let value = element.getAttribute(settings.dataNameCloseOnBackgroundClick); if (isNull(value)) { value = true; } return toBoolean(value); } let shouldIncludeBackground = (element) => { let value = element.getAttribute(settings.dataNameIncludeBackground); if (isNull(value)) { value = true; } return toBoolean(value); } let createBackground = (element) => { let container = document.createElement('div'); let parentNode = element.parentNode; addClass(container, settings.classNameSlidePanelBackground); parentNode.insertBefore(container, element); container.appendChild(element); return container; } let addClass = (element, cssClass) => { cssClass = settings.cleanClassName(cssClass); let modified = false; if (cssClass.length > 0 && !hasClass(element, cssClass)) { element.classList.add(cssClass) modified = true; } return modified; } let removeClass = (element, cssClass) => { cssClass = settings.cleanClassName(cssClass); let modified = false; if (cssClass.length > 0 && hasClass(element, cssClass)) { element.classList.remove(cssClass); modified = true; } return modified; } let hasClass = (element, cssClass) => { cssClass = settings.cleanClassName(cssClass); return element.classList.contains(cssClass); } let toBoolean = (value) => { value = String(value).trim().toLowerCase(); let ret = false; switch (value) { case 'true': case 'yes': case '1': ret = true; break; } return ret; } let isNull = (item) => { return undefined === item || null === item; } let isEmpty = (str) => { return isNull(str) || String(str).trim().length < 1; } let getTransitionDuration = (element) => { let style = window.getComputedStyle(element); let duration = style['transitionDuration'] || style['transition-duration']; // fix miliseconds vs seconds duration = (duration.indexOf('ms') > -1) ? parseFloat(duration) : parseFloat(duration) * 1000; return duration; } (function (factory) { if (typeof define === 'function' && define.amd) { define([], factory); } else if (typeof exports === 'object') { module.exports = factory(); } }(function() { return namespace; })); }(SlidePanel)); // http://programmingnotes.org/ |
The following is SlidePanel.css.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
/* // ============================================================================ // Author: Kenneth Perkins // Date: Jan 9, 2021 // Taken From: http://programmingnotes.org/ // File: SlidePanel.css // Description: CSS for the slide panel // ============================================================================ */ @import url('https://fonts.googleapis.com/css?family=Roboto:400,400italic,500,500italic,700,700italic,900,900italic,300italic,300,100italic,100'); .slide-panel { position: fixed; width: 300px; top: 0; left: 0; transform: translate(-100%, 0%); /* (x, y) */ height: 100%; overflow: auto; transition: transform 220ms ease-in-out; z-index: 1000; background-color: #f3f3f3; background-color: white; box-shadow: 0 0 15px rgba(0,0,0,0.55); font-family: "Roboto",sans-serif, Marmelad,"Lucida Grande",Arial,"Hiragino Sans GB",Georgia,"Helvetica Neue",Helvetica; } .slide-panel.right { right: 0; left: auto; transform: translate(100%, 0%); } .slide-panel.top { top: 0; bottom: auto; transform: translate(0%, -100%); } .slide-panel.bottom { bottom: 0; top: auto; transform: translate(0%, 100%); } .slide-panel.open { transform: translate(0%, 0%); } @media screen and (max-width: 450px) { .slide-panel { width: 80%; } .slide-panel.top, .slide-panel.bottom { width: 100%; } } .slide-panel-background { position: fixed; z-index: 1000; padding-top: 60px; left: 0; top: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); opacity: 0; visibility: hidden; transition: visibility 300ms, opacity 300ms; } .slide-panel-background.open { opacity: 1; visibility: visible; } .slide-panel-button:hover { background-color: #eee; } .slide-panel-button { display: inline-block; cursor: pointer; padding: 10px; background: none; border: none; position: absolute; left: 0; } .slide-panel-button.right { right: 0; left: auto; } .slide-panel-button.open {} .slide-panel-button.close { top: 0; } .slide-panel-button:before { width: 25px; height: 25px; content: ""; display: inline-block; vertical-align: middle; background-size: 100%; background-repeat: no-repeat; } .slide-panel-button.open:before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='35' height='35' viewBox='0 0 24 24' fill='none' stroke='%23000000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='3' y1='12' x2='21' y2='12'%3E%3C/line%3E%3Cline x1='3' y1='6' x2='21' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='18' x2='21' y2='18'%3E%3C/line%3E%3C/svg%3E"); } .slide-panel-button.close:before { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='35' height='35' viewBox='0 0 24 24' fill='none' stroke='%23000000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E"); }/* http://programmingnotes.org/ */ |
4. More Examples
Below are more examples demonstrating the use of ‘SlidePanel.js‘. Don’t forget to include the module when running the examples!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
<!-- // ============================================================================ // Author: Kenneth Perkins // Date: Jan 9, 2021 // Taken From: http://programmingnotes.org/ // File: SlidePanel.html // Description: Demonstrates a simple animated slide panel // ============================================================================ --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>My Programming Notes SlidePanel.js Demo</title> <style> * { margin:0; padding:0; box-sizing:border-box; } .content { margin: 15px; } p { margin-top: 20px; } .nav { display: flex; align-items: center; position: sticky; top: 0; background-color: #e0e0cc; background-color: white; z-index: 1; padding: 8px 0; height: 52px; box-shadow: 0 1px 2px rgba(0,0,0,0.10),0 1px 4px rgba(0,0,0,0.10),0 2px 8px rgba(0,0,0,0.10); font-family: "Roboto",sans-serif, Marmelad,"Lucida Grande",Arial,"Hiragino Sans GB",Georgia,"Helvetica Neue",Helvetica; } .links a { padding: 8px 8px 8px 32px; text-decoration: none; font-size: 25px; color: #818181; display: block; } .links a:hover { color: orangered; } .links { padding-top: 40px; margin: auto; } .menu-header { margin-top: 50px; text-align: center; } </style> <!-- // Include module --> <link type="text/css" rel="stylesheet" href="./SlidePanel.css"> <script type="text/javascript" src="./SlidePanel.js"></script> </head> <body> <main> <nav class="nav"> <!-- SlidePanel menu 'hamburger' button. -- The property 'data-for' specifies the element id of the slide panel the button should open --> <button class="slide-panel-button open" data-for="nav-menu"></button> <button class="slide-panel-button open" data-for="nav-menu-top" style="left: 50px;"></button> <button class="slide-panel-button open right" data-for="nav-menu-right"></button> <button class="slide-panel-button open right" data-for="nav-menu-right-bottom" style="right: 50px;"></button> <div style="margin: auto;"> <h1>My Programing Notes</h1> </div> </nav> <section class="content"> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Placerat duis ultricies lacus sed turpis tincidunt id aliquet. Fringilla urna porttitor rhoncus dolor purus non. Tortor dignissim convallis aenean et. Nulla facilisi nullam vehicula ipsum a arcu cursus vitae. Egestas maecenas pharetra convallis posuere. Nam aliquam sem et tortor consequat. Scelerisque varius morbi enim nunc. In fermentum posuere urna nec. Malesuada fames ac turpis egestas sed. Fringilla ut morbi tincidunt augue interdum velit. Sed augue lacus viverra vitae congue eu consequat. </p> <p> Imperdiet sed euismod nisi porta lorem mollis. Purus sit amet volutpat consequat mauris. Eu nisl nunc mi ipsum faucibus vitae. Platea dictumst quisque sagittis purus sit. Ultrices tincidunt arcu non sodales neque sodales. Nec sagittis aliquam malesuada bibendum arcu vitae elementum curabitur. Vitae aliquet nec ullamcorper sit amet risus. Nisi quis eleifend quam adipiscing vitae proin sagittis nisl rhoncus. Est ullamcorper eget nulla facilisi. Quis vel eros donec ac odio tempor orci dapibus ultrices. Dapibus ultrices in iaculis nunc sed augue lacus viverra. Nec feugiat nisl pretium fusce id velit ut. A cras semper auctor neque vitae tempus. Faucibus scelerisque eleifend donec pretium vulputate sapien nec sagittis. Eget mi proin sed libero enim sed faucibus. <p> Bibendum enim facilisis gravida neque. A scelerisque purus semper eget. Nisl nisi scelerisque eu ultrices vitae auctor. Semper viverra nam libero justo laoreet sit. Nunc congue nisi vitae suscipit tellus mauris a diam maecenas. Auctor eu augue ut lectus arcu bibendum. Adipiscing commodo elit at imperdiet dui accumsan sit amet. Pellentesque habitant morbi tristique senectus et netus. Mauris nunc congue nisi vitae suscipit tellus mauris a diam. Felis donec et odio pellentesque diam. Ornare aenean euismod elementum nisi quis eleifend quam adipiscing. Ullamcorper malesuada proin libero nunc consequat. Viverra mauris in aliquam sem fringilla ut morbi tincidunt. Sodales ut etiam sit amet nisl purus in. Sed faucibus turpis in eu mi bibendum neque egestas congue. Viverra nam libero justo laoreet sit amet. Egestas quis ipsum suspendisse ultrices gravida dictum fusce ut. </p> <p> Felis imperdiet proin fermentum leo. Lacinia at quis risus sed vulputate odio ut enim blandit. Vitae sapien pellentesque habitant morbi. Amet facilisis magna etiam tempor orci eu lobortis. Massa placerat duis ultricies lacus sed turpis tincidunt id aliquet. Duis ut diam quam nulla porttitor massa. In egestas erat imperdiet sed euismod nisi porta lorem mollis. Interdum posuere lorem ipsum dolor sit amet consectetur adipiscing. Nunc sed id semper risus in hendrerit gravida. Ornare arcu dui vivamus arcu felis bibendum ut. Amet porttitor eget dolor morbi non. Vitae justo eget magna fermentum iaculis eu. Nibh tellus molestie nunc non blandit massa enim nec. Sollicitudin nibh sit amet commodo nulla facilisi nullam vehicula ipsum. Proin nibh nisl condimentum id venenatis a condimentum vitae sapien. Tempor id eu nisl nunc mi ipsum faucibus vitae. </p> <p> Et molestie ac feugiat sed lectus vestibulum mattis. Tristique senectus et netus et malesuada fames. Purus in mollis nunc sed id semper. Mauris cursus mattis molestie a iaculis. Auctor elit sed vulputate mi sit amet mauris commodo quis. Vel orci porta non pulvinar neque. Augue neque gravida in fermentum et. Non odio euismod lacinia at quis risus. In hac habitasse platea dictumst vestibulum rhoncus. Sapien et ligula ullamcorper malesuada proin. Sed vulputate mi sit amet mauris commodo. Et pharetra pharetra massa massa ultricies mi quis hendrerit. In nisl nisi scelerisque eu ultrices vitae auctor eu. Luctus accumsan tortor posuere ac ut consequat semper. </p> </section> </main> <!-- This is the SlidePanel. The opening behavior of the panel is defined here. -- The property 'data-includeBackground' determines whether a background overlay should be displayed behind the menu panel -- The property 'data-closeOnBackgroundClick' determines whether the menu panel should close when clicking on the background overlay --> <section class="slide-panel" id="nav-menu" data-includeBackground="true" data-closeOnBackgroundClick="true"> <!-- Close button --> <button class="slide-panel-button close"></button> <div class="menu-header"> Left SlidePanel Menu </div> <div class="links"> <a href="#">About</a> <a href="#">Services</a> <a href="#">Clients</a> <a href="#">Contact</a> </div> </section> <section class="slide-panel top" id="nav-menu-top"> <!-- Close button --> <button class="slide-panel-button close"></button> <div class="menu-header"> Left Top SlidePanel Menu </div> <div class="links"> <a href="#">About</a> <a href="#">Services</a> <a href="#">Clients</a> <a href="#">Contact</a> </div> </section> <section class="slide-panel right" id="nav-menu-right"> <!-- Close button --> <button class="slide-panel-button close"></button> <div class="menu-header"> Right SlidePanel Menu </div> <div class="links"> <a href="#">About</a> <a href="#">Services</a> <a href="#">Clients</a> <a href="#">Contact</a> </div> </section> <section class="slide-panel right bottom" id="nav-menu-right-bottom"> <!-- Close button --> <button class="slide-panel-button close"></button> <div class="menu-header"> Right Bottom SlidePanel Menu </div> <div class="links"> <a href="#">About</a> <a href="#">Services</a> <a href="#">Clients</a> <a href="#">Contact</a> </div> </section> <script> document.addEventListener('DOMContentLoaded', function(eventLoaded) { SlidePanel.init(); }); </script> </body> </html><!-- // http://programmingnotes.org/ --> |
QUICK NOTES:
The highlighted lines are sections of interest to look out for.
The code is heavily commented, so no further insight is necessary. If you have any questions, feel free to leave a comment below.
Leave a Reply