diff --git a/README.md b/README.md index 884a60a..2691e17 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ Following the steps below you will be able to get the plugin up and running. If 4. Customizable options: ```javascript var nav = responsiveNav(".nav-collapse", { // Selector + alwaysDropdown: false, // Boolean: Override breakpoint to always use mobile-style nav animate: true, // Boolean: Use CSS3 transitions, true or false transition: 284, // Integer: Speed of the transition, in milliseconds label: "Menu", // String: Label for the navigation toggle @@ -218,6 +219,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI # Changelog +`1.0.33` (2014-12-05) - Adds `alwaysDropdown` option to enable mobile-style navigation regardless of the MQ breakpoint. `1.0.32` (2014-03-05) - Ditching the `[].forEach.call(NodeList)` hack to make the code more sensible and future-proof. diff --git a/bower.json b/bower.json index d602080..8721e98 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "responsive-nav", - "version": "1.0.32", + "version": "1.0.33", "main": [ "responsive-nav.css", "responsive-nav.js" diff --git a/client/dist/responsive-nav.js b/client/dist/responsive-nav.js index 6336d3c..35880a0 100644 --- a/client/dist/responsive-nav.js +++ b/client/dist/responsive-nav.js @@ -142,6 +142,7 @@ // Default options this.options = { + alwaysDropdown: false, animate: true, // Boolean: Use CSS3 transitions, true or false transition: 284, // Integer: Speed of the transition, in milliseconds label: "Menu", // String: Label for the navigation toggle @@ -165,6 +166,11 @@ // Adds "js" class for addClass(htmlEl, this.options.jsClass); + // Disable media query at breakpoint + if (!this.options.alwaysDropdown) { + addClass(htmlEl, "nav-multiple-layouts"); + } + // Wrapper this.wrapperEl = el.replace("#", ""); @@ -202,6 +208,7 @@ removeClass(nav, opts.navClass); removeClass(nav, opts.navClass + "-" + this.index); removeClass(htmlEl, opts.navActiveClass); + removeClass(htmlEl, "nav-multiple-layouts"); nav.removeAttribute("style"); nav.removeAttribute("aria-hidden"); diff --git a/client/dist/responsive-nav.min.js b/client/dist/responsive-nav.min.js index 714c165..82ac25b 100644 --- a/client/dist/responsive-nav.min.js +++ b/client/dist/responsive-nav.min.js @@ -1 +1 @@ -!function(a,b,c){"use strict";var d=function(d,e){var f=!!b.getComputedStyle;f||(b.getComputedStyle=function(a){return this.el=a,this.getPropertyValue=function(b){var c=/(\-([a-z]){1})/g;return"float"===b&&(b="styleFloat"),c.test(b)&&(b=b.replace(c,function(){return arguments[2].toUpperCase()})),a.currentStyle[b]?a.currentStyle[b]:null},this});var g,h,i,j,k,l,m=function(a,b,c,d){if("addEventListener"in a)try{a.addEventListener(b,c,d)}catch(e){if("object"!=typeof c||!c.handleEvent)throw e;a.addEventListener(b,function(a){c.handleEvent.call(c,a)},d)}else"attachEvent"in a&&("object"==typeof c&&c.handleEvent?a.attachEvent("on"+b,function(){c.handleEvent.call(c)}):a.attachEvent("on"+b,c))},n=function(a,b,c,d){if("removeEventListener"in a)try{a.removeEventListener(b,c,d)}catch(e){if("object"!=typeof c||!c.handleEvent)throw e;a.removeEventListener(b,function(a){c.handleEvent.call(c,a)},d)}else"detachEvent"in a&&("object"==typeof c&&c.handleEvent?a.detachEvent("on"+b,function(){c.handleEvent.call(c)}):a.detachEvent("on"+b,c))},o=function(a){if(a.children.length<1)throw new Error("The Nav container has no containing elements");for(var b=[],c=0;c10||Math.abs(a.touches[0].clientY-this.startY)>10)&&(this.touchHasMoved=!0)},_onTouchEnd:function(c){if(this._preventDefault(c),!this.touchHasMoved){if("touchend"===c.type)return this.toggle(),"after"===h.insert&&setTimeout(function(){r(a.body,"disable-pointer-events")},h.transition+300),void 0;var d=c||b.event;3!==d.which&&2!==d.button&&this.toggle()}},_onKeyUp:function(a){var c=a||b.event;13===c.keyCode&&this.toggle()},_transitions:function(){if(h.animate){var a=g.style,b="max-height "+h.transition+"ms";a.WebkitTransition=b,a.MozTransition=b,a.OTransition=b,a.transition=b}},_calcHeight:function(){for(var a=0,b=0;b10||Math.abs(a.touches[0].clientY-this.startY)>10)&&(this.touchHasMoved=!0)},_onTouchEnd:function(c){if(this._preventDefault(c),!this.touchHasMoved){if("touchend"===c.type)return this.toggle(),void("after"===h.insert&&setTimeout(function(){r(a.body,"disable-pointer-events")},h.transition+300));var d=c||b.event;3!==d.which&&2!==d.button&&this.toggle()}},_onKeyUp:function(a){var c=a||b.event;13===c.keyCode&&this.toggle()},_transitions:function(){if(h.animate){var a=g.style,b="max-height "+h.transition+"ms";a.WebkitTransition=b,a.MozTransition=b,a.OTransition=b,a.transition=b}},_calcHeight:function(){for(var a=0,b=0;b addClass(htmlEl, this.options.jsClass); + // Disable media query at breakpoint + if (!this.options.alwaysDropdown) { + addClass(htmlEl, "nav-multiple-layouts"); + } + // Wrapper this.wrapperEl = el.replace("#", ""); @@ -89,6 +95,7 @@ removeClass(nav, opts.navClass); removeClass(nav, opts.navClass + "-" + this.index); removeClass(htmlEl, opts.navActiveClass); + removeClass(htmlEl, "nav-multiple-layouts"); nav.removeAttribute("style"); nav.removeAttribute("aria-hidden"); diff --git a/client/src/styles/responsive-nav.css b/client/src/styles/responsive-nav.css index 470d2d2..ccf99ef 100644 --- a/client/src/styles/responsive-nav.css +++ b/client/src/styles/responsive-nav.css @@ -41,13 +41,13 @@ } @media screen and (min-width: 40em) { - .js .nav-collapse { + .js.nav-multiple-layouts .nav-collapse { position: relative; } - .js .nav-collapse.closed { + .js.nav-multiple-layouts .nav-collapse.closed { max-height: none; } - .nav-toggle { + .nav-multiple-layouts .nav-toggle { display: none; } } diff --git a/client/test/responsive-nav.spec.js b/client/test/responsive-nav.spec.js index 803cd43..893fdd3 100644 --- a/client/test/responsive-nav.spec.js +++ b/client/test/responsive-nav.spec.js @@ -58,7 +58,7 @@ describe("responsive-nav", function () { it("adds a 'js' class", function () { insertNav(); - expect(document.documentElement.className).toBe("js"); + expect(document.documentElement.className).toBe("js nav-multiple-layouts"); nav.destroy(); }); @@ -242,6 +242,13 @@ describe("responsive-nav", function () { */ describe("options", function () { + it("allows user to always use dropdown navigation", function() { + document.getElementsByTagName("body")[0].appendChild(el); + nav = responsiveNav("#" + selector, { alwaysDropdown: true }); + expect(document.documentElement.className).not.toBe("js nav-multiple-layouts"); + nav.destroy(); + }); + it("turns off animation if needed", function () { document.getElementsByTagName("body")[0].appendChild(el); nav = responsiveNav("#" + selector, { animate: false }); @@ -295,14 +302,14 @@ describe("responsive-nav", function () { it("allows users to change the default container class", function () { document.getElementsByTagName("body")[0].appendChild(el); nav = responsiveNav("#" + selector, { navClass: "random-class" }); - expect(el.className).toBe("random-class random-class-22 closed"); + expect(el.className).toBe("random-class random-class-23 closed"); nav.destroy(); }); it("allows users to specify custom JS class", function () { document.getElementsByTagName("body")[0].appendChild(el); nav = responsiveNav("#" + selector, { jsClass: "foobar" }); - expect(document.documentElement.className).toBe("js foobar"); + expect(document.documentElement.className).toBe("js foobar nav-multiple-layouts"); nav.destroy(); }); diff --git a/demos/always-dropdown/index.html b/demos/always-dropdown/index.html new file mode 100644 index 0000000..a2194ff --- /dev/null +++ b/demos/always-dropdown/index.html @@ -0,0 +1,42 @@ + + + + + Responsive Nav · Always Dropdown + + + + + + + + + + + + + diff --git a/demos/always-dropdown/styles.css b/demos/always-dropdown/styles.css new file mode 100644 index 0000000..84ae8e0 --- /dev/null +++ b/demos/always-dropdown/styles.css @@ -0,0 +1,153 @@ +@charset "UTF-8"; + +/* ------------------------------------------ + RESET +--------------------------------------------- */ + +body, div, +h1, h2, h3, h4, h5, h6, +p, blockquote, pre, dl, dt, dd, ol, ul, li, hr, +fieldset, form, label, legend, th, td, +article, aside, figure, footer, header, hgroup, menu, nav, section, +summary, hgroup { + margin: 0; + padding: 0; + border: 0; +} + +a:active, +a:hover { + outline: 0; +} + +@-webkit-viewport { width: device-width; } +@-moz-viewport { width: device-width; } +@-ms-viewport { width: device-width; } +@-o-viewport { width: device-width; } +@viewport { width: device-width; } + + +/* ------------------------------------------ + BASE DEMO STYLES +--------------------------------------------- */ + +body { + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; + text-size-adjust: 100%; + color: #37302a; + background: #fff; + font: normal 100%/1.4 sans-serif; +} + +section { + border-bottom: 1px solid #999; + float: left; + width: 100%; + height: 800px; +} + + +/* ------------------------------------------ + NAVIGATION STYLES + (+ responsive-nav.css file is loaded in the ) +--------------------------------------------- */ + +.fixed { + position: fixed; + width: 100%; + top: 0; + left: 0; +} + +.nav-collapse, +.nav-collapse * { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +.nav-collapse, +.nav-collapse ul { + list-style: none; + width: 100%; + float: left; +} + +.nav-collapse li { + float: left; + width: 100%; +} + +.nav-collapse a { + color: #fff; + text-decoration: none; + width: 100%; + background: #f4421a; + border-bottom: 1px solid white; + padding: 0.7em 1em; + float: left; +} + +.nav-collapse ul ul a { + background: #ca3716; + padding-left: 2em; +} + + +/* ------------------------------------------ + NAV TOGGLE STYLES +--------------------------------------------- */ + +@font-face { + font-family: "responsivenav"; + src:url("../icons/responsivenav.eot"); + src:url("../icons/responsivenav.eot?#iefix") format("embedded-opentype"), + url("../icons/responsivenav.ttf") format("truetype"), + url("../icons/responsivenav.woff") format("woff"), + url("../icons/responsivenav.svg#responsivenav") format("svg"); + font-weight: normal; + font-style: normal; +} + +.nav-toggle { + position: fixed; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + text-decoration: none; + text-indent: -999px; + position: relative; + overflow: hidden; + width: 70px; + height: 55px; + float: right; +} + +.nav-toggle:before { + color: #f4421a; /* Edit this to change the icon color */ + font-family: "responsivenav", sans-serif; + font-style: normal; + font-weight: normal; + font-variant: normal; + font-size: 28px; + text-transform: none; + position: absolute; + content: "≡"; + text-indent: 0; + text-align: center; + line-height: 55px; + speak: none; + width: 100%; + top: 0; + left: 0; +} + +.nav-toggle.active::before { + font-size: 24px; + content:"x"; +} diff --git a/package.json b/package.json index a655078..8e3ff65 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "responsive-nav", - "version": "1.0.32", + "version": "1.0.33", "description": "Responsive Nav JS plugin", "main": "index.js", "scripts": { diff --git a/responsive-nav.css b/responsive-nav.css index d0ac2e2..f44fb7b 100644 --- a/responsive-nav.css +++ b/responsive-nav.css @@ -41,13 +41,13 @@ } @media screen and (min-width: 40em) { - .js .nav-collapse { + .js.nav-multiple-layouts .nav-collapse { position: relative; } - .js .nav-collapse.closed { + .js.nav-multiple-layouts .nav-collapse.closed { max-height: none; } - .nav-toggle { + .nav-multiple-layouts .nav-toggle { display: none; } } diff --git a/responsive-nav.js b/responsive-nav.js index 6336d3c..35880a0 100644 --- a/responsive-nav.js +++ b/responsive-nav.js @@ -142,6 +142,7 @@ // Default options this.options = { + alwaysDropdown: false, animate: true, // Boolean: Use CSS3 transitions, true or false transition: 284, // Integer: Speed of the transition, in milliseconds label: "Menu", // String: Label for the navigation toggle @@ -165,6 +166,11 @@ // Adds "js" class for addClass(htmlEl, this.options.jsClass); + // Disable media query at breakpoint + if (!this.options.alwaysDropdown) { + addClass(htmlEl, "nav-multiple-layouts"); + } + // Wrapper this.wrapperEl = el.replace("#", ""); @@ -202,6 +208,7 @@ removeClass(nav, opts.navClass); removeClass(nav, opts.navClass + "-" + this.index); removeClass(htmlEl, opts.navActiveClass); + removeClass(htmlEl, "nav-multiple-layouts"); nav.removeAttribute("style"); nav.removeAttribute("aria-hidden"); diff --git a/responsive-nav.min.js b/responsive-nav.min.js index 714c165..82ac25b 100644 --- a/responsive-nav.min.js +++ b/responsive-nav.min.js @@ -1 +1 @@ -!function(a,b,c){"use strict";var d=function(d,e){var f=!!b.getComputedStyle;f||(b.getComputedStyle=function(a){return this.el=a,this.getPropertyValue=function(b){var c=/(\-([a-z]){1})/g;return"float"===b&&(b="styleFloat"),c.test(b)&&(b=b.replace(c,function(){return arguments[2].toUpperCase()})),a.currentStyle[b]?a.currentStyle[b]:null},this});var g,h,i,j,k,l,m=function(a,b,c,d){if("addEventListener"in a)try{a.addEventListener(b,c,d)}catch(e){if("object"!=typeof c||!c.handleEvent)throw e;a.addEventListener(b,function(a){c.handleEvent.call(c,a)},d)}else"attachEvent"in a&&("object"==typeof c&&c.handleEvent?a.attachEvent("on"+b,function(){c.handleEvent.call(c)}):a.attachEvent("on"+b,c))},n=function(a,b,c,d){if("removeEventListener"in a)try{a.removeEventListener(b,c,d)}catch(e){if("object"!=typeof c||!c.handleEvent)throw e;a.removeEventListener(b,function(a){c.handleEvent.call(c,a)},d)}else"detachEvent"in a&&("object"==typeof c&&c.handleEvent?a.detachEvent("on"+b,function(){c.handleEvent.call(c)}):a.detachEvent("on"+b,c))},o=function(a){if(a.children.length<1)throw new Error("The Nav container has no containing elements");for(var b=[],c=0;c10||Math.abs(a.touches[0].clientY-this.startY)>10)&&(this.touchHasMoved=!0)},_onTouchEnd:function(c){if(this._preventDefault(c),!this.touchHasMoved){if("touchend"===c.type)return this.toggle(),"after"===h.insert&&setTimeout(function(){r(a.body,"disable-pointer-events")},h.transition+300),void 0;var d=c||b.event;3!==d.which&&2!==d.button&&this.toggle()}},_onKeyUp:function(a){var c=a||b.event;13===c.keyCode&&this.toggle()},_transitions:function(){if(h.animate){var a=g.style,b="max-height "+h.transition+"ms";a.WebkitTransition=b,a.MozTransition=b,a.OTransition=b,a.transition=b}},_calcHeight:function(){for(var a=0,b=0;b10||Math.abs(a.touches[0].clientY-this.startY)>10)&&(this.touchHasMoved=!0)},_onTouchEnd:function(c){if(this._preventDefault(c),!this.touchHasMoved){if("touchend"===c.type)return this.toggle(),void("after"===h.insert&&setTimeout(function(){r(a.body,"disable-pointer-events")},h.transition+300));var d=c||b.event;3!==d.which&&2!==d.button&&this.toggle()}},_onKeyUp:function(a){var c=a||b.event;13===c.keyCode&&this.toggle()},_transitions:function(){if(h.animate){var a=g.style,b="max-height "+h.transition+"ms";a.WebkitTransition=b,a.MozTransition=b,a.OTransition=b,a.transition=b}},_calcHeight:function(){for(var a=0,b=0;b