├── .editorconfig ├── .gitignore ├── .htmlhintrc ├── LICENSE ├── README.md ├── bower.json ├── demo.gif ├── dist ├── example.css ├── example.js ├── example.min.css ├── example.min.js ├── jquery-stack-menu.css ├── jquery-stack-menu.js ├── jquery-stack-menu.min.css └── jquery-stack-menu.min.js ├── docs ├── example.min.css ├── example.min.js ├── index.html ├── jquery-stack-menu.min.css └── jquery-stack-menu.min.js ├── gulpfile.js ├── package.json └── src ├── coffee ├── example.coffee └── jquery-stack-menu.coffee ├── pug └── index.pug └── stylus ├── example.styl └── jquery-stack-menu.styl /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = tab 6 | indent_size = 2 7 | end_of_line = lf 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | bower_components/ 4 | node_modules/ 5 | src/js/app.js 6 | dist/*.html 7 | dist/templates/site/assets/scripts 8 | dist/templates/site/assets/json 9 | dist/templates/site/assets/styles 10 | dist/templates/site/assets/templates 11 | -------------------------------------------------------------------------------- /.htmlhintrc: -------------------------------------------------------------------------------- 1 | { 2 | "tagname-lowercase": true, 3 | "attr-lowercase": true, 4 | "attr-value-double-quotes": true, 5 | "doctype-first": true, 6 | "tag-pair": true, 7 | "spec-char-escape": true, 8 | "id-unique": true, 9 | "src-not-empty": true, 10 | "attr-no-duplication": true, 11 | "title-require": true, 12 | "alt-require": true, 13 | "doctype-html5": true 14 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Max Zhurkin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jQuery Stack Menu 2 | 3 | The plugin make simply navigation menu from unordered list 4 | 5 | ![](demo.gif) 6 | 7 | [VIEW DEMO](https://maximzhurkin.github.io/jquery-stack-menu/) 8 | 9 | ## Getting Started 10 | ### 1. Include [jQuery](https://jquery.com/) and stackMenu 11 | ```html 12 | 13 | 14 | 15 | 16 | 17 | ``` 18 | ### 2. Add HTML 19 | ```html 20 | 35 | ``` 36 | ### 3. Call plugin 37 | ```javascript 38 | $(document).ready(function() { 39 | $('#stack-menu').stackMenu(); 40 | }); 41 | ``` 42 | ## Options 43 | ```javascript 44 | $('#stack-menu').stackMenu({ 45 | all: false, // add links to parents 46 | allTitle: 'All' // parents links text 47 | }); 48 | ``` 49 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-slide-menu", 3 | "description": "jQuery slide menu plugin", 4 | "main": "index.js", 5 | "authors": [ 6 | "Max Zhurkin" 7 | ], 8 | "license": "ISC", 9 | "homepage": "", 10 | "ignore": [ 11 | "**/.*", 12 | "node_modules", 13 | "bower_components", 14 | "test", 15 | "tests" 16 | ], 17 | "dependencies": { 18 | "jquery": "^3.4.1", 19 | "reset-stylus": "*", 20 | "reset-additional": "https://github.com/maximzhurkin/reset-additional.git" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maximzhurkin/jquery-stack-menu/94b4574c00d63cfc2a9d2e0dfd66636699c275b2/demo.gif -------------------------------------------------------------------------------- /dist/example.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 60px; 3 | padding-bottom: 60px; 4 | font-family: 'Roboto', sans-serif; 5 | background-color: #f9f9f9; 6 | } 7 | .header, 8 | .footer { 9 | text-align: center; 10 | } 11 | .header { 12 | margin-bottom: 60px; 13 | } 14 | .header__heading { 15 | margin-top: 0; 16 | margin-bottom: 20px; 17 | font-weight: 900; 18 | font-size: 30px; 19 | line-height: 1em; 20 | } 21 | .header__description { 22 | font-size: 16px; 23 | font-weight: 300; 24 | } 25 | .header__link { 26 | margin-left: 10px; 27 | margin-right: 10px; 28 | font-weight: 700; 29 | color: #007aff; 30 | text-decoration: none; 31 | border-bottom: 2px solid rgba(0,122,255,0.2); 32 | -webkit-transition: border-color 0.25s ease-in-out; 33 | -moz-transition: border-color 0.25s ease-in-out; 34 | transition: border-color 0.25s ease-in-out; 35 | } 36 | .header__link:hover { 37 | border-color: #007aff; 38 | } 39 | .menu { 40 | margin-left: auto; 41 | margin-right: auto; 42 | margin-bottom: 60px; 43 | width: 200px; 44 | font-weight: 500; 45 | font-size: 14px; 46 | } 47 | .footer { 48 | font-size: 12px; 49 | font-weight: 300; 50 | } 51 | .footer__link { 52 | color: #000; 53 | text-decoration: none; 54 | border-bottom: 1px solid rgba(0,0,0,0.2); 55 | -webkit-transition: border-color 0.25s ease-in-out; 56 | -moz-transition: border-color 0.25s ease-in-out; 57 | transition: border-color 0.25s ease-in-out; 58 | } 59 | .footer__link:hover { 60 | border-color: #000; 61 | } 62 | -------------------------------------------------------------------------------- /dist/example.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | $('#stack-menu').stackMenu(); 3 | }); 4 | -------------------------------------------------------------------------------- /dist/example.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:60px;padding-bottom:60px;font-family:'Roboto',sans-serif;background-color:#f9f9f9}.footer,.header{text-align:center}.header{margin-bottom:60px}.header__heading{margin-top:0;margin-bottom:20px;font-weight:900;font-size:30px;line-height:1em}.header__description{font-size:16px;font-weight:300}.header__link{margin-left:10px;margin-right:10px;font-weight:700;color:#007aff;text-decoration:none;border-bottom:2px solid rgba(0,122,255,.2);-webkit-transition:border-color .25s ease-in-out;-moz-transition:border-color .25s ease-in-out;transition:border-color .25s ease-in-out}.header__link:hover{border-color:#007aff}.menu{margin-left:auto;margin-right:auto;margin-bottom:60px;width:200px;font-weight:500;font-size:14px}.footer{font-size:12px;font-weight:300}.footer__link{color:#000;text-decoration:none;border-bottom:1px solid rgba(0,0,0,.2);-webkit-transition:border-color .25s ease-in-out;-moz-transition:border-color .25s ease-in-out;transition:border-color .25s ease-in-out}.footer__link:hover{border-color:#000} -------------------------------------------------------------------------------- /dist/example.min.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){$("#stack-menu").stackMenu()}); -------------------------------------------------------------------------------- /dist/jquery-stack-menu.css: -------------------------------------------------------------------------------- 1 | .stack-menu { 2 | background-color: #fff; 3 | -webkit-border-radius: 5px; 4 | border-radius: 5px; 5 | -webkit-box-shadow: 0 6px 32px 0 rgba(0,0,0,0.1); 6 | box-shadow: 0 6px 32px 0 rgba(0,0,0,0.1); 7 | overflow: hidden; 8 | } 9 | .stack-menu--active { 10 | display: block; 11 | } 12 | .stack-menu__list { 13 | margin: 0; 14 | padding: 0; 15 | list-style-type: none; 16 | display: none; 17 | } 18 | .stack-menu__list--active { 19 | display: block; 20 | } 21 | .stack-menu__item--hidden { 22 | display: none; 23 | } 24 | .stack-menu__link { 25 | padding-top: 14px; 26 | padding-left: 20px; 27 | padding-right: 20px; 28 | padding-bottom: 14px; 29 | display: block; 30 | text-decoration: none; 31 | position: relative; 32 | color: #000; 33 | -webkit-transition: color 0.25s ease-in-out, background-color 0.25s ease-in-out, background-image 0.25s ease-in-out; 34 | -moz-transition: color 0.25s ease-in-out, background-color 0.25s ease-in-out, background-image 0.25s ease-in-out; 35 | transition: color 0.25s ease-in-out, background-color 0.25s ease-in-out, background-image 0.25s ease-in-out; 36 | } 37 | .stack-menu__link--parent, 38 | .stack-menu__link--back { 39 | -webkit-background-size: 6px 12px; 40 | background-size: 6px 12px; 41 | background-repeat: no-repeat; 42 | } 43 | .stack-menu__link--parent { 44 | background-position: center right 20px; 45 | background-image: url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23000' fill-rule='evenodd'%3E%3Cpath d='M.087 1.656L1.232.318l4.681 5.026-1.145 1.338z'/%3E%3Cpath d='M4.768 5.318l1.145 1.338-4.68 5.026-1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E"); 46 | } 47 | .stack-menu__link--back { 48 | background-position: center left 20px; 49 | background-image: url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23000' fill-rule='evenodd'%3E%3Cpath d='M5.913 1.656L4.768.318.087 5.344l1.145 1.338z'/%3E%3Cpath d='M1.232 5.318L.087 6.656l4.68 5.026 1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E"); 50 | } 51 | .stack-menu__link--hidden { 52 | display: none; 53 | } 54 | .stack-menu__link:hover { 55 | color: #fff; 56 | background-color: #007aff; 57 | } 58 | .stack-menu__link--parent:hover { 59 | background-image: url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23fff' fill-rule='evenodd'%3E%3Cpath d='M.087 1.656L1.232.318l4.681 5.026-1.145 1.338z'/%3E%3Cpath d='M4.768 5.318l1.145 1.338-4.68 5.026-1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E"); 60 | } 61 | .stack-menu__link--back:hover { 62 | background-image: url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23fff' fill-rule='evenodd'%3E%3Cpath d='M5.913 1.656L4.768.318.087 5.344l1.145 1.338z'/%3E%3Cpath d='M1.232 5.318L.087 6.656l4.68 5.026 1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E"); 63 | } 64 | -------------------------------------------------------------------------------- /dist/jquery-stack-menu.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | var methods; 3 | methods = { 4 | init: function(element, options) { 5 | element.addClass('stack-menu'); 6 | element.find('ul').addClass('stack-menu__list'); 7 | element.find('ul:first').addClass('stack-menu__list--root').addClass('stack-menu__list--active'); 8 | element.find('li').addClass('stack-menu__item'); 9 | element.find('a').addClass('stack-menu__link'); 10 | $('.stack-menu__item').each(function(index) { 11 | $(this).attr('data-id', index); 12 | if ($(this).find('.stack-menu__list').length > 0) { 13 | $(this).children('.stack-menu__link').addClass('stack-menu__link--parent'); 14 | } 15 | }); 16 | $('.stack-menu__list').each(function() { 17 | var allItemElement, allLinkElement, backItemElement, backLinkElement, url; 18 | if (!$(this).hasClass('stack-menu__list--root')) { 19 | if (options.all) { 20 | url = $(this).closest('.stack-menu__item').find('.stack-menu__link').attr('href'); 21 | allItemElement = $('
  • ', { 22 | "class": 'stack-menu__item' 23 | }); 24 | allLinkElement = $('', { 25 | "class": 'stack-menu__link', 26 | href: url, 27 | text: options.allTitle 28 | }); 29 | allItemElement.append(allLinkElement); 30 | $(this).prepend(allItemElement); 31 | } 32 | backItemElement = $('
  • ', { 33 | "class": 'stack-menu__item' 34 | }); 35 | backLinkElement = $('', { 36 | "class": 'stack-menu__link stack-menu__link--back', 37 | href: '#', 38 | html: ' ' 39 | }); 40 | backItemElement.append(backLinkElement); 41 | $(this).prepend(backItemElement); 42 | } 43 | }); 44 | element.find('.stack-menu__link').click(function(event) { 45 | var item, link, list, parent, sub; 46 | link = $(this); 47 | item = link.closest('.stack-menu__item'); 48 | list = item.closest('.stack-menu__list'); 49 | parent = list.closest('.stack-menu__item'); 50 | sub = item.children('.stack-menu__list'); 51 | if (link.hasClass('stack-menu__link--back')) { 52 | event.preventDefault(); 53 | list.removeClass('stack-menu__list--active'); 54 | list.removeClass('stack-menu__list--active'); 55 | parent.removeClass('stack-menu__item--opened'); 56 | parent.find('.stack-menu__link').removeClass('stack-menu__link--hidden'); 57 | parent.closest('.stack-menu__list').children('.stack-menu__item').removeClass('stack-menu__item--hidden'); 58 | } else { 59 | if (item.children('.stack-menu__list').length === 0) { 60 | return true; 61 | } else { 62 | event.preventDefault(); 63 | parent.addClass('stack-menu__item--opened'); 64 | link.addClass('stack-menu__link--hidden'); 65 | sub.addClass('stack-menu__list--active'); 66 | $(list.children('.stack-menu__item')).each(function() { 67 | if ($(this).data('id') !== item.data('id')) { 68 | $(this).addClass('stack-menu__item--hidden'); 69 | } 70 | }); 71 | } 72 | } 73 | }); 74 | } 75 | }; 76 | jQuery.fn.stackMenu = function(options) { 77 | options = $.extend({ 78 | all: false, 79 | allTitle: 'All' 80 | }, options); 81 | methods.init(this, options); 82 | return { 83 | reset: function(element) { 84 | $(element).find('.stack-menu').removeClass('stack-menu--active'); 85 | $(element).find('.stack-menu__list').removeClass('stack-menu__list--active'); 86 | $(element).find('.stack-menu__item').removeClass('stack-menu__item--hidden').removeClass('stack-menu__item--opened'); 87 | $(element).find('.stack-menu__link').removeClass('stack-menu__link--hidden'); 88 | $(element).find('.stack-menu__list--root').addClass('stack-menu__list--active'); 89 | } 90 | }; 91 | }; 92 | })(jQuery); 93 | -------------------------------------------------------------------------------- /dist/jquery-stack-menu.min.css: -------------------------------------------------------------------------------- 1 | .stack-menu{background-color:#fff;-webkit-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 6px 32px 0 rgba(0,0,0,.1);box-shadow:0 6px 32px 0 rgba(0,0,0,.1);overflow:hidden}.stack-menu--active{display:block}.stack-menu__list{margin:0;padding:0;list-style-type:none;display:none}.stack-menu__list--active{display:block}.stack-menu__item--hidden{display:none}.stack-menu__link{padding:14px 20px;display:block;text-decoration:none;position:relative;color:#000;-webkit-transition:color .25s ease-in-out,background-color .25s ease-in-out,background-image .25s ease-in-out;-moz-transition:color .25s ease-in-out,background-color .25s ease-in-out,background-image .25s ease-in-out;transition:color .25s ease-in-out,background-color .25s ease-in-out,background-image .25s ease-in-out}.stack-menu__link--back,.stack-menu__link--parent{-webkit-background-size:6px 12px;background-size:6px 12px;background-repeat:no-repeat}.stack-menu__link--parent{background-position:center right 20px;background-image:url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23000' fill-rule='evenodd'%3E%3Cpath d='M.087 1.656L1.232.318l4.681 5.026-1.145 1.338z'/%3E%3Cpath d='M4.768 5.318l1.145 1.338-4.68 5.026-1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E")}.stack-menu__link--back{background-position:center left 20px;background-image:url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23000' fill-rule='evenodd'%3E%3Cpath d='M5.913 1.656L4.768.318.087 5.344l1.145 1.338z'/%3E%3Cpath d='M1.232 5.318L.087 6.656l4.68 5.026 1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E")}.stack-menu__link--hidden{display:none}.stack-menu__link:hover{color:#fff;background-color:#007aff}.stack-menu__link--parent:hover{background-image:url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23fff' fill-rule='evenodd'%3E%3Cpath d='M.087 1.656L1.232.318l4.681 5.026-1.145 1.338z'/%3E%3Cpath d='M4.768 5.318l1.145 1.338-4.68 5.026-1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E")}.stack-menu__link--back:hover{background-image:url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23fff' fill-rule='evenodd'%3E%3Cpath d='M5.913 1.656L4.768.318.087 5.344l1.145 1.338z'/%3E%3Cpath d='M1.232 5.318L.087 6.656l4.68 5.026 1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E")} -------------------------------------------------------------------------------- /dist/jquery-stack-menu.min.js: -------------------------------------------------------------------------------- 1 | !function(s){var e;e={init:function(e,t){e.addClass("stack-menu"),e.find("ul").addClass("stack-menu__list"),e.find("ul:first").addClass("stack-menu__list--root").addClass("stack-menu__list--active"),e.find("li").addClass("stack-menu__item"),e.find("a").addClass("stack-menu__link"),s(".stack-menu__item").each(function(e){s(this).attr("data-id",e),s(this).find(".stack-menu__list").length>0&&s(this).children(".stack-menu__link").addClass("stack-menu__link--parent")}),s(".stack-menu__list").each(function(){var e,a,n,i,_;s(this).hasClass("stack-menu__list--root")||(t.all&&(_=s(this).closest(".stack-menu__item").find(".stack-menu__link").attr("href"),e=s("
  • ",{class:"stack-menu__item"}),a=s("",{class:"stack-menu__link",href:_,text:t.allTitle}),e.append(a),s(this).prepend(e)),n=s("
  • ",{class:"stack-menu__item"}),i=s("",{class:"stack-menu__link stack-menu__link--back",href:"#",html:" "}),n.append(i),s(this).prepend(n))}),e.find(".stack-menu__link").click(function(e){var t,a,n,i,_;if(a=s(this),t=a.closest(".stack-menu__item"),n=t.closest(".stack-menu__list"),i=n.closest(".stack-menu__item"),_=t.children(".stack-menu__list"),a.hasClass("stack-menu__link--back"))e.preventDefault(),n.removeClass("stack-menu__list--active"),n.removeClass("stack-menu__list--active"),i.removeClass("stack-menu__item--opened"),i.find(".stack-menu__link").removeClass("stack-menu__link--hidden"),i.closest(".stack-menu__list").children(".stack-menu__item").removeClass("stack-menu__item--hidden");else{if(0===t.children(".stack-menu__list").length)return!0;e.preventDefault(),i.addClass("stack-menu__item--opened"),a.addClass("stack-menu__link--hidden"),_.addClass("stack-menu__list--active"),s(n.children(".stack-menu__item")).each(function(){s(this).data("id")!==t.data("id")&&s(this).addClass("stack-menu__item--hidden")})}})}},jQuery.fn.stackMenu=function(t){return t=s.extend({all:!1,allTitle:"All"},t),e.init(this,t),{reset:function(e){s(e).find(".stack-menu").removeClass("stack-menu--active"),s(e).find(".stack-menu__list").removeClass("stack-menu__list--active"),s(e).find(".stack-menu__item").removeClass("stack-menu__item--hidden").removeClass("stack-menu__item--opened"),s(e).find(".stack-menu__link").removeClass("stack-menu__link--hidden"),s(e).find(".stack-menu__list--root").addClass("stack-menu__list--active")}}}}(jQuery); -------------------------------------------------------------------------------- /docs/example.min.css: -------------------------------------------------------------------------------- 1 | body{padding-top:60px;padding-bottom:60px;font-family:'Roboto',sans-serif;background-color:#f9f9f9}.footer,.header{text-align:center}.header{margin-bottom:60px}.header__heading{margin-top:0;margin-bottom:20px;font-weight:900;font-size:30px;line-height:1em}.header__description{font-size:16px;font-weight:300}.header__link{margin-left:10px;margin-right:10px;font-weight:700;color:#007aff;text-decoration:none;border-bottom:2px solid rgba(0,122,255,.2);-webkit-transition:border-color .25s ease-in-out;-moz-transition:border-color .25s ease-in-out;transition:border-color .25s ease-in-out}.header__link:hover{border-color:#007aff}.menu{margin-left:auto;margin-right:auto;margin-bottom:60px;width:200px;font-weight:500;font-size:14px}.footer{font-size:12px;font-weight:300}.footer__link{color:#000;text-decoration:none;border-bottom:1px solid rgba(0,0,0,.2);-webkit-transition:border-color .25s ease-in-out;-moz-transition:border-color .25s ease-in-out;transition:border-color .25s ease-in-out}.footer__link:hover{border-color:#000} -------------------------------------------------------------------------------- /docs/example.min.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function(){$("#stack-menu").stackMenu()}); -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
    15 |

    jQuery Stack Menu

    16 |

    The plugin make simply navigation menu from unordered list

    17 |
    18 |
    19 | 63 | 66 | -------------------------------------------------------------------------------- /docs/jquery-stack-menu.min.css: -------------------------------------------------------------------------------- 1 | .stack-menu{background-color:#fff;-webkit-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 6px 32px 0 rgba(0,0,0,.1);box-shadow:0 6px 32px 0 rgba(0,0,0,.1);overflow:hidden}.stack-menu--active{display:block}.stack-menu__list{margin:0;padding:0;list-style-type:none;display:none}.stack-menu__list--active{display:block}.stack-menu__item--hidden{display:none}.stack-menu__link{padding:14px 20px;display:block;text-decoration:none;position:relative;color:#000;-webkit-transition:color .25s ease-in-out,background-color .25s ease-in-out,background-image .25s ease-in-out;-moz-transition:color .25s ease-in-out,background-color .25s ease-in-out,background-image .25s ease-in-out;transition:color .25s ease-in-out,background-color .25s ease-in-out,background-image .25s ease-in-out}.stack-menu__link--back,.stack-menu__link--parent{-webkit-background-size:6px 12px;background-size:6px 12px;background-repeat:no-repeat}.stack-menu__link--parent{background-position:center right 20px;background-image:url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23000' fill-rule='evenodd'%3E%3Cpath d='M.087 1.656L1.232.318l4.681 5.026-1.145 1.338z'/%3E%3Cpath d='M4.768 5.318l1.145 1.338-4.68 5.026-1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E")}.stack-menu__link--back{background-position:center left 20px;background-image:url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23000' fill-rule='evenodd'%3E%3Cpath d='M5.913 1.656L4.768.318.087 5.344l1.145 1.338z'/%3E%3Cpath d='M1.232 5.318L.087 6.656l4.68 5.026 1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E")}.stack-menu__link--hidden{display:none}.stack-menu__link:hover{color:#fff;background-color:#007aff}.stack-menu__link--parent:hover{background-image:url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23fff' fill-rule='evenodd'%3E%3Cpath d='M.087 1.656L1.232.318l4.681 5.026-1.145 1.338z'/%3E%3Cpath d='M4.768 5.318l1.145 1.338-4.68 5.026-1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E")}.stack-menu__link--back:hover{background-image:url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23fff' fill-rule='evenodd'%3E%3Cpath d='M5.913 1.656L4.768.318.087 5.344l1.145 1.338z'/%3E%3Cpath d='M1.232 5.318L.087 6.656l4.68 5.026 1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E")} -------------------------------------------------------------------------------- /docs/jquery-stack-menu.min.js: -------------------------------------------------------------------------------- 1 | !function(s){var e;e={init:function(e,t){e.addClass("stack-menu"),e.find("ul").addClass("stack-menu__list"),e.find("ul:first").addClass("stack-menu__list--root").addClass("stack-menu__list--active"),e.find("li").addClass("stack-menu__item"),e.find("a").addClass("stack-menu__link"),s(".stack-menu__item").each(function(e){s(this).attr("data-id",e),s(this).find(".stack-menu__list").length>0&&s(this).children(".stack-menu__link").addClass("stack-menu__link--parent")}),s(".stack-menu__list").each(function(){var e,a,n,i,_;s(this).hasClass("stack-menu__list--root")||(t.all&&(_=s(this).closest(".stack-menu__item").find(".stack-menu__link").attr("href"),e=s("
  • ",{class:"stack-menu__item"}),a=s("",{class:"stack-menu__link",href:_,text:t.allTitle}),e.append(a),s(this).prepend(e)),n=s("
  • ",{class:"stack-menu__item"}),i=s("",{class:"stack-menu__link stack-menu__link--back",href:"#",html:" "}),n.append(i),s(this).prepend(n))}),e.find(".stack-menu__link").click(function(e){var t,a,n,i,_;if(a=s(this),t=a.closest(".stack-menu__item"),n=t.closest(".stack-menu__list"),i=n.closest(".stack-menu__item"),_=t.children(".stack-menu__list"),a.hasClass("stack-menu__link--back"))e.preventDefault(),n.removeClass("stack-menu__list--active"),n.removeClass("stack-menu__list--active"),i.removeClass("stack-menu__item--opened"),i.find(".stack-menu__link").removeClass("stack-menu__link--hidden"),i.closest(".stack-menu__list").children(".stack-menu__item").removeClass("stack-menu__item--hidden");else{if(0===t.children(".stack-menu__list").length)return!0;e.preventDefault(),i.addClass("stack-menu__item--opened"),a.addClass("stack-menu__link--hidden"),_.addClass("stack-menu__list--active"),s(n.children(".stack-menu__item")).each(function(){s(this).data("id")!==t.data("id")&&s(this).addClass("stack-menu__item--hidden")})}})}},jQuery.fn.stackMenu=function(t){return t=s.extend({all:!1,allTitle:"All"},t),e.init(this,t),{reset:function(e){s(e).find(".stack-menu").removeClass("stack-menu--active"),s(e).find(".stack-menu__list").removeClass("stack-menu__list--active"),s(e).find(".stack-menu__item").removeClass("stack-menu__item--hidden").removeClass("stack-menu__item--opened"),s(e).find(".stack-menu__link").removeClass("stack-menu__link--hidden"),s(e).find(".stack-menu__list--root").addClass("stack-menu__list--active")}}}}(jQuery); -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | plumber = require('gulp-plumber'), 3 | watch = require('gulp-watch'), 4 | rename = require('gulp-rename'), 5 | pug = require('gulp-pug'), 6 | coffee = require('gulp-coffee'), 7 | stylus = require('gulp-stylus'), 8 | uglify = require('gulp-uglify'), 9 | csso = require('gulp-csso'), 10 | autoprefixer = require('gulp-autoprefixer'), 11 | htmlhint = require('gulp-htmlhint'), 12 | browserSync = require('browser-sync').create(); 13 | 14 | gulp.task('serve', function() { 15 | browserSync.init({ 16 | server: { 17 | baseDir: "./docs" 18 | }, 19 | notify: false 20 | }); 21 | }); 22 | 23 | gulp.task('html', function() { 24 | return gulp.src(['./src/pug/*.pug']) 25 | .pipe(plumber()) 26 | .pipe(pug({ 27 | pretty: '\t' 28 | })) 29 | .pipe(htmlhint('./.htmlhintrc')) 30 | .pipe(htmlhint.reporter()) 31 | .pipe(gulp.dest('./docs/')) 32 | }); 33 | 34 | gulp.task('html-refresh', ['html'], function () { 35 | browserSync.reload(); 36 | }); 37 | 38 | gulp.task('js', function() { 39 | return gulp.src('./src/coffee/*.coffee') 40 | .pipe(plumber()) 41 | .pipe(coffee({ bare: true })) 42 | .pipe(gulp.dest('./dist/')) 43 | .pipe(uglify({ 44 | preserveComments: 'license', 45 | mangle: true 46 | })) 47 | .pipe(rename({ extname: '.min.js' })) 48 | .pipe(gulp.dest('./dist/')) 49 | .pipe(gulp.dest('./docs/')) 50 | .pipe(browserSync.stream({ match: '**/*.js' })); 51 | }); 52 | 53 | gulp.task('css', function() { 54 | return gulp.src(['./src/stylus/*.styl']) 55 | .pipe(plumber()) 56 | .pipe(stylus({'include css': true})) 57 | .pipe(autoprefixer({ browsers: ['last 2 versions', 'ios >= 7','firefox >=4','safari >=7','IE >=8','android >=2'] })) 58 | .pipe(gulp.dest('./dist/')) 59 | .pipe(csso()) 60 | .pipe(rename({ extname: '.min.css' })) 61 | .pipe(gulp.dest('./dist/')) 62 | .pipe(gulp.dest('./docs/')) 63 | .pipe(browserSync.stream({ match: '**/*.css' })); 64 | }); 65 | 66 | gulp.task('watch', function () { 67 | watch(['./src/coffee/*.coffee'], function() { 68 | gulp.start('js'); 69 | }); 70 | watch(['./src/stylus/*.styl'], function() { 71 | gulp.start('css'); 72 | }); 73 | watch(['./src/pug/*.pug'], function() { 74 | gulp.start('html-refresh'); 75 | }); 76 | }); 77 | 78 | gulp.task('main', function() { 79 | gulp.start('html'); 80 | gulp.start('js'); 81 | gulp.start('css'); 82 | }); 83 | 84 | gulp.task('default', ['main', 'watch', 'serve']); 85 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jquery-slide-menu", 3 | "version": "1.0.0", 4 | "description": "jQuery slide menu plugin", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Max Zhurkin", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "browser-sync": "^2.24.4", 13 | "gulp": "^3.9.1", 14 | "gulp-autoprefixer": "^3.1.1", 15 | "gulp-coffee": "^2.3.5", 16 | "gulp-csso": "^2.0.0", 17 | "gulp-htmlhint": "^2.2.1", 18 | "gulp-plumber": "^1.2.0", 19 | "gulp-pug": "^3.2.0", 20 | "gulp-stylus": "^2.7.0", 21 | "gulp-uglify": "^2.0.1", 22 | "gulp-watch": "^4.3.11", 23 | "gulp-rename": "^1.4.0" 24 | }, 25 | "dependencies": {} 26 | } 27 | -------------------------------------------------------------------------------- /src/coffee/example.coffee: -------------------------------------------------------------------------------- 1 | $(document).ready -> 2 | 3 | $('#stack-menu').stackMenu() 4 | 5 | return 6 | -------------------------------------------------------------------------------- /src/coffee/jquery-stack-menu.coffee: -------------------------------------------------------------------------------- 1 | (($) -> 2 | methods = 3 | init: (element, options) -> 4 | element.addClass 'stack-menu' 5 | element.find('ul').addClass 'stack-menu__list' 6 | element.find('ul:first') 7 | .addClass 'stack-menu__list--root' 8 | .addClass 'stack-menu__list--active' 9 | element.find('li').addClass 'stack-menu__item' 10 | element.find('a').addClass 'stack-menu__link' 11 | 12 | $('.stack-menu__item').each (index) -> 13 | $(@).attr('data-id', index) 14 | if $(@).find('.stack-menu__list').length > 0 15 | $(@).children('.stack-menu__link').addClass 'stack-menu__link--parent' 16 | return 17 | 18 | $('.stack-menu__list').each -> 19 | if not $(@).hasClass('stack-menu__list--root') 20 | if options.all 21 | url = $(@).closest('.stack-menu__item') 22 | .find('.stack-menu__link') 23 | .attr('href') 24 | allItemElement = $('
  • ', class: 'stack-menu__item') 25 | allLinkElement = $('', class: 'stack-menu__link', href: url, text: options.allTitle) 26 | allItemElement.append allLinkElement 27 | $(@).prepend allItemElement 28 | backItemElement = $('
  • ', class: 'stack-menu__item') 29 | backLinkElement = $('', class: 'stack-menu__link stack-menu__link--back', href: '#', html: ' ') 30 | backItemElement.append backLinkElement 31 | $(@).prepend backItemElement 32 | return 33 | 34 | element.find('.stack-menu__link').click (event) -> 35 | link = $(@) 36 | item = link.closest('.stack-menu__item') 37 | list = item.closest('.stack-menu__list') 38 | parent = list.closest('.stack-menu__item') 39 | sub = item.children('.stack-menu__list') 40 | 41 | if link.hasClass('stack-menu__link--back') 42 | event.preventDefault() 43 | list.removeClass('stack-menu__list--active') 44 | list.removeClass('stack-menu__list--active') 45 | parent.removeClass('stack-menu__item--opened') 46 | parent.find('.stack-menu__link') 47 | .removeClass('stack-menu__link--hidden') 48 | parent.closest('.stack-menu__list') 49 | .children('.stack-menu__item') 50 | .removeClass('stack-menu__item--hidden') 51 | else 52 | if item.children('.stack-menu__list').length == 0 53 | return true 54 | else 55 | event.preventDefault() 56 | parent.addClass('stack-menu__item--opened') 57 | link.addClass('stack-menu__link--hidden') 58 | sub.addClass('stack-menu__list--active') 59 | $(list.children('.stack-menu__item')).each -> 60 | if $(@).data('id') != item.data('id') 61 | $(@).addClass('stack-menu__item--hidden') 62 | return 63 | return 64 | return 65 | 66 | jQuery.fn.stackMenu = (options) -> 67 | options = $.extend({ 68 | all: false 69 | allTitle: 'All' 70 | }, options) 71 | methods.init @, options 72 | return { reset: (element) -> 73 | $(element).find('.stack-menu').removeClass('stack-menu--active') 74 | $(element).find('.stack-menu__list').removeClass('stack-menu__list--active') 75 | $(element).find('.stack-menu__item').removeClass('stack-menu__item--hidden') 76 | .removeClass('stack-menu__item--opened') 77 | $(element).find('.stack-menu__link').removeClass('stack-menu__link--hidden') 78 | $(element).find('.stack-menu__list--root').addClass('stack-menu__list--active') 79 | return 80 | } 81 | 82 | return 83 | ) jQuery 84 | -------------------------------------------------------------------------------- /src/pug/index.pug: -------------------------------------------------------------------------------- 1 | 2 | html(lang="en") 3 | head 4 | meta(charset="UTF-8") 5 | title Example 6 | link(href='https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,900&display=swap', rel='stylesheet') 7 | link(href='example.min.css', rel='stylesheet') 8 | link(href='jquery-stack-menu.min.css', rel='stylesheet') 9 | script(src='https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js') 10 | script(src='jquery-stack-menu.min.js') 11 | script(src='example.min.js') 12 | body 13 | header.header 14 | h1.header__heading jQuery Stack Menu 15 | p.header__description The plugin make simply navigation menu from unordered list 16 | .header__links 17 | a.header__link(href='https://github.com/maximzhurkin/jquery-stack-menu/archive/master.zip') Download 18 | a.header__link(href='https://github.com/maximzhurkin/jquery-stack-menu#getting-started') Docs 19 | .menu 20 | nav#stack-menu 21 | ul 22 | li 23 | a(href='#') Fugiat 24 | ul 25 | li 26 | a(href='#') Perferendis 27 | li 28 | a(href='#') Recusandae 29 | ul 30 | li 31 | a(href='#') Vel 32 | li 33 | a(href='#') Cumque 34 | li 35 | a(href='#') Facere 36 | li 37 | a(href='#') Enim 38 | li 39 | a(href='#') Nihil 40 | ul 41 | li 42 | a(href='#') Quidem 43 | li 44 | a(href='#') Temporibus 45 | li 46 | a(href='#') Fugit 47 | li 48 | a(href='#') Dolore 49 | li 50 | a(href='#') Vitae 51 | li 52 | a(href='#') Ipsum 53 | li 54 | a(href='#') Lorem 55 | ul 56 | li 57 | a(href='#') Similique 58 | li 59 | a(href='#') Distinctio 60 | li 61 | a(href='#') Porro 62 | li 63 | a(href='#') Illum 64 | li 65 | a(href='#') Perspiciatis 66 | li 67 | a(href='#') Doloremque 68 | ul 69 | li 70 | a(href='#') Quod 71 | li 72 | a(href='#') Provident 73 | li 74 | a(href='#') Cumque 75 | footer.footer 76 | p 77 | | © 2019  78 | a.footer__link(href='http://maxzhurkin.name') Max Zhurkin 79 | | . Mit license. 80 | -------------------------------------------------------------------------------- /src/stylus/example.styl: -------------------------------------------------------------------------------- 1 | // Vendors 2 | // @import '../../bower_components/reset-stylus/src/reset-stylus.styl' 3 | // @import '../../bower_components/reset-additional/src/reset-additional.styl' 4 | 5 | body 6 | padding-top 60px 7 | padding-bottom 60px 8 | font-family 'Roboto', sans-serif 9 | background-color #f9f9f9 10 | 11 | .header, 12 | .footer 13 | text-align center 14 | .header 15 | margin-bottom 60px 16 | &__heading 17 | margin-top 0 18 | margin-bottom 20px 19 | font-weight 900 20 | font-size 30px 21 | line-height 1em 22 | &__description 23 | font-size 16px 24 | font-weight 300 25 | &__link 26 | margin-left 10px 27 | margin-right 10px 28 | font-weight 700 29 | color #007aff 30 | text-decoration none 31 | border-bottom 2px solid rgba(#007aff, .2) 32 | transition border-color .25s ease-in-out 33 | &:hover 34 | border-color #007aff 35 | .menu 36 | margin-left auto 37 | margin-right auto 38 | margin-bottom 60px 39 | width 200px 40 | font-weight 500 41 | font-size 14px 42 | .footer 43 | font-size 12px 44 | font-weight 300 45 | &__link 46 | color #000 47 | text-decoration none 48 | border-bottom 1px solid rgba(#000, .2) 49 | transition border-color .25s ease-in-out 50 | &:hover 51 | border-color #000 52 | -------------------------------------------------------------------------------- /src/stylus/jquery-stack-menu.styl: -------------------------------------------------------------------------------- 1 | .stack-menu 2 | background-color #ffffff 3 | border-radius 5px 4 | box-shadow 0 6px 32px 0 rgba(#000, .1) 5 | overflow hidden 6 | &--active 7 | display block 8 | &__list 9 | margin 0 10 | padding 0 11 | list-style-type none 12 | display none 13 | &--active 14 | display block 15 | &__item--hidden 16 | display none 17 | &__link 18 | padding-top 14px 19 | padding-left 20px 20 | padding-right 20px 21 | padding-bottom 14px 22 | display block 23 | text-decoration none 24 | position relative 25 | color #000000 26 | transition color .25s ease-in-out, background-color .25s ease-in-out, background-image .25s ease-in-out 27 | &--parent, 28 | &--back 29 | background-size 6px 12px 30 | background-repeat no-repeat 31 | &--parent 32 | background-position center right 20px 33 | background-image url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23000' fill-rule='evenodd'%3E%3Cpath d='M.087 1.656L1.232.318l4.681 5.026-1.145 1.338z'/%3E%3Cpath d='M4.768 5.318l1.145 1.338-4.68 5.026-1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E") 34 | &--back 35 | background-position center left 20px 36 | background-image url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23000' fill-rule='evenodd'%3E%3Cpath d='M5.913 1.656L4.768.318.087 5.344l1.145 1.338z'/%3E%3Cpath d='M1.232 5.318L.087 6.656l4.68 5.026 1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E") 37 | &--hidden 38 | display none 39 | &:hover 40 | color #fff 41 | background-color #007aff 42 | &--parent:hover 43 | background-image url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23fff' fill-rule='evenodd'%3E%3Cpath d='M.087 1.656L1.232.318l4.681 5.026-1.145 1.338z'/%3E%3Cpath d='M4.768 5.318l1.145 1.338-4.68 5.026-1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E") 44 | &--back:hover 45 | background-image url("data:image/svg+xml,%3Csvg width='6' height='12' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23fff' fill-rule='evenodd'%3E%3Cpath d='M5.913 1.656L4.768.318.087 5.344l1.145 1.338z'/%3E%3Cpath d='M1.232 5.318L.087 6.656l4.68 5.026 1.146-1.338z'/%3E%3C/g%3E%3C/svg%3E") 46 | --------------------------------------------------------------------------------