Tag Archives: css
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.
CSS || How To Zoom & Enlarge An Image On Mouse Hover Using CSS
The following is a module which demonstrates how to zoom and enlarge an image on mouse hover using CSS.
1. Usage
The example below demonstrates the use of the Grow CSS.
1 2 3 |
<!-- // Usage --> <img class="grow" src="https://en.wikipedia.org/static/images/project-logos/enwiki.png" /> |
2. Grow CSS
The following is the Grow css file. 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 |
/* // ============================================================================ // Author: Kenneth Perkins // Date: Dec 23, 2020 // Taken From: http://programmingnotes.org/ // File: Utils.css // Description: General utility Css // ============================================================================ */ .grow:hover { transform: scale(1.1); } .grow { transition: all .2s ease-in-out; } /* http://programmingnotes.org/ */ |
3. More Examples
Below are more examples demonstrating the use of the ‘Grow‘ css. 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 |
<!-- // ============================================================================ // Author: Kenneth Perkins // Date: Dec 23, 2020 // Taken From: http://programmingnotes.org/ // File: demo.html // Description: Demonstrates the use of css styles // ============================================================================ --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>My Programming Notes HTML Demo</title> <style> .main { text-align:center; margin-left:auto; margin-right:auto; } </style> <!-- // Include module --> <link rel="stylesheet" type="text/css" href="./Utils.css"> </head> <body> <div class="main"> <div>My Programming Notes HTML Demo</div> <img class="grow" src="https://en.wikipedia.org/static/images/project-logos/enwiki.png" /> </div> </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.
CSS || How To Create Horizontal Divider Line With Words In The Middle Using CSS
The following is a module which demonstrates how to create a horizontal divider line with words in the middle using CSS.
1. Usage
The example below demonstrates the use of the Divider CSS.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<!-- // Usage --> <h2> This </h2> <div class="divider"> <span class="divider-line"></span> <span class="divider-content"> or </span> <span class="divider-line"></span> </div> <h2> That </h2> |
2. Divider Line CSS
The following is the Divider css file. 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 |
/* // ============================================================================ // Author: Kenneth Perkins // Date: Dec 23, 2020 // Taken From: http://programmingnotes.org/ // File: Utils.css // Description: General utility Css // ============================================================================ */ .divider-line { -webkit-box-flex: 1; -webkit-flex-grow: 1; -ms-flex-positive: 1; flex-grow: 1; -webkit-flex-shrink: 1; -ms-flex-negative: 1; flex-shrink: 1; background-color: #dbdbdb; height: 1px; position: relative; top: .45em; } .divider { -webkit-box-orient: horizontal; -webkit-box-direction: normal; -webkit-flex-direction: row; -ms-flex-direction: row; flex-direction: row; display: flex; } .divider-content { color: #8e8e8e; -webkit-box-flex: 0; -webkit-flex-grow: 0; -ms-flex-positive: 0; flex-grow: 0; -webkit-flex-shrink: 0; -ms-flex-negative: 0; flex-shrink: 0; font-size: 13px; font-weight: 600; line-height: 15px; margin: 0 18px; text-transform: uppercase; } /* http://programmingnotes.org/ */ |
3. More Examples
Below are more examples demonstrating the use of the ‘Divider‘ css. 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 |
<!-- // ============================================================================ // Author: Kenneth Perkins // Date: Dec 23, 2020 // Taken From: http://programmingnotes.org/ // File: demo.html // Description: Demonstrates the use of css styles // ============================================================================ --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>My Programming Notes HTML Demo</title> <style> .main { text-align:center; margin-left:auto; margin-right:auto; } </style> <!-- // Include module --> <link rel="stylesheet" type="text/css" href="./Utils.css"> </head> <body> <div class="main"> <div>My Programming Notes HTML Demo</div> <h2> This </h2> <div class="divider"> <span class="divider-line"></span> <span class="divider-content"> or </span> <span class="divider-line"></span> </div> <h2> That </h2> </div> </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.
JavaScript/CSS/HTML || Simple Tic Tac Toe Game With Minimax AI Using Vanilla JavaScript
The following is a module with functions which demonstrates how to create a simple Tic-Tac-Toe game with computer AI using vanilla JavaScript.
This game allows for two or more players, or computer AI vs computer AI. The first player to match 3 pieces horizontally, vertically and diagonally in a row wins!
1. Features
This game allows for two or more players. To add more players, click the “add new” button under the “players” list. To edit a player, click the “edit” button next to a players name.
• Adding and editing a player allows you to change:
1. Name
2. Avatar image url
3. CPU player & difficulty
• To remove a player, click the “remove” button next to the players name.
• A “save game” button allows to save the progress of the game. A file is generated, which consists of the state of the game. This file can be used to reload the game, using the “load game” button.
• Match history is saved, and lets you see the game stats from the previous completed games.
• Player AI uses the minimax algorithm. It has 4 levels of difficulty:
1. Easy
2. Normal
3. Hard
4. Impossible - You will not win!
2. Screenshots
3. Download & Play
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.
JavaScript/CSS/HTML || Notice.js – Simple Notification Message With Success, Warning & Error Icons Using Vanilla JavaScript
The following is a module with functions that demonstrates how to display a simple notification message with success, warning & error icons.
Using vanilla JavaScript, the message fades in and is displayed to the page, and fades out after a delay (in milliseconds). It’s look and feel can also be adjusted via CSS.
Contents
1. Basic Usage
2. Notice HTML
3. Notice.js & CSS Source
4. More Examples
1. Basic Usage
Syntax is very straightforward. The following demonstrates the JavaScript used to show the notification message. The message fades away after a specified amount of time (in milliseconds).
1 2 3 4 5 6 7 8 9 10 |
<!-- Display Message. --> <div class="notice hide"></div> <script> document.addEventListener("DOMContentLoaded", function(eventLoaded) { // Display Message Notice.display(document.querySelector('.notice'), 'Success', Notice.type.success, 3000); }); </script> |
2. Notice HTML
If you wish to show the messages without using JavaScript, the following is an example of the HTML used to display the notices.
1 2 3 |
<!-- // HTML - Success --> <div class="notice"> Success </div> |
1 2 3 |
<!-- // HTML - Warning --> <div class="notice warning"> Warning </div> |
1 2 3 |
<!-- // HTML - Error --> <div class="notice error"> Error </div> |
3. Notice.js & CSS Source
The following is the Notice.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 |
// ============================================================================ // Author: Kenneth Perkins // Date: Oct 31, 2020 // Taken From: http://programmingnotes.org/ // File: Notice.js // Description: Javascript that displays a Notice to the screen // ============================================================================ /** * NAMESPACE: Notice * USE: Displays a Notice to the screen. */ var Notice = Notice || {}; (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 = { classNameNotice: '.notice', classNameSuccess: '', classNameWarning: '.warning', classNameError: '.error', classNameShow: '.show', classNameHide: '.hide', cleanClassName: function(str) { return str ? str.trim().replace('.', '') : ''; }, }; exposed.settings = settings; // Notice type enum const NoticeType = Object.freeze({success:1, warning:2, error:3}); exposed.type = NoticeType; /** * FUNCTION: display * USE: Displays a message to the page with the given text. * @param text: Text to be displayed to the page. * @param type: The message type. The type determines the style of the notice. * @param duration: The length the notice is displayed to the page (in milliseconds). * Set value to null to display the notice indefinitely. * @return: N/A. */ exposed.display = (element, text, type = Notice.type.success, duration = 3000) => { // Make sure the notice element exists if (!element) { throw new Error('Notice element does not exist'); } let id = getId(element); // Remove any previously existing notice duration delay if (noticeClearTimeout[id]) { clearTimeout(noticeClearTimeout[id]); } // Style the notice depending on the notice type applyStyleType(element, type); // Set the notice text element.innerHTML = text; // Fade in the notice to make it visible fadeIn(element); // Set the notice display duration if (duration) { noticeClearTimeout[id] = setTimeout(() => { // Fade out the notice to make it invisible fadeOut(element); delete noticeClearTimeout[id]; }, duration); } } // -- Private data -- // Holds the notice duration timeout delay let noticeClearTimeout = {}; let applyStyleType = (element, type) => { // Define the css classes for the different notice types let styles = getStyles(); // Remove previous styles for the notice and set the new one for (const style of styles) { if (style.class.length < 1) { continue; } // Check if the class is applied let classExists = hasClass(element, style.class); // Add the selected class if (type && type === style.type) { if (!classExists) { addClass(element, style.class); } } else if (classExists) { // Remove class if exists removeClass(element, style.class); } } } let getId = (element) => { if (!element.id || element.id.trim().length < 1) { element.id = `notice_${randomFromTo(1271991, 7281987)}`; } return element.id; } let randomFromTo = (from, to) => { return Math.floor(Math.random() * (to - from + 1) + from); } let getStyles = () => { let styles = [ {type: NoticeType.success, class: settings.classNameSuccess}, {type: NoticeType.warning, class: settings.classNameWarning}, {type: NoticeType.error, class: settings.classNameError} ]; return styles; } let fadeIn = (element) => { removeClass(element, settings.classNameHide); addClass(element, settings.classNameShow); } let fadeOut = (element) => { removeClass(element, settings.classNameShow); addClass(element, settings.classNameHide); } 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); } (function (factory) { if (typeof define === 'function' && define.amd) { define([], factory); } else if (typeof exports === 'object') { module.exports = factory(); } }(function() { return namespace; })); }(Notice)); // http://programmingnotes.org/ |
The following is Notice.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 |
/* // ============================================================================ // Author: Kenneth Perkins // Date: Oct 31, 2020 // Taken From: http://programmingnotes.org/ // File: Notice.css // Description: CSS for the notifiaction display // ============================================================================ */ .notice { border: 1px solid #d6dadb; border-left: 4.5px solid #32b032; background-color: #f1f5f6; text-align:left; padding: 8px; font-family: helvetica,arial,sans-serif; transition: opacity 400ms ease-in-out; } /* Background url source: http://icons.iconarchive.com/icons/custom-icon-design/flatastic-9/256/Accept-icon.png */ .notice:before { width: 18px; height: 18px; margin-right: 10px; content: ""; display: inline-block; vertical-align: middle; background-image: url('data:image/png;base64,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'); background-size: 100%; background-repeat: no-repeat; transform: translateY(-10%); } .notice.error { border-left-color: red; } /* Background url source: http://icons.iconarchive.com/icons/papirus-team/papirus-status/256/dialog-error-icon.png */ .notice.error:before { background-image: url('data:image/png;base64,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'); } .notice.warning { border-left-color: #ff8000; } /* Background url source: http://icons.iconarchive.com/icons/papirus-team/papirus-status/256/dialog-warning-icon.png */ .notice.warning:before { background-image: url('data:image/png;base64,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'); } .notice.show { opacity: 1; } .notice.hide { opacity: 0; } /* http://programmingnotes.org/ */ |
4. More Examples
Below are more examples demonstrating the use of ‘Notice.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 |
<!-- // ============================================================================ // Author: Kenneth Perkins // Date: Oct 31, 2020 // Taken From: http://programmingnotes.org/ // File: Notification.html // Description: Demonstrates how to display a notification div message to // the page with icons. // ============================================================================ --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>My Programming Notes Notification Display Demo</title> <style> .button { padding: 8px; background-color: #d2d2d2; height:100%; text-align:center; text-decoration:none; color:black; display: flex; justify-content: center; align-items: center; flex-direction: column; border-radius: 15px; cursor: pointer; } .button:hover { background-color:#bdbdbd; } .buttonSection { border-top: 1px solid #a3a3a3; border-bottom: 1px solid #a3a3a3; padding:8px; margin-bottom:10px; text-align:center; margin-top: 10px; } .inline { display:inline-block; } .main { text-align:center; margin-left:auto; margin-right:auto; } </style> <!-- // Include module --> <link type="text/css" rel="stylesheet" href="./Notice.css"> <script type="text/javascript" src="./Notice.js"></script> </head> <body> <div class="main"> <div class="notice-message notice hide"></div> <br /> <div class="notice"> Success </div> <br /> <div class="notice warning"> Warning </div> <br /> <div class="notice error"> Error </div> <br /> <div class="buttonSection"> <div id="btnSuccessMessage" class="inline button"> Success Message </div> <div id="btnWarningMessage" class="inline button"> Warning Message </div> <div id="btnErrorMessage" class="inline button"> Error Message </div> <div style="margin-top: 5px;"> Click a button to see the different message styles! </div> </div> </div> <script> document.addEventListener('DOMContentLoaded', function(eventLoaded) { document.querySelector('#btnSuccessMessage').addEventListener('click', (e) => { Notice.display(document.querySelector('.notice-message'), 'Success!', Notice.type.success); }); document.querySelector('#btnWarningMessage').addEventListener('click', (e) => { Notice.display(document.querySelector('.notice-message'), 'Warning!', Notice.type.warning); }); document.querySelector('#btnErrorMessage').addEventListener('click', (e) => { Notice.display(document.querySelector('.notice-message'), 'Error!', Notice.type.error); }); }); </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.
JavaScript/CSS/HTML || Modal.js – Simple Modal Dialog Prompt Using Vanilla JavaScript
The following is a module which allows for a simple modal popup dialog panel in vanilla JavaScript. This module allows for custom Yes/No prompts.
Options include allowing the modal to be dragged, resized, and closed on outer area click. Modal drag and resize supports touch (mobile) input.
Contents
1. Basic Usage
2. Available Options
3. Alert Dialog
4. Yes/No Dialog
5. Custom Buttons
6. Dialog Instance
7. Modal.js & CSS Source
8. More Examples
1. Basic Usage
Syntax is very straightforward. The following demonstrates adding a simple dialog to the page.
1 2 3 4 5 6 7 8 9 10 11 |
// Add simple dialog. <script> (() => { // Adds a simple dialog to the page Modal.showDialog({ title: 'Save Content?', body: 'Content will be saved. Continue?', }); })(); </script> |
The following example demonstrates how to add click events to the default dialog buttons. The ordering of the buttons on initialization defines its placement on the dialog.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// Add click events. <script> (() => { // Add click events to the buttons Modal.showDialog({ title: 'Save Content?', body: 'Content will be saved. Continue?', buttons: { cancel: { onClick: (event) => { console.log(`${event.target.innerText} Button Clicked`); } }, ok: { onClick: (event) => { console.log(`${event.target.innerText} Button Clicked`); } } } }); })(); </script> |
2. Available Options
The options supplied to the ‘Modal.showDialog‘ function is an object that is made up of the following properties.
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 |
// Available options Modal.showDialog({ title: 'Save Content?', // The title of the dialog body: 'Content will be saved. Continue?', // The content body of the dialog onShow: () => { // Optional. Function that allows to do something before show. Return false to stop showing process console.log(`Dialog is showing`); }, onHide: () => { // Optional. Function that allows to do something before hide. Return false to stop hidding process console.log(`Dialog is hiding`); }, onDestroy: () => { // Optional. Function that allows to do something before destroy. Return false to stop destroying process console.log(`Dialog is destroying`); }, closeOnOuterClick: false, // Optional. Determines if the dialog should close on outer dialog click. Default is False draggable: false, // Optional. Determines if the dialog is draggable. Default is False resizable: false, // Optional. Determines if the dialog is resizable. Default is False autoOpen: true, // Optional. Determines if the dialog should auto open. Default is True destroyOnHide: true, // Optional. Determines if the dialog should be removed from DOM after hiding. Default is True buttons: { // Optional. Allows to add custom buttons to the dialog custom1: { text: 'Custom Button', cssClass: '', visible: true, onClick: (event) => { console.log(`${event.target.innerText} Button Clicked`); } }, // ..... Additional buttons }, }); |
Supplying different options to ‘Modal.showDialog‘ can change its appearance. The following examples below demonstrate this.
3. Alert Dialog
The following example demonstrates an alert dialog.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// Alert dialog. <script> (() => { // Alert Modal.showDialog({ title: 'Content Saved!', body: 'Content has been saved to your account', buttons: { ok: {} } }); })(); </script> |
4. Yes/No Dialog
The following example demonstrates a yes/no dialog.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// Yes/No dialog. <script> (() => { // Yes/No Modal.showDialog({ title: 'Continue?', body: 'Do you want to continue?', buttons: { no: {text: '😢 No'}, yes: {text: '😊 Yes'} } }); })(); </script> |
5. Custom Buttons
The following example demonstrates multiple custom buttons.
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 |
// Custom buttons. <script> (() => { // Custom buttons Modal.showDialog({ title: 'Save Content?', body: 'Content will be saved. Continue?', draggable: true, buttons: { custom1: { text: 'No Way!', cssClass: '', visible: true, onClick: (event) => { console.log(`${event.target.innerText} Button Clicked`); } }, custom2: { text: 'Maybe?', cssClass: '', visible: true, onClick: (event) => { console.log(`${event.target.innerText} Button Clicked`); } }, custom3: { text: 'Sure', cssClass: '', visible: true, onClick: (event) => { console.log(`${event.target.innerText} Button Clicked`); } }, }, }); })(); </script> |
6. Dialog Instance
The following example demonstrates how to use the dialog instance to show and hide when needed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Dialog Instance. <script> (() => { // Create dialog instance and show/hide when needed let dialog = Modal.showDialog({ title: 'Save Content?', body: 'Content will be saved. Continue?', autoOpen: false, }); //dialog.modal // The Javascript modal element dialog.show(); // Shows the modal popup //dialog.hide(); // Hides the modal popup //dialog.destroy(); // Hides the modal popup & removes it from the DOM })(); </script> |
7. Modal.js & CSS Source
8. More Examples
Below are more examples demonstrating the use of ‘Modal.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 |
<!-- // ============================================================================ // Author: Kenneth Perkins // Date: Sept 23, 2020 // Taken From: http://programmingnotes.org/ // File: modalDialog.html // Description: Demonstrates the use of Modal.js // ============================================================================ --> <!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <title>My Programming Notes Modal.js Demo</title> <style> .main { text-align:center; margin-left:auto; margin-right:auto; } .button { padding: 5px; background-color: #d2d2d2; height:100%; text-align:center; text-decoration:none; color:black; display: flex; justify-content: center; align-items: center; flex-direction: column; border-radius: 15px; cursor: pointer; width: 120px; border: none; font-size: 15px; font-family: "Roboto",sans-serif, Marmelad,"Lucida Grande",Arial,"Hiragino Sans GB",Georgia,"Helvetica Neue",Helvetica; } .button:hover { background-color:#bdbdbd; } .inline { display:inline-block; } </style> <!-- // Include module --> <link type="text/css" rel="stylesheet" href="./Modal.css"> <script type="text/javascript" src="./Modal.js"></script> </head> <body> <div class="main"> My Programming Notes Modal.js Demo <div style="margin-top: 20px;"> <div id="btnShow" class="inline button"> Show </div> </div> </div> <script> document.addEventListener("DOMContentLoaded", function(eventLoaded) { // Custom buttons let dialog = Modal.showDialog({ title: 'Save Content?', // The title of the dialog body: 'Content will be saved. Continue?', // The content body of the dialog onShow: () => { // Optional. Function that allows to do something before show. Return false to stop showing process console.log(`Dialog ${dialog.modal.id} is showing`); }, onHide: () => { // Optional. Function that allows to do something before hide. Return false to stop hidding process console.log(`Dialog ${dialog.modal.id} is hiding`); }, onDestroy: () => { // Optional. Function that allows to do something before destroy. Return false to stop destroying process console.log(`Dialog ${dialog.modal.id} is destroying`); }, closeOnOuterClick: false, // Optional. Determines if the dialog should close on outer dialog click. Default is False draggable: true, // Optional. Determines if the dialog is draggable. Default is False resizable: false, // Optional. Determines if the dialog is resizable. Default is False autoOpen: false, // Optional. Determines if the dialog should auto open. Default is True destroyOnHide: false, // Optional. Determines if the dialog should be removed from DOM after hiding. Default is True buttons: { // Optional. Allows to add custom buttons to the dialog custom1: { text: 'No Way!', cssClass: '', visible: true, onClick: (event) => { console.log(`${event.target.innerText} Button Clicked`); } }, custom2: { text: 'Maybe?', cssClass: '', visible: true, onClick: (event) => { console.log(`${event.target.innerText} Button Clicked`); } }, custom3: { text: 'Sure', cssClass: '', visible: true, onClick: (event) => { console.log(`${event.target.innerText} Button Clicked`); } }, }, }); setTimeout(() => { //dialog.modal // The Javascript modal element dialog.show(); // Shows the modal popup setTimeout(() => { dialog.hide(); // Hides the modal popup //dialog.destroy(); // Hides the modal popup & removes it from the DOM }, 2000); }, 200); // Show the modal document.querySelector('#btnShow').addEventListener('click', (event) => { dialog.show() }); }); </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.
JavaScript/CSS/HTML || Placeholder.js – Simple Animated Floating Label For Textbox, Dropdown & Textarea Using Vanilla JavaScript
The following is a module which allows for a simple animated floating placeholder label for textbox, dropdown & textarea using vanilla javascript.
No special HTML markup is required.
Contents
1. Basic Usage
2. Placeholder HTML
3. Placeholder.js & CSS Source
4. More Examples
1. Basic Usage
Syntax is very straightforward. The following demonstrates adding and removing floating placeholders to elements.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// Add & Remove Floating Placeholders. <script> (() => { // Add floating placeholder. It accepts either a string selector // or one or more Javascript elements. Placeholder.float('input:not([type="radio"]):not([type="checkbox"]), select, textarea'); // Remove floating placeholder. It accepts either a string selector // or one or more Javascript elements. Placeholder.remove(document.querySelectorAll('input:not([type="radio"]):not([type="checkbox"]), select, textarea')); })(); </script> |
‘Placeholder.float‘ and ‘Placeholder.remove‘ accepts either a string selector or one or more Javascript elements.
2. Placeholder HTML
The following is sample HTML used for the floating placeholder label. No special HTML markup is required. Simply supply an element with a ‘placeholder‘ attribute, and you’re all set!
1 2 3 4 5 6 7 8 9 10 11 12 |
<!-- // Sample HTML --> <input type="text" placeholder="Name" /> <input type="email" placeholder="Email" /> <input type="password" placeholder="Password" /> <select name="gender" id="gender" placeholder="Gender" > <option value="" selected></option> <option value="male">Male</option> <option value="female">Female</option> <option value="na">Prefer Not To Say</option> </select> <textarea rows="4" cols="50" placeholder="Describe Yourself"></textarea> |
3. Placeholder.js & CSS Source
The following is the Placeholder.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 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 |
// ============================================================================ // Author: Kenneth Perkins // Date: Sept 9, 2020 // Taken From: http://programmingnotes.org/ // File: Placeholder.js // Description: Module that adds a floating placeholder label to an element // Example: // // Add Placeholder // Placeholder.float('selector') // // // Remove Placeholder // Placeholder.remove('selector') // ============================================================================ /** * NAMESPACE: Placeholder * USE: Handles Placeholder related functions */ var Placeholder = Placeholder || {}; (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 = { attributePlaceholder: 'placeholder', // Element class names classNameFloatingItem: '.floating-placeholder', classNameContainer: '.floating-placeholder-container', classNamePlaceholder: '.floating-placeholder-label', classNameActive: '.active', // Element data names dataNamePlaceholderCache: 'data-placeholder-cache', cleanClassName: (str) => { return str ? str.trim().replace('.', '') : ''; }, }; exposed.settings = settings; /** * FUNCTION: float * USE: Adds a floating placeholder to the selected elements. * @param element: One or more JavaScript element to add floating placeholder to. * @return: N/A. */ exposed.float = (element) => { let items = verifyElements(element); for (const item of items) { addPlaceholder(item); } } /** * FUNCTION: remove * USE: Removes a floating placeholder from the selected elements. * @param element: One or more JavaScript element to remove floating placeholder from. * @return: N/A. */ exposed.remove = (element) => { let items = verifyElements(element); for (const item of items) { removePlaceholder(item); } } // -- Private data -- let removePlaceholder = (element) => { let container = getContainer(element); let parentNode = container.parentNode; removeClass(element, settings.classNameFloatingItem); let placeholderText = getPlaceholderTextCache(element); setPlaceholderText(element, placeholderText); setPlaceholderTextCache(element, null); getEvents().forEach(eventInfo => { eventInfo.names.forEach(eventName => { element.removeEventListener(eventName, eventInfo.callback); }); }); parentNode.insertBefore(element, container); parentNode.removeChild(container); } let addPlaceholder = (element) => { addClass(element, settings.classNameFloatingItem); let placeholderText = verifyPlaceholderText(element); let container = getContainer(element) || createContainer(element); let placeholder = getPlaceholder(element) || createPlaceholder(container); placeholder.innerHTML = placeholderText; placeholder.htmlFor = element.id; getEvents().forEach(eventInfo => { registerEvents(element, eventInfo.names, eventInfo.callback); }); if (!isEmpty(element.value)) { floatPlaceholder(element); } else { resetPlaceholder(element); } setDetectChangeHandler(element); } let setDetectChangeHandler = (element, property = 'value') => { let elementPrototype = Object.getPrototypeOf(element); if (!elementPrototype.hasOwnProperty(property)) { return; } let descriptor = Object.getOwnPropertyDescriptor(elementPrototype, property); let newProps = { get: function () { return descriptor.get.apply(element, arguments); }, set: function (t) { setTimeout( () => { element.dispatchEvent(new Event(property + 'change')); }, 10); return descriptor.set.apply(element, arguments); } }; Object.defineProperty(element, property, newProps); } let floatPlaceholder = (element) => { let placeholder = getPlaceholder(element); if (!isNull(placeholder)) { addClass(placeholder, settings.classNameActive); } } let resetPlaceholder = (element) => { let placeholder = getPlaceholder(element); if (!isNull(placeholder)) { if (isEmpty(element.value)) { removeClass(placeholder, settings.classNameActive); } } } let getContainer = (element) => { return element.closest(settings.classNameContainer); } let getPlaceholder = (element) => { let container = getContainer(element); return !isNull(container) ? container.querySelector(settings.classNamePlaceholder) : null; } let createPlaceholder = (container) => { let placeholder = document.createElement('label'); addClass(placeholder, settings.classNamePlaceholder); container.appendChild(placeholder); return placeholder; } let createContainer = (element) => { let container = document.createElement('div'); let parentNode = element.parentNode; addClass(container, settings.classNameContainer); parentNode.insertBefore(container, element); container.appendChild(element); return container; } let verifyPlaceholderText = (element) => { if (isEmpty(element.id)) { element.id = `floating_placeholder_${randomFromTo(1271991, 7281987)}`; } let placeholderText = getPlaceholderText(element); if (!isEmpty(placeholderText)) { setPlaceholderTextCache(element, placeholderText); } setPlaceholderText(element, null); return getPlaceholderTextCache(element); } let setPlaceholderText = (element, value) => { addData(element, {key: settings.attributePlaceholder, value: value}); } let getPlaceholderText = (element) => { let value = getData(element, settings.attributePlaceholder); return value; } let setPlaceholderTextCache = (element, value) => { addData(element, {key: settings.dataNamePlaceholderCache, value: value}); } let getPlaceholderTextCache = (element) => { let value = getData(element, settings.dataNamePlaceholderCache); return value; } let floatPlaceholderEvent = (event) => { let element = event.currentTarget; let nodeName = element.nodeName.toLowerCase(); switch (event.type) { case 'focus': if (nodeName === 'select') { return; } break; case 'keyup': if (nodeName === 'select') { if (isEmpty(element.value)) { return; } } else { return; } break; } floatPlaceholder(element); } let resetPlaceholderEvent = (event) => { let element = event.currentTarget; let nodeName = element.nodeName.toLowerCase(); switch (event.type) { case 'keyup': if (nodeName === 'select') { if (!isEmpty(element.value)) { return; } } else { return; } break; } resetPlaceholder(element); } let registerEvents = (element, eventNames, func) => { eventNames.forEach((eventName, index) => { element.removeEventListener(eventName, func); element.addEventListener(eventName, func); }); } 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 isNull = (item) => { return undefined === item || null === item; } let isEmpty = (str) => { return isNull(str) || String(str).trim().length < 1; } let randomFromTo = (from, to) => { return Math.floor(Math.random() * (to - from + 1) + from); } let addData = (element, data) => { if (isNull(data)) { return; } else if (!Array.isArray(data)) { data = [data]; } data.forEach(item => { if (!isNull(item.value)) { element.setAttribute(item.key, item.value); } else { removeData(element, item); } }); } let removeData = (element, data) => { if (isNull(data)) { return; } else if (!Array.isArray(data)) { data = [data]; } data.forEach(item => { let key = item.key || item; element.removeAttribute(key); }); } let getData = (element, data) => { if (isNull(data)) { return null; } else if (!Array.isArray(data)) { data = [data]; } let results = []; data.forEach(item => { let key = item.key || item; results.push(element.getAttribute(key)); }); return results.length == 1 ? results[0] : results; } |