Coder Social home page Coder Social logo

responsive-nav.js's Introduction

Responsive Nav

Responsive navigation plugin without library dependencies and with fast touch screen support.

Responsive Nav is a tiny JavaScript plugin which weighs only 1.3kb minified and Gzip’ed, and helps you to create a toggled navigation for small screens. It uses touch events and CSS3 transitions for the best possible performance. It also contains a “clever” workaround that makes it possible to transition from height: 0 to height: auto, which isn’t normally possible with CSS3 transitions.

Features:

  • Simple, semantic markup.
  • Weighs only 1.3kb minified and Gzip’ed.
  • Doesn’t require any external library.
  • Uses CSS3 transitions and touch events.
  • Supports RequireJS and multiple instances.
  • Removes the 300ms delay between a physical tap and the click event.
  • Makes it possible to use CSS3 transitions with height: auto.
  • Built with accessibility in mind, meaning that everything works on screen readers and with JavaScript disabled, too.
  • Works in all major desktop and mobile browsers, including IE 6 and up.
  • Free to use under the MIT license.

Demos

Usage instructions

Following the steps below you will be able to get the plugin up and running. If you notice any bugs, please post them to GitHub issues.

  1. Link files:

    	<!-- Put these into the <head> -->
    	<link rel="stylesheet" href="responsive-nav.css">
    	<script src="responsive-nav.js"></script>
  2. Add markup:

    	<nav class="nav-collapse">
    		<ul>
    			<li><a href="#">Home</a></li>
    			<li><a href="#">About</a></li>
    			<li><a href="#">Projects</a></li>
    			<li><a href="#">Contact</a></li>
    	 	</ul>
    	</nav>
  3. Hook up the plugin:

    <!-- Put this right before the </body> closing tag -->
    	<script>
    		var nav = responsiveNav(".nav-collapse");
    	</script>
  4. Customizable options:

    	var nav = responsiveNav(".nav-collapse", { // Selector
    		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
    		insert: "before", // String: Insert the toggle before or after the navigation
    		customToggle: "", // Selector: Specify the ID of a custom toggle
    		closeOnNavClick: false, // Boolean: Close the navigation when one of the links are clicked
    		openPos: "relative", // String: Position of the opened nav, relative or static
    		navClass: "nav-collapse", // String: Default CSS class. If changed, you need to edit the CSS too!
    		navActiveClass: "js-nav-active", // String: Class that is added to <html> element when nav is active
    		jsClass: "js", // String: 'JS enabled' class which is added to <html> element
    		init: function(){}, // Function: Init callback
    		open: function(){}, // Function: Open callback
    		close: function(){} // Function: Close callback
    	});

Public methods

See the example code here for the usage.

nav.toggle();

nav.open();

nav.close();

nav.destroy();

nav.resize();

Changing the breakpoint

Breakpoint is defined in the responsive-nav.css file. Responsive Nav checks on window resize and on orientation change if the navigation toggle has display: none; and based on that switches between mobile and desktop states.

Supporting old IEs

Even though Responsive Nav works even on IE6, you should remember that IE8 and under do not support media queries and thus can’t change between "small screen" and "large screen" styles. If needed, you can add Media Query support for those browsers using respond.js. There’s an example here.

When old IE support is needed you should stick to using ID selector with Responsive Nav. That’s because the plugin uses getElementById method by default which is widely supported in all browsers. When using classes or element selectors querySelector will be used instead which isn’t supported in old IEs.

Things to keep in mind

Calculated Max-height doesn't account for top/bottom padding on .nav-collapse (this is on purpose). If you need to add padding inside the nav, you can apply it to any other element, for example the <ul> inside .nav-collapse.

Tested on the following platforms

  • iOS 4.2.1+
  • Android 1.6+
  • Windows Phone 7.5+
  • Blackberry 7.0+
  • Blackberry Tablet 2.0+
  • Jolla 1.0+
  • Kindle 3.3+
  • Maemo 5.0+
  • Meego 1.2+
  • Symbian 3
  • Symbian Belle
  • Symbian S40 Asha
  • webOS 2.0+
  • Windows XP+
  • Mac OS X

Working on the repository

GruntJS is used for the build process, which means node and npm are required. If you already have those on your machine, you can install Grunt and all dependencies required for the build using:

npm install -g grunt-cli
npm install

Starting the server

python -m SimpleHTTPServer 8000

Git Hooks

It is useful to setup a pre-commit and post-checkout hooks to smooth your workflow. On pre-commit we want to ensure that the project can build successfully, and on post-checkout we want to ensure that any new dependencies are installed via npm.

