├── LICENSE ├── README.md ├── slideToggle.min.js ├── slideToggle.js ├── demo-styles.css └── demo.html /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright 2020 Eric Butler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # slideToggle in plain JavaScript 2 | 3 | Plain vanilla JavaScript version of jQuery's `slideToggle()`, `slideUp()`, and `slideDown()` functions. No dependencies! 4 | 5 | Check out [the demo page](https://ericbutler555.github.io/plain-js-slidetoggle/demo.html). 6 | 7 | ## How To Use 8 | 9 | element.slideToggle(duration, callback); // opens and closes an element 10 | 11 | element.slideUp(duration, callback); // closes/collapses an element 12 | 13 | element.slideDown(duration, callback); // opens/expands an element 14 | 15 | - element: *HTMLElement*. Required. 16 | - duration: *Number*. Optional. In milliseconds. Default 400. 17 | - callback: *Function*. Optional. Called once slide animation completes. 18 | 19 | That's it. 20 | 21 | ## Example Usage 22 | 23 | 24 |
blah blah blah
25 | 26 | 27 | 33 | -------------------------------------------------------------------------------- /slideToggle.min.js: -------------------------------------------------------------------------------- 1 | /* plain JS slideToggle https://github.com/ericbutler555/plain-js-slidetoggle */ 2 | function _s(o,i,p,l){void 0===i&&(i=400),void 0===l&&(l=!1),o.style.overflow="hidden",l&&(o.style.display="block");var n,t=window.getComputedStyle(o),s=parseFloat(t.getPropertyValue("height")),a=parseFloat(t.getPropertyValue("padding-top")),r=parseFloat(t.getPropertyValue("padding-bottom")),y=parseFloat(t.getPropertyValue("margin-top")),d=parseFloat(t.getPropertyValue("margin-bottom")),g=s/i,m=a/i,h=r/i,u=y/i,x=d/i;window.requestAnimationFrame(function t(e){void 0===n&&(n=e);e-=n;l?(o.style.height=g*e+"px",o.style.paddingTop=m*e+"px",o.style.paddingBottom=h*e+"px",o.style.marginTop=u*e+"px",o.style.marginBottom=x*e+"px"):(o.style.height=s-g*e+"px",o.style.paddingTop=a-m*e+"px",o.style.paddingBottom=r-h*e+"px",o.style.marginTop=y-u*e+"px",o.style.marginBottom=d-x*e+"px"),i<=e?(o.style.height="",o.style.paddingTop="",o.style.paddingBottom="",o.style.marginTop="",o.style.marginBottom="",o.style.overflow="",l||(o.style.display="none"),"function"==typeof p&&p()):window.requestAnimationFrame(t)})}HTMLElement.prototype.slideToggle=function(t,e){0===this.clientHeight?_s(this,t,e,!0):_s(this,t,e)},HTMLElement.prototype.slideUp=function(t,e){_s(this,t,e)},HTMLElement.prototype.slideDown=function(t,e){_s(this,t,e,!0)}; 3 | -------------------------------------------------------------------------------- /slideToggle.js: -------------------------------------------------------------------------------- 1 | /* plain JS slideToggle https://github.com/ericbutler555/plain-js-slidetoggle */ 2 | 3 | HTMLElement.prototype.slideToggle = function(duration, callback) { 4 | if (this.clientHeight === 0) { 5 | _s(this, duration, callback, true); 6 | } else { 7 | _s(this, duration, callback); 8 | } 9 | }; 10 | 11 | HTMLElement.prototype.slideUp = function(duration, callback) { 12 | _s(this, duration, callback); 13 | }; 14 | 15 | HTMLElement.prototype.slideDown = function (duration, callback) { 16 | _s(this, duration, callback, true); 17 | }; 18 | 19 | function _s(el, duration, callback, isDown) { 20 | 21 | if (typeof duration === 'undefined') duration = 400; 22 | if (typeof isDown === 'undefined') isDown = false; 23 | 24 | el.style.overflow = "hidden"; 25 | if (isDown) el.style.display = "block"; 26 | 27 | var elStyles = window.getComputedStyle(el); 28 | 29 | var elHeight = parseFloat(elStyles.getPropertyValue('height')); 30 | var elPaddingTop = parseFloat(elStyles.getPropertyValue('padding-top')); 31 | var elPaddingBottom = parseFloat(elStyles.getPropertyValue('padding-bottom')); 32 | var elMarginTop = parseFloat(elStyles.getPropertyValue('margin-top')); 33 | var elMarginBottom = parseFloat(elStyles.getPropertyValue('margin-bottom')); 34 | 35 | var stepHeight = elHeight / duration; 36 | var stepPaddingTop = elPaddingTop / duration; 37 | var stepPaddingBottom = elPaddingBottom / duration; 38 | var stepMarginTop = elMarginTop / duration; 39 | var stepMarginBottom = elMarginBottom / duration; 40 | 41 | var start; 42 | 43 | function step(timestamp) { 44 | 45 | if (start === undefined) start = timestamp; 46 | 47 | var elapsed = timestamp - start; 48 | 49 | if (isDown) { 50 | el.style.height = (stepHeight * elapsed) + "px"; 51 | el.style.paddingTop = (stepPaddingTop * elapsed) + "px"; 52 | el.style.paddingBottom = (stepPaddingBottom * elapsed) + "px"; 53 | el.style.marginTop = (stepMarginTop * elapsed) + "px"; 54 | el.style.marginBottom = (stepMarginBottom * elapsed) + "px"; 55 | } else { 56 | el.style.height = elHeight - (stepHeight * elapsed) + "px"; 57 | el.style.paddingTop = elPaddingTop - (stepPaddingTop * elapsed) + "px"; 58 | el.style.paddingBottom = elPaddingBottom - (stepPaddingBottom * elapsed) + "px"; 59 | el.style.marginTop = elMarginTop - (stepMarginTop * elapsed) + "px"; 60 | el.style.marginBottom = elMarginBottom - (stepMarginBottom * elapsed) + "px"; 61 | } 62 | 63 | if (elapsed >= duration) { 64 | el.style.height = ""; 65 | el.style.paddingTop = ""; 66 | el.style.paddingBottom = ""; 67 | el.style.marginTop = ""; 68 | el.style.marginBottom = ""; 69 | el.style.overflow = ""; 70 | if (!isDown) el.style.display = "none"; 71 | if (typeof callback === 'function') callback(); 72 | } else { 73 | window.requestAnimationFrame(step); 74 | } 75 | } 76 | 77 | window.requestAnimationFrame(step); 78 | } 79 | -------------------------------------------------------------------------------- /demo-styles.css: -------------------------------------------------------------------------------- 1 | /* Header */ 2 | header { 3 | position: relative; /* for anchoring .site-navigation */ 4 | display: flex; 5 | justify-content: flex-start; 6 | align-items: center; 7 | padding: 0 0 0 10px; 8 | background: white; 9 | border-bottom: 1px solid #ddd; 10 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); 11 | } 12 | 13 | /* Hamburger icon */ 14 | .hamburger { 15 | display: block; 16 | flex: 0 0 64px; /* keep this exactly 64px wide */ 17 | margin-left: auto; /* keep it flush right */ 18 | width: 64px; 19 | height: 64px; 20 | border-radius: 0; 21 | border: 0; 22 | padding: 0; 23 | background: black; 24 | cursor: pointer; 25 | transition: all 0.2s; 26 | } 27 | .hamburger rect { 28 | fill: white; /* SVGs use "fill", not "background-color" */ 29 | transform-origin: center center; /* for line transforms later */ 30 | transition: all 0.2s; 31 | } 32 | 33 | /* Fancy icon toggle styling */ 34 | .hamburger:hover, 35 | .hamburger.is-active { 36 | background: #eb4; 37 | box-shadow: 0 0 8px rgba(0, 0, 0, 0.3); 38 | } 39 | .is-active #line1 { 40 | transform: rotate(-135deg) translate(-1px, 5px); 41 | fill: black; 42 | } 43 | .is-active #line2 { 44 | transform: rotate(-135deg) translate(-1px, -3px); 45 | fill: black; 46 | } 47 | .is-active #line3 { 48 | transform: rotate(135deg) translate(1px, -11px); 49 | fill: black; 50 | } 51 | 52 | /* Navigation menu */ 53 | .site-navigation { 54 | display: none; /* hides navigation by default until it's toggled open */ 55 | position: absolute; 56 | top: 100%; /* with absolute position, anchor to bottom of header */ 57 | left: 0; 58 | right: 0; 59 | background-color: white; 60 | border-top: 1px solid #ddd; 61 | border-bottom: 1px solid #ddd; 62 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); 63 | } 64 | .site-navigation ul { 65 | display: block; 66 | list-style: none; 67 | margin: 0; 68 | padding: 0; 69 | } 70 | .site-navigation a { 71 | display: block; 72 | padding: 20px; 73 | border-bottom: 1px solid #ddd; 74 | color: inherit; 75 | font-size: 24px; 76 | font-weight: bold; 77 | text-decoration: none; 78 | transition: background-color 0.2s; 79 | } 80 | .site-navigation a:hover, 81 | .site-navigation a:focus { 82 | background-color: #eb4; 83 | border-bottom-color: black; 84 | box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.2); 85 | text-shadow: 0px 1px 1px white; 86 | } 87 | 88 | /* Accordion */ 89 | .accordion { 90 | list-style: none; 91 | margin: 0 auto 2em; 92 | padding: 0; 93 | border-radius: 8px; 94 | border: 1px solid black; 95 | overflow: hidden; 96 | } 97 | .accordion > li:not(:last-child) { 98 | border-bottom: 1px solid black; 99 | } 100 | .tab { 101 | padding: 1em; 102 | background: #eb4; 103 | font-weight: bold; 104 | text-shadow: 0px 1px 1px white; 105 | cursor: pointer; 106 | } 107 | .panel { 108 | display: none; 109 | padding: 1em; 110 | } 111 | .panel.is-open { 112 | display: block; 113 | } 114 | 115 | /* Other unrelated stuff */ 116 | * { 117 | box-sizing: border-box; 118 | } 119 | body { 120 | margin: 0; 121 | font: 18px/1.4 -apple-system, BlinkMacSystemFont, Roboto, sans-serif; 122 | } 123 | .site-name { 124 | padding-left: 1em; 125 | } 126 | h1 { 127 | font-size: 1.6em; 128 | line-height: 1.2; 129 | } 130 | h2 { 131 | display: inline-block; 132 | margin: 1.5em 0 0; 133 | padding: 0.5em 0.75em; 134 | font-size: 1.1em; 135 | background: black; 136 | color: #eb4; 137 | } 138 | p { 139 | padding-left: 1em; 140 | } 141 | code { 142 | padding: 5px; 143 | background: #ddd; 144 | border-radius: 3px; 145 | font-size: 0.875em; 146 | } 147 | main { 148 | max-width: 1000px; 149 | margin: 0 auto; 150 | padding: 20px; 151 | } 152 | footer { 153 | padding: 20px; 154 | background: black; 155 | color: white; 156 | text-align: center; 157 | font-size: 12px; 158 | } 159 | -------------------------------------------------------------------------------- /demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | slideToggle in plain JavaScript 7 | 8 | 9 | 10 |
11 | 14 | slideToggle Demo 15 | 22 | 32 |
33 |
34 | 35 |

slideToggle Demo - no jQuery necessary!

36 |

Plain JavaScript version of the great slideToggle() function in jQuery. Slightly simplified.

37 |

Also includes slideUp() and slideDown().

38 | 39 |

Navigation Menu Demo

40 |

Click/tap the hamburger icon in the upper-right to open and close the navigation menu.

41 | 42 |

Accordion Demo

43 |

Click the different tabs to open a panel with more information. The other tabs will close as you open new ones, but you can adjust your own code if you want them to stay open.

44 | 45 | 62 | 63 |

Like It?

64 |

Head over to the GitHub repo, click the star button and download for your own personal and commercial use.

65 | 66 |
67 | 70 | 71 | 72 | 73 | 74 | 75 | 98 | 99 | 100 | --------------------------------------------------------------------------------