Ignored By Dinosaurs 🦕

ui

That unwanted thing that Google put in your top menu bar? That bothers you, too? Can't find where to turn it off? That's because they wanted to hide it from you.

  1. chrome://flags – put that in your address bar.
  2. Search for 'notifi', as in 'notifications' (which is what it's called right now).
  3. DISABLE

#ui

Dropdown menus may seem like something that falls under the “solved problem” category, but they can be surprisingly tricky. Tutorials that you find online will usually walk you through a very simple example that assumes markup that you never have. This will not be that. We're going to talk about the theory behind building a drop down so that you can better reason your way through the mess of markup that you're given.

If you're working with Drupal and your requirements are outside the scope of what Nice Menus can give you (which happens as soon as you hear the word “responsive”), this tutorial is for you.

[!note] Be advised that some parent themes do not render the submenus even if you set the parents to “expanded” in the menu admin. I'm not sure what the logic is for that, but it's a feature you should be aware of in some base themes.


Beginning

Your menu markup is going to look something like this —

* Item 1
* Item 2
* Item 3
	+ Sub-item 1
	+ Sub-item 2
	+ Sub-item 3
* Last item

Out of the box that'll render something like this

  • Item 1
  • Item 2
  • Item 3
    • Sub-item 1
    • Sub-item 2
    • Sub-item 3
  • Last item

If you are working with Drupal, you're going to have to dig through a lot of layers of wrapper divs, and there will be a lot more classes added to each item, but the general structure is the same. One early gotcha is that all the submenu uls are also given a class of .menu, which is annoying at best.

* Item 1
* Item 2
* Item 3
	+ Sub-item 1
	+ Sub-item 2
	+ Sub-item 3
* Last item

Ignoring all that, the general idea is to hide the submenu ul, get all the top level items to line up next to each other, and show the submenus when you hover over a parent item. How?

ul {
	float: left;
	margin: 0;
	padding: 0;
}
ul li {
	position: relative;
	float: left;
	list-style: none;
	list-style-image: none; /\* DRUPAL!! \*/
	padding: .5em 1em;
	margin: 0;
}
ul li ul {
	display: none;
	position: absolute;
	width: 10em;
	left: 0;
	top: 1em;
}

ul li:hover ul {
	display: block;
}

JSBin here

Play by play

I'll assume that the left floating stuff is understandable. The real action with a drop down happens with the display:none; position:absolute set on the submenus and the position:relative set on the parent -s. position: relative means nothing much to the item on which it's set (unless you start adding positioning rules to it as well). It's importance here is because any child elements/nodes that are absolutely positioned inside of it will now be positioned as if they exist inside that element. Without position:relative on that item, the absolutely positioned elements inside of it will position themselves relative to the body element, or the first containing ancestor that is positioned relatively. See here for an example.

As an aside, these two ALA articles are required reading if this part makes your eyes cross.

The rest of this is hopefully understandable. display: none on the submenu hides it from view, until you hover over it's parent -, at which point it gets the display property of block, which makes it show up in your browser. Since it's absolutely positioned, it'll need a width specified. You'll need something narrow enough to prevent short item from floating next to each other, but wide enough to keep longer items from breaking to too many lines.

On Superfish, Nice Menus, javascript, etc

Presumably, you might have heard of Superfish. It was the defacto JS solution to drop downs for many years, most of them in IE6/pre-jQuery era. IE6 has a (ahem) “feature” where only certain elements properly respond to the :hover pseudo-selector. That meant for a great many years that the only real solution was to patch this behavior with javascript. Fortunately, you only have to deal with this issue now if you still support IE6.

The other, definitely legitimate issue, is that using CSS only means that the instant you leave the zone of the parent item (don't forget the the parent - is wrapped around the entire submenu), your submenu will disappear. This means either judicious placement of your submenu, or utilizing some javascript to make your menu behave a bit more smoothly. Both are good solutions, imo.

Here's an updated JSBin. Note in the collapsed CSS column I've commented out this part —

ul li:hover ul {
	/*display: block;*/
}

This means we'll be hiding and showing the dropdown with javascript (jQuery in this example). I've added a class of expanded to the parent * to make selector targeting easier. Here's the full javascript -

jQuery(function($){
	var timerOut;

	$('.expanded').on('mouseover', function(){
		clearTimeout(timerOut);
		var self = this;
		setTimeout(function(){
		$('ul', self).show();
	}, 300);

	}).on('mouseout', function() {
		var self = this;
		timerOut = setTimeout(function() {
		$('ul', self).hide();
	}, 300);
	});
});

So, setTimeout returns a numeric timer id that you can use to cancel out the setTimeout callback if you need to. Since we're going to need access to one event handler's timeout in another event handler, we're going to declare the variable for the timerId outside the scope of both of them – var timerOut in the outer function.

Any time you use jQuery on(), the element that is triggering the handler is this inside the callback function (the function after 'mouseover'). We'll assign that to var self; since we're going to enter another context once we enter the setTimeout() callback. By the way, all of this gobbledygook about scope and this is THE trick to Javascript. Understand function scope in Javascript and you'll be highly paid. I'm still getting there myself.

So anyway, discounting that bit, it's very simple. When you mouseover the parent, show the submenu. When you mouseout of the parent, hide the submenu. All we're doing is adding a delay to those actions firing. The trick is cancelling that hide() call if the user decides within 300ms that they didn't mean to wander out of the submenu. That's where clearTimout() works it's magic in the mouseover function. If there is a mouseout timer still ticking, it's ID will be assigned to timerOut and it'll get cleared. If it's already hidden the submenu, no harm and no foul.

Note that if $('ul', self) looks weird, what that means is the item in the context of self is what we're trying to find. Omit the context and it implicitly becomes the whole window. Add the context and is almost the same as saying $('li.expanded ul'). I say “almost” because the second, longer example will actually grab *any* ul inside of *any* li.expanded, which is not what you want. That's why specifying the context not only shortens your code and improves performance since the whole DOM doesn't need to be searched each time, but also scopes your selector dynamically based on which element triggered the handler. I know this is total babble, and I'm sorry.

Final gotcha

Drupal's version of jQuery is so dated that on() isn't available. If you have the option of jQuery_updating to 1.7, you can enjoy the modern era. If your site breaks, as is often the case, and you're stuck with lt 1.7, you'll need to use bind() instead. It works more or less the same in these use cases, but being familiar with event delegation is another JS Jedi trick, and the one promoted by modern Javascript authors.

In closing

This got longer than I wanted, but it's not the easiest thing in the world to build the ubiquitous drop down menu. My first one took me at least a week, and I think I eventually stumbled on Nice Menus to actually get the job done. Luckily, modern browser environments are much more predictable than they used to be, so knowing how to fish on your own is much easier these days, and the taste of a fish you caught on your own is always superior to something bought at the store, right?

This post touched on the word “responsive” at the top, and I'll follow up with how to work with that. If you've come this far, you've set yourself up nicely for an easier mobile menu job without having to fight against a bunch of other people's code.

#ui #css #javascript #theory

One of the first discoveries I ever made in web development was that CSS hex values that were all the same number would always give me some shade of grey. #444 was dark, almost black. #ccc was light, perfect for form borders. Not long after I discovered that Photoshop wouldn't accept these values and would make me type in all 6 characters – #444444, #cccccc.

Sometime after that I discovered how to tap into a shade somewhere between #eee and #fff, just a barely perceptible off-white with #f4f4f4. #f9f9f9 was lighter. #efefef was darker. The jobs at hand didn't really call for a deeper cognitive understanding. Just some off white colors.


It was only much more recently that somewhere in the corners of my mind I began to unravel the science, and just this morning walking down the stairs when it hit me in the way that I now have to write about.

parseInt()

There is a javascript function called parseInt(), and its job (in case it isn't clear) is to parse integers. There are two parameters – the first being the string/number you want to parse, but the second is often overlooked. The second is the “base number” in which you wish to parse, 10 (for decimal) is the default. I've read there is a bug whereby if you pass is a string beginning with the number 0, a base of 8 is assumed. This can lead to some weird bugs if that's not what you intend, so it's considered good style to always specify what you mean, usually 10.

If you want to play with this function, pass it some hexadecimal (base 16) and see what you get back. This will let you do something like this —

 parseInt('ef', 16); // -> 239
 
 parseInt('ff', 16); // -> 255
 
 parseInt('44', 16); // -> 68
 
 parseInt('45', 16); // -> 69

Hex and Rgb

Rgb color notation is pretty simple to get your head around. It means Red, Green, Blue. The same colors that your old tube TV used to put out in those tiny little dots if you got close enough. Mix those 3 colors together in the proper proportions anbd you can pull any shade of the rainbow out. Mix them together in equal portions and you get shades of grey, ie rgb(239, 239, 239) is a lovely shade of off-white.

16 x 16 = 256; Duh.

rgb(68, 68, 68) === #444444;

I haven't really been required to think much in base 16, and the average developer probably isn't called on to think in those terms very often, but once that clicked for me, I can somewhat compute the shades of colors that I want in my head based off of thinking about basic color mixing theory and then doing some base 16 calculations in my head. Staving off Alzheimers, hopefully.

#ui #javascript #css

I've recently finished up a project here at the job that gave me a blank check as far as writing the front end code was concerned. It was among the most blissful Drupal projects I've ever worked on, as my boss did all of the Drupal stuff, and I wrote all the code. It was heaven.

So, there were a lot of requests for some cool javascript features, and rather than reaching for the plugin drawer, I decided to write most of them from scratch. The main feature pages are mostly like this. The left hand “scrollspy” navigation was ripped off from Twitter's Bootstrap UI library, and (imho) warrants it's own write up, as it's some of the coolest code I've written yet.

Most of the moving features on the site, especially the left nav when activated and the contact tab flyout thingy were written initially using jQuery to animate positioning and display properties on the DOM elements themselves. “60 fps” is the battle cry of the UI engineer this month though, so I thought I'd try a few new tricks, namely swapping out those jQuery animations for CSS transitions. Turns out this is insanely easy, requires way less code than the previous alternative, and will outperform the JS implementation any day of the week.

var ABM = window.ABM || {};
ABM.contactFlyout = {};
 
ABM.contactFlyout = (function() {
 
var $ = window.jQuery;


var cloneContacts = function() {
// Clones the contacts div, which already exists on the page,
// attaches the copy to the page elsewhere where it can be 
// persistent, and slid out with a toggle.
};
 
var attachToggle = function() {
  $flyout = $('#flyout-wrapper');
  $('#toggle', $flyout).on('click autoFlyout', function() {
    var posY = ($flyout.hasClass('open')) ? -670 : 0;
    var winWidth = $(window).width();
    $flyout.animate({
    right: posY
    }, 350);
    $flyout.toggleClass('open');
    $(this).toggleClass('open');
  });
}

var flyoutFlash = function() {
// A thing Marketing wanted where the flyout would popout if you had never
// visited this page before, determined by a cookie.
}

// positions the contact flyout
var makeSticky = function() {
// SSIA
}
 
return {
  init: function() {
    if ($('.lt-ie9').length) return;
    cloneContacts();
    attachToggle();
    setInterval(makeSticky, 250);
    var cookieRE = /flyout/;
    if (!cookieRE.test(document.cookie)) {
      flyoutFlash();
      document.cookie = 'flyout=flown';
    }
  }
}
 
})();

The initial implementation.

So, obviously, the meat of the animation is in that attachToggle() method, which is totally poorly named and I'll refactor that right after I finish writing this. But, it just animates the positioning of that whole div, totally simple, right? So the only thing that's animating the is “right” property of that div, maybe we should try a CSS transform on that instead?

Turns out all that involves is removing that animation bit so it looks like this —

var attachToggle = function() {
  $flyout = $('#flyout-wrapper');
  $('#toggle', $flyout).on('click autoFlyout', function() {
    $flyout.toggleClass('open');
    $(this).toggleClass('open');
  });
}

And then the stylesheet for that div goes from this —

#flyout-wrapper
  position: fixed
  width: 700px
  right: -670px
  top: 20px
  z-index: 3

To this —

#flyout-wrapper
  position: fixed
  width: 700px
  right: -670px
  top: 20px
  z-index: 3
  transition: right .5s
  &.open
    right: 0

Yeah. Seriously. Instead of animating whole element, try just adding a class to it – the after state – and animate the in between with CSS. I did the same thing with the left nav dropdown bit. The whole page is a lot smoother now and I'm going to rework the main nav as soon as feasible.

Edit

If you're animating the position of something, say a #page-wrap element for an off-canvas menu, and it should present in a “normal” state when not activated, you still have to specify the default positioning (ie. left: 0), or the transition will not work. Just wasted too much time figuring out why my off canvas nav wasn't working like I wanted it to.

#ui #javascript #css

If you haven't seen this yet, it's a clip taken from yesterday's press event for the new Microsoft tablet. Dude launches Internet Explorer to show us around the browsing experience, but the thing immediately locks up. It's not just slow but completely frozen, and after about 20 seconds when he realizes that he's not going to be able to demo anything else he has to go swap it out for another device to get on with the presentation. Awkward!

Anyway, there's so many things that we (the royal we) want to rip on here. But I started thinking about how many times in a week I experience something similar on my iPad. I can't remember the last time it completely froze on me, but the UI isn't as snappy as it once felt.

I started thinking – is it because the touch UI is supposed to be simulating doing something tactilely – actually touching an icon and moving it with your finger – that this is so annoying? With a computer, you have this interface – the keyboard and the mouse or whatever – that still provides a mental buffer between you and the screen. If it gets unresponsive it's definitely annoying, but not anything like tapping an icon on the screen and getting the computer equivalent of “huh? what? me?”.

I know we've only started to scratch the surface (pun intended) of alternative ways of interacting with computing devices, but seriously – responsiveness of the UIs we already have has got to be a front burner issue. You really want to blow me away Apple? Make iOS 6 not turn my 3GS into an unusable brick, thereby forcing me to upgrade an otherwise perfectly good phone.

#ui