Pre-Commit

touch .git/hooks/pre-commit && echo -e '#!/bin/sh\ngrunt test' > .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit

Post-Checkout

touch .git/hooks/post-checkout && echo -e '#!/bin/sh\nnpm install\nexit 0' > .git/hooks/post-checkout && chmod +x .git/hooks/post-checkout

Building The Project

To build the project, run unit tests etc. enter the following at the terminal:

grunt

Grunt can also be used to monitor files and re-build the project on each change. For this we use Grunt's watch task:

grunt watch

Next time you change the file, Grunt will perform all build tasks.

Testing

The test suite can be run with grunt test and is also part of the default Grunt task. This command runs all tests locally using PhantomJS.

Running on multiple devices/browsers

It's possible to run the test suite on multiple devices with Karma.

The Karma server can be started with grunt karma and multiple browsers should then point to the machine running the server on port 9876 (e.g. http://localhost:9876). Once the browsers are connected, the test suite can be run with grunt karma:all:run.

An easier way to test on multiple devices as part of the development cycle is to use karma with the watch task. Running grunt karma watch will automatically start the Karma server in the background and will run the tests automatically every time a file changes, on every connected device.

Special thanks

In random order:

License

Licensed under the MIT license.

Copyright (c) 2012-2023 Ariel Salminen, https://arie.ls/

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:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

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.

Changelog

1.0.39 (2015-04-09) - Adds Browserify support.

1.0.38 (2015-04-02) - Fixes custom-toggle demo, adds feature detection for event.stopImmediatePropagation, removes all pointer-event hacks completely and also fixes an issue which caused multiple taps to sometimes freeze the nav completely.

1.0.34 (2014-12-16) - Fixes versioning.

1.0.33 (2014-12-15) - "closeOnNavClick" now works on old IEs too, so no more features that only work in modern browsers. Fixes a bug which caused the navigation to sometimes not toggle. Also fixes a bug in Safari that sometimes caused the navigation render incorrectly when switching between browser tabs. (+Adds more comments to the code.)

1.0.32 (2014-03-05) - Ditching the [].forEach.call(NodeList) hack to make the code more sensible and future-proof.

1.0.31 (2014-03-02) - Fixes Chrome Mobile rendering issue.

1.0.30 (2014-03-02) - Better performance. New fixed navigation example provided by Adtile. This release fixes an issue where multiple navigations on the same page got the same calculated height. Toggle now has an "active" class when the nav is open. Two new options are added: "closeOnNavClick" and "navActiveClass". Two new methods are also added: "open" and "close" (thanks to @munkius for the heads up on this!). This release adds also better "hamburger" icon for advanced demos which you can now style via css (size, color, shadow & etc). Includes also other bug fixes targeting older Android devices.

1.0.25 (2013-12-13) - Fixes ghost click issues on Android + a problem where calculated max-height might get overridden under certain circumstances.

1.0.24 (2013-11-27) - Adds new option called "navClass." All tests should also work now on real iOS, Windows Phone and Android devices (when using grunt-karma), and not just with PhantomJS.

1.0.23 (2013-09-25) - Fixes IE8 bugs + starts using automated builds and tests.

1.0.22 (2013-09-19) - Public resize method (to allow calling resize manually when needed).

1.0.21 (2013-09-18) - Multiple instances are now possible thanks to @toomuchdesign. Uses classes instead of ID's now by default, but can be configured to use ID's if old IE support is needed (check the "ie-support" folder in demos).

1.0.20 (2013-08-12) - Uses now touchmove & touchend, which means that the menu doesn’t trigger anymore if the user starts moving finger instead of just tapping. Also fixes one Android bug and a bug which appeared when tapping the toggle really fast over and over. Plugin’s Functionality doesn’t depent on window load event anymore so it works now with tools like require.js too.

1.0.16 (2013-08-02) - Set navOpen state in the _init method. Thanks @nicolashery!

1.0.15 (2013-06-28) - Responsive Nav now automatically combines multiple navigations inside a container.

1.0.14 (2013-04-13) - Adds touchend listener and stopProganation + prevents ghost click from happening on some Android browsers. "tabIndex" and "debug" settings are being removed.

v1.11 (2013-04-09) - Performance optimization, bug fixes and 6 additional usage examples

v1.07 (2013-04-03) - Simplifies the codebase and fixes few bugs

v1.05 (2013-03-31) - Adds callback functionality and removes unnecessary CSS.

v1.03 (2013-03-28) - Adds option to disable CSS3 transitions + three other options called "tabIndex", "openPos" and "jsClass".

v1.00 (2013-03-25) - Release. Big thank you’s for the help go out to @cubiq, @stowball, @jcxplorer and @vesan!

Want to do a pull request?

Great! New ideas are more than welcome, but please check the Pull Request Guidelines first before doing so.

responsive-nav.js's People

Contributors

arielsalminen avatar benschwarz avatar bez4pieci avatar cappslock avatar dboulet avatar fritx avatar jakegiltsoff avatar jcxplorer avatar kkirsche avatar matiaskorhonen avatar nc avatar nicolashery avatar nvartolomei avatar philwareham avatar stowball avatar vesan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

responsive-nav.js's Issues

Menu disappearing in IE8, appears on resize

In IE8 the menu disappears and only shows if you re-size your browser (even by 1-2 pixels). The menu shows if the page is loaded or sized below the break-point. But essentially in IE8 at anything above the break-point the menu isn't initially there until you re-size.

I have uploaded the demo files direct to my site. I haven't changed anything in these.
http://jamesrobertedward.com/responsive-nav.js-master/demos/ie-support-using-respondjs/

Note: seems to be fine in IE7 and 10. I don't have a copy of IE9 to check.

Thanks

Scroll to top on close event

When I open the menu, scroll down just a little bit and then close the menu the browser stays in the same position – it doesn't scroll along with the closing menu. Is this behavior a bug or a feature?

I made an attempt to fix this on my site as simple as possible but it seems a bit ugly:

var navigation = responsiveNav("#nav", { customToggle: "#toggle", close: function() { setTimeout( function () { $('html,body').animate({scrollTop: $("a[name='toggle']").offset().top},'slow'); }, 400); } });

How can I get the browser to scroll along with the closing menu?

Thanks for a great responsive menu script!

position: fixed on the navigation bar?

I can not seem to make the navigation bar stay fixed at the top. I have tried adding

    position: fixed;
    top: 0;
    left: 0;

But it does not seem to work. Any ideas how can I get this working?

A flash upon load

I'm experiencing a slight flash of the fully expanded nav when loading the page. It hides very quickly, but it's still quite noticeable. Is there anything I can do to the CSS to prevent this? Have I made an implementation mistake?
I really appreciate the plugin, btw. Thank you tons.

Please some better docs.

There are lots of inconstancies in the demo files. Especially the CSS. Sometimes its mobile-first sometimes not. There are duplicate entries for the same CSS selector in the same media query. Sometimes responsive.css is used sometimes not. There are very little comments and it's not clear what's going on.

Anyway, my point is that the examples need to be a lot cleaner. Very hard to follow and understand how to implement. But this is really just encouraging feedback. I am an intermediate CSS guy, so there are probably some obvious things that I don't catch. I will try to pinpoint some of things and send you specific examples. Thanks.

Tabindex necessary?

Love the plugin! Really useful and I'll probably use it on about 90% of all my websites.

I had questions/concerns about wondering setting the TabIndex is necessary and possibly too aggressive from an accessibility standpoint. Most accessibility blogs tend to recommend against overriding TabIndex order unless truly necessary, and those cases are typically rare.

Anyhow, just wanted to start a discussion because I see this being used on lots of websites. Good work!

Hamburger Icon rotates on desktop but doesn‘t on mobile

First things first: Thanks all who are involved in this great piece of software!

I added a neat little gimmick to the responsive nav menu, when you click on the hamburger menu icon it will rotates while the menu expands and rotates back when the menu collapse. This works very well on desktop but not on mobile.

I`m very new to jQuery, so may someone help me on this:

http://andalusi.com/patriciawinter.de/Version2/index.html

(You have to resize your browser window in order to see the mobile menu)

Best,
andalusi.

Navigation element disappears, then reappears after page load

Hi There
I can't exactly work out what's going on and I was hoping I could get some help.

On this site in development here:
http://kittysitting.com.au/contact-us-for-cat-sitting-brisbane-northside/

On every page load, the navigation disappears, then re-appears after page load.

If I disable the responsive nav script it works again...so that seems like the culprit.

What i can't work out is why its doing it - I must have changed a style somewhere, but the styles are fine without the js script running?

Any hints?

Thanks

`bower install` fails to parse json.

I get the following error when trying to install into a little web project using Bower:

$ bower install responsive-nav --save
bower cloning git://github.com/viljamis/responsive-nav.js.git
bower caching git://github.com/viljamis/responsive-nav.js.git
bower fetching responsive-nav
bower checking out responsive-nav#1.0.14
bower error Failed to parse json
Unexpected token ]

There were errors, here's a summary of them:
- responsive-nav Failed to parse json
Unexpected token ]
An error was caught when reading the bower.json: Failed to parse json
Unexpected token ]

I tried removing the trailing comma here:

{
  "name": "responsive-js",
  "version": "1.0.14",
  "main": [
    "responsive-nav.css",
    "responsive-nav.js",      (REMOVED THIS COMMA)
  ]
}

With the comma removed it seems to be valid json and runs through http://jsonlint.com/ fine but Bower stills gives me a json parse error. Any ideas, I'm a n00b at this stuff?

Environment:
Node: v0.8.20 (on FreeBSD 9.1 Stable)
NPM: 1.2.19
Bower: 0.9.2

.js #nav overflow:hidden alternative?

Sweet plugin!!

I pulled it down and am playing around with it. However, I'm using a dropdown inside the .js #nav div and the overflow:hidden hides it. I remove it and the animation gets really wonky. Any way you can think of to not use overflow:hidden on that element and have the animations still look sexy?

How to link to real full paths in navigation.

I don't know if I'm missing out something real simple but I couldn't manage using the responsive nav for other links than #, is there a workaround?
I find it perfect otherwise!

Thanks you very much!

Geoffrey

Triggering navigation.toggle(); without animation / Offset scroll issue

First i want to say this is a really nice plugin. Light and fast!
This issue is not really a problem with the plugin itself, but i hope someone can point me in the right direction.

In my menu, i have added links that scroll to different parts of the same page (a typical jump to #id function). I have also added a click function that toggles/closes the menu (with nav animating) when clicking on one of the list items.

The problem occurs when the menu closes (animates) at the same time the page scrolls to its id.

What happens is that the page scrolls past its target, approximately the height of the nav.
This problem does not occur when i set the animate to false, as the nav probably closes so fast that the scroll target does not include its height.

I have thought of two options to solve my problem, either disable nav animation only when clicking a nav link/scrolling, or some how get the offset of the nav height.
I am not very advanced at javascript, so i hope that someone can help me with this.

To explain, here is my code:

html:

    <div id="nav">
        <ul>
            <li><a href="#">Home</a></li>
            <li><a href="#">About</a></li>
            <li><a href="#projects">Projects</a></li>
            <li><a href="#">Blog</a></li>
        </ul>
    </div>

    <div role="main" class="main">
        <h1>Home</h1>
        <p>Content</p>
        <p>Content</p>
        <p>Content</p>
        <h1>About</h1>
        <p>Content</p>
        <p>Content</p>
        <p>Content</p>
        <h1 id="projects">Projects start here</h1>
    </div>

js:

    var navigation = responsiveNav("#nav", {
        animate: true, 
        transition: 400,      
    });

    $(document).ready(function() {
        $("#nav a").click(function () {
            // How do i set the function below to "animate: false"
            navigation.toggle(); // Close nav when jumping to target
        });
    });

Any help would be appreciated :)

Minified version doesn't support debug function

Hi,

When I use the minified version (responsive-nav.min.js) of your script and I use debug: false or debug: true, your responsive navigation doesn't work.

If I delete this debug parameter line in my options, than your script works fine.

Transition method

Hey guys,

Great work on this guy, thanks heaps for the contribution. Out of curiosity, has using transform: translate3d... instead of the current transition property been considered?

I'm seeing a wee bit of frame refresh issues on mobile, and immediately thought of Jordan Moore's example of using the translate in his Top Drawer experiment. From my machines, it seems that it produced better results.

Again, thanks for the contribution. It's a great solution you guys have come up with. I salute you.

Close on click

First: Thanks. This is awesome.

I'm hoping there's a good and easy way to get the menu to toggle closed when you click li item. My guess is that that's what the public methods are for, but, that's a guess, and as far as it goes.

Many thanks again,
-m

Left & Right menus with touch/swipe support and tap content to close

Firstly, this is really great - it has a very nice fluid 'feel' it.. theres a few additions that would be good to consider:

  • Option for the menu to slide in from the left and/or right side of the screen
  • Touch/swipe support to open and close with left/right swipe
  • Ability to click anywhere in the 'content' area to close the menu

I know these things are covered in some other RWD menu plugins but none have your attention to the feel and details!

Add .gitignore

It would be handy to add a .gitignore to the repo, with an ignore for .DS_Store.

AFAIK .inner only ever returns the first element so loop is redundant

In _calcHeight(), the code loops through nav.inner.length to increase savedHeight.

However, regardless of how many direct children exist in nav.inner's length will always be 1 (tested in FF, Chrome & IE 10).

You might want to change the code to the following to reduce size and improve performance.

    _calcHeight: function () {
      var innerStyles = "#" + this.wrapperEl + ".opened{max-height:" + nav.inner[0].offsetHeight + "px}";

      // Hide from old IE
      if (computed) {
        styleElement.innerHTML = innerStyles;
        innerStyles = "";
      }
    },

Nav-toggle active class

An enhancement to the nav-toggle would have a toggled class for when the toggle is active similar to the opened class on the #nav container

Nav displaying improperly in IE 8

Working great in Chrome, FF, IE 9/10 but in IE8 the Nav is collapsed to the hamburger gif and showing as Mobile Navigation on Desktop display. How can I correct this?

Expanding the Browser Window

If you open the menu in a narrow browser window and then drag the side of the browser window to make it larger than the media query breakpoint for the collapsed menu, there is this very noticable bounceback effect that is annoying and takes away from the smoothness of the solution (this occurs in the advanced demo). Thanks!

Can I add sub navigation

Hello Sir

Could you please let me know how can I add sub navigation in the left menu like under "About us" tab I have more 3 links which I have to add it in the same. can we setup this menu like vertical drop down list,

If possible could you please tell me How I can implement second level navigation.

Thanks in Advance

Best Regards
Pinakin Patel

Reason for double up of media query in advanced css demo?

@media screen and (min-width: 40em) {
  #nav a {
    margin: 0;
    padding: 1em;
    float: left;
    text-align: center;
    border-bottom: 0;
    border-right: 1px solid white;
  }
}

#nav ul ul a {
  background: #ca3716;
  padding-left: 2em;
  display: none;
}

@media screen and (min-width: 40em) {
  #nav ul ul a {
    display: none;
  }
}

Why not this?

  #nav a {
    margin: 0;
    padding: 1em;
    float: left;
    text-align: center;
    border-bottom: 0;
    border-right: 1px solid white;
  }
}

#nav ul ul a {
  background: #ca3716;
  padding-left: 2em;
  display: none;
}

My toggle link only works the second time

Hi, My toggle button only works the second time, can someone help me fix this issue.

/*! responsive-nav.js v1.0.13

/* jshint strict:false, forin:false, noarg:true, noempty:true, eqeqeq:true,
boss:true, bitwise:true, browser:true, devel:true, indent:2 /
/
exported responsiveNav */

var responsiveNav = (function (window, document) {

var computed = !!window.getComputedStyle;

// getComputedStyle polyfill
if (!window.getComputedStyle) {
window.getComputedStyle = function(el) {
this.el = el;
this.getPropertyValue = function(prop) {
var re = /(-([a-z]){1})/g;
if (prop === "float") {
prop = "styleFloat";
}
if (re.test(prop)) {
prop = prop.replace(re, function () {
return arguments[2].toUpperCase();
});
}
return el.currentStyle[prop] ? el.currentStyle[prop] : null;
};
return this;
};
}

var nav,
opts,
navToggle,
docEl = document.documentElement,
head = document.getElementsByTagName("head")[0],
styleElement = document.createElement("style"),
navOpen = true,

// fn arg can be an object or a function, thanks to handleEvent
// read more at: http://www.thecssninja.com/javascript/handleevent
addEvent = function (el, evt, fn, bubble) {
  if ("addEventListener" in el) {
    // BBOS6 doesn't support handleEvent, catch and polyfill
    try {
      el.addEventListener(evt, fn, bubble);
    } catch (e) {
      if (typeof fn === "object" && fn.handleEvent) {
        el.addEventListener(evt, function (e) {
          // Bind fn as this and set first arg as event object
          fn.handleEvent.call(fn, e);
        }, bubble);
      } else {
        throw e;
      }
    }
  } else if ("attachEvent" in el) {
    // check if the callback is an object and contains handleEvent
    if (typeof fn === "object" && fn.handleEvent) {
      el.attachEvent("on" + evt, function () {
        // Bind fn as this
        fn.handleEvent.call(fn);
      });
    } else {
      el.attachEvent("on" + evt, fn);
    }
  }
},

removeEvent = function (el, evt, fn, bubble) {
  if ("removeEventListener" in el) {
    try {
      el.removeEventListener(evt, fn, bubble);
    } catch (e) {
      if (typeof fn === "object" && fn.handleEvent) {
        el.removeEventListener(evt, function (e) {
          fn.handleEvent.call(fn, e);
        }, bubble);
      } else {
        throw e;
      }
    }
  } else if ("detachEvent" in el) {
    if (typeof fn === "object" && fn.handleEvent) {
      el.detachEvent("on" + evt, function () {
        fn.handleEvent.call(fn);
      });
    } else {
      el.detachEvent("on" + evt, fn);
    }
  }
},

getFirstChild = function (e) {
  var firstChild = e.firstChild;
  // skip TextNodes
  while (firstChild !== null && firstChild.nodeType !== 1) {
    firstChild = firstChild.nextSibling;
  }
  return firstChild;
},

setAttributes = function (el, attrs) {
  for (var key in attrs) {
    el.setAttribute(key, attrs[key]);
  }
},

addClass = function (el, cls) {
  el.className += " " + cls;
  el.className = el.className.replace(/(^\s*)|(\s*$)/g,"");
},

removeClass = function (el, cls) {
  var reg = new RegExp("(\\s|^)" + cls + "(\\s|$)");
  el.className = el.className.replace(reg, " ").replace(/(^\s*)|(\s*$)/g,"");
},

log = function () {},

ResponsiveNav = function (el, options) {
  var i;

  // Default options
  this.options = {
    animate: true,        // Boolean: Use CSS3 transitions, true or false
    transition: 400,      // Integer: Speed of the transition, in milliseconds
    label: "Menu",        // String: Label for the navigation toggle
    insert: "before",      // String: Insert the toggle before or after the navigation
    customToggle: "",     // Selector: Specify the ID of a custom toggle
    openPos: "relative",  // String: Position of the opened nav, relative or static
    jsClass: "js",        // String: 'JS enabled' class which is added to <html> el
    debug: false,         // Boolean: Log debug messages to console, true or false
    init: function(){},   // Function: Init callback
    open: function(){},   // Function: Open callback
    close: function(){}   // Function: Close callback
  };

  // User defined options
  for (i in options) {
    if (i in this.options) {
      this.options[i] = options[i];
    } else {
      throw new Error("Responsive Nav doesn't support option: " + i);
    }
  }

  // Adds "js" class for <html>
  addClass(docEl, this.options.jsClass);

  // Debug logger
  if (this.options.debug) {
    log = function (s) {
      try {
        console.log(s);
      } catch (e) {
        alert(s);
      }
    };
  }

  // Wrapper
  this.wrapperEl = el.replace("#", "");
  if (document.getElementById(this.wrapperEl)) {
    this.wrapper = document.getElementById(this.wrapperEl);
  } else {
    // If el doesn't exists, stop here.
    throw new Error("The nav element you are trying to select doesn't exist");
  }

  // Inner wrapper
  this.wrapper.inner = getFirstChild(this.wrapper);

  // For minification
  opts = this.options;
  nav = this.wrapper;

  // Init
  this._init(this);
};

ResponsiveNav.prototype = {

// Public methods
destroy: function () {
  this._removeStyles();
  removeClass(nav, "closed");
  removeClass(nav, "opened");
  nav.removeAttribute("style");
  nav.removeAttribute("aria-hidden");
  nav = null;
  _instance = null;

  removeEvent(window, "load", this, false);
  removeEvent(window, "resize", this, false);
  removeEvent(navToggle, "mousedown", this, false);
  removeEvent(navToggle, "touchstart", this, false);
  removeEvent(navToggle, "touchend", this, false);
  removeEvent(navToggle, "keyup", this, false);
  removeEvent(navToggle, "click", this, false);

  if (!opts.customToggle) {
    navToggle.parentNode.removeChild(navToggle);
  } else {
    navToggle.removeAttribute("aria-hidden");
  }

  log("Destroyed!");
},

toggle: function () {
  if (!navOpen) {
    removeClass(nav, "closed");
    addClass(nav, "opened");
    nav.style.position = opts.openPos;
    setAttributes(nav, {"aria-hidden": "false"});

    navOpen = true;
    opts.open();
    log("Opened nav");

  } else {
    removeClass(nav, "opened");
    addClass(nav, "closed");
    setAttributes(nav, {"aria-hidden": "true"});

    if (opts.animate) {
      setTimeout(function () {
        nav.style.position = "absolute";
      }, opts.transition + 10);
    } else {
      nav.style.position = "absolute";
    }

    navOpen = false;
    opts.close();
    log("Closed nav");
  }
},

handleEvent: function (e) {
  var evt = e || window.event;

  switch (evt.type) {
  case "mousedown":
    this._onmousedown(evt);
    break;
  case "touchstart":
    this._ontouchstart(evt);
    break;
  case "touchend":
    this._ontouchend(evt);
    break;
  case "keyup":
    this._onkeyup(evt);
    break;
  case "click":
    this._onclick(evt);
    break;
  case "load":
    this._transitions(evt);
    this._resize(evt);
    break;
  case "resize":
    this._resize(evt);
    break;
  }
},

// Private methods
_init: function () {
  log("Inited Responsive Nav");
  addClass(nav, "closed");
  this._createToggle();

  addEvent(window, "load", this, false);
  addEvent(window, "resize", this, false);
  addEvent(navToggle, "mousedown", this, false);
  addEvent(navToggle, "touchstart", this, false);
  addEvent(navToggle, "touchend", this, false);
  addEvent(navToggle, "keyup", this, false);
  addEvent(navToggle, "click", this, false);
},

_createStyles: function () {
  if (!styleElement.parentNode) {
    head.appendChild(styleElement);
    log("Created 'styleElement' to <head>");
  }
},

_removeStyles: function () {
  if (styleElement.parentNode) {
    styleElement.parentNode.removeChild(styleElement);
    log("Removed 'styleElement' from <head>");
  }
},

_createToggle: function () {
  if (!opts.customToggle) {
    var toggle = document.createElement("a");
    toggle.innerHTML = opts.label;
    setAttributes(toggle, {
      "href": "#",
      "id": "nav-toggle"
    });

    if (opts.insert === "after") {
      nav.parentNode.insertBefore(toggle, nav.nextSibling);
    } else {
      nav.parentNode.insertBefore(toggle, nav);
    }

    navToggle = document.getElementById("nav-toggle");
    log("Default nav toggle created");

  } else {
    var toggleEl = opts.customToggle.replace("#", "");

    if (document.getElementById(toggleEl)) {
      navToggle = document.getElementById(toggleEl);
      log("Custom nav toggle created");
    } else {
      throw new Error("The custom nav toggle you are trying to select doesn't exist");
    }
  }
},

_preventDefault: function(e) {
  if (e.preventDefault) {
    e.preventDefault();
    e.stopPropagation();
  } else {
    e.returnValue = false;
  }
},

_onmousedown: function (e) {
  var evt = e || window.event;
  // If the user isn't right clicking:
  if (!(evt.which === 3 || evt.button === 2)) {
    this._preventDefault(e);
    this.toggle(e);
  }
},

_ontouchstart: function (e) {
  // Touchstart event fires before
  // the mousedown and can wipe it
  navToggle.onmousedown = null;
  this._preventDefault(e);
  this.toggle(e);
},

_ontouchend: function () {
  // Prevents ghost click from happening on some Android browsers
  var that = this;
  nav.addEventListener("click", that._preventDefault, true);
  setTimeout(function () {
    nav.removeEventListener("click", that._preventDefault, true);
  }, opts.transition);
},

_onkeyup: function (e) {
  var evt = e || window.event;
  if (evt.keyCode === 13) {
    this.toggle(e);
  }
},

_onclick: function (e) {
  // For older browsers (looking at IE)
  this._preventDefault(e);
},

_transitions: function () {
  if (opts.animate) {
    var objStyle = nav.style,
      transition = "max-height " + opts.transition + "ms";

    objStyle.WebkitTransition = transition;
    objStyle.MozTransition = transition;
    objStyle.OTransition = transition;
    objStyle.transition = transition;
  }
},

_calcHeight: function () {
  var savedHeight = nav.inner.offsetHeight,
    innerStyles = "#" + this.wrapperEl + ".opened{max-height:" + savedHeight + "px}";

  // Hide from old IE
  if (computed) {
    styleElement.innerHTML = innerStyles;
    innerStyles = "";
  }

  log("Calculated max-height of " + savedHeight + "px and updated 'styleElement'");
},

_resize: function () {
  if (window.getComputedStyle(navToggle, null).getPropertyValue("display") !== "none") {
    setAttributes(navToggle, {"aria-hidden": "false"});

    // If the navigation is hidden
    if (nav.className.match(/(^|\s)closed(\s|$)/)) {
      setAttributes(nav, {"aria-hidden": "true"});
      nav.style.position = "absolute";
    }

    this._createStyles();
    this._calcHeight();
  } else {
    setAttributes(navToggle, {"aria-hidden": "true"});
    setAttributes(nav, {"aria-hidden": "false"});
    nav.style.position = opts.openPos;
    this._removeStyles();
  }

  // Init callback
  opts.init();
}

};

var _instance;
function rn (el, options) {
if (!_instance) {
_instance = new ResponsiveNav(el, options);
}
return _instance;
}

return rn;
})(window, document);

Thanks

computed var is not immediatetly used in polyfill

The code checks whether window.getComputedStyle is supported and stores the value in computed.

But then immediately after, the polyfill again checks for !window.getComputedStyle.

You should use the computed var as it will minify smaller, like so:

  var computed = !!window.getComputedStyle;

  // getComputedStyle polyfill
  if (!computed) {

#nav disappears after 2 clicks until window resize

After clicking on the links when in "Desktop Mode", when the window is > 40em wide, the navigation will disappear after 2 clicks until you resize the window horizontally or vertically after that.
This only happened after I added:

$("#nav a").click(function() {
navigation.toggle();
});

This does not happen when the window is < 40 em.

I'm using jQuery v1.4.4 and tested in Chrome v28, IE9, and Firefox.

Any ideas?

Remove error throwing when passing wrong options.

I think it's not so good idea to throw errors when wrong options are passed, or at least
it's not good idea to throw an error and to break script, just console log a notice/warning.

Also this will protect user who updates responsive-nav.js in case api changes.
For example after debug option was removed it broke, after tabIndex option was removed
it broke again.

By the way, does anyone else do this?

Transition Effect not working in Firefox

See this instance: dev.citylightphilly.com - view at < 1024px wide and click the menu.

The transition effect works great in Safari / Chrome but not in Firefox.

Minimize size of .min.js

You could remove log function and calls from minimized version of script, you could save some bytes.

Responsive nav loading ahead of tabs.

I have an issue that's appearing in every version of IE before 9, but only for the initial load of a page.

Essentially, the collapsed/closed navigation loads and appears for a second, but then it disappears and doesn't even show the default navigation before/after. It seems as if IE7 and 8 are triggering the media query and thus the responsive nav early, but then immediately loading the query to hide it.

The strange part is that this only happens on the initial load. The navigation appears just fine upon refresh and/or resizing the browser. Otherwise, the nav functionality works great and does what I want once it appears.

Any and all help is much appreciated.

Do not depend on the window load event

Responsive Nav depends on the window load event to be fired to initialize the transitions and resize:

case "load":
  this._transitions(evt);
  this._resize(evt);

In many cases, one might want to delay intializing Responsive Nav to a point where the page load event has already been fired and as a result, Responsive Nav has missed the window.

For instance, this happens when working with RequireJS to load JavaScript.

One fix is to call the two methods manually:

var navigation = responsiveNav("#nav");

navigation._transitions();
navigation._resize();

I suggest a public method to notify Responsive Nav that everything has been loaded.

How to make the transparent hamburger gif?

I really like the way you go with the transparent hamburger gif.
The benefit is that you can easily change the color via css.

But I don`t know how to achieve this with photoshop.
Please help!

Thanks

Great plugin!

No jquery, inserts toggle with js, etc

Thank you very much!!

Multiple Menus

I have two menus in my navigational area. One for languages and one for the main navigation, which are different lists. How do I set up this as one responsive Nav?

Touch event with scrolling

As always, stellar work on this. One issue so far:

On touchscreens (tested on safari/chrome on iOS6), the menu toggle event fires as soon as your finger touches the toggle, not when it's lifted after a tap. So it essentially intercepts scrolling and turns the toggle into a dead area on the screen.

Cheers.

How to install on wordpress?

I'm new to HTML but still know the basics. I recently created a website on wordpress.

Just looking for some help on how to nstall Responsive Nav onto your wordpress site. I already have a resposive dropdown navigation but I would much rather have this one instead.

Thank you!

Multiple instances not supported?

It doesn't seem that multiple instances of the menu on the same page are supported, due to _instance being set by the first use of the function, and thus will not produce any more instances after the first.

Is there a reason why the code limits usage to one instance per page load?

Problems with HTML5

Hi,

When I use special semantic HTML5 tags instead of div markup for my navigation, i.e.:

<nav id="main">
<ul>
<li>Page 1</li>
<li>Page 2</li>
</ul>
</nav>

Following doesn't work:

<script>var navigation = responsiveNav("nav#main");</script>

But this work:

<script>var navigation = responsiveNav("#main");</script>

Sit navigation showing as a link on widescreen

IM trying to use theresponsive nav js plug in. I have 2 issues. site navigation is coming up as a link in widescreen and i can't get my menu to toggle on and off/ up down. It seems to be moving sideways
any help would be aprreciated

transition issue with screen resize and download link broken.

hey
when you resize ur browser and expand menu and again resize to full size, the normal menu tend to transition up as if you just opened it. simple and advanced demo has this effect.
the main site has no unnecessary transition effect.
issue noticed while using chrome.

and main site has its download link broken.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.