Saturday, June 21, 2014

Sticky Nav Bars and Top-of-Page Jumpers

Ever visit a website where if you start to scroll then certain elements on a page will stick to a particular location, like a navigation bar that stays put at the top of the page? Sometimes even a link will also appear from a side of the document which allows the user to jump back to the top of the page with a single click.

Well that's what this tutorial is about. In fact the concepts I'll be discussing can actually be taken a step further and you'll be able to create other "sticky" elements on your page (like a social media link list that can slide into view).

With this walk-through I'm assuming you're somewhat familiar with jQuery and CSS, so as with pretty much all things jQuery please download the library if you want to try this for yourself. Overall this example is really easy, so even with minimal experience it should be a snap to get this working on your site.

First let's take a look at how this works. Click here to view the example and then scroll down the page to see how the nav bar locks in place; also look for the TOP link to appear on the right-hand side of the document towards the bottom (you might need to resize your browser window to make it smaller if you can't scroll).

Now let's examine the HTML markup. Overall, there's nothing really that special one has to do here, so I'm only going through the notable elements.

<div id="top"><a href="#topofpage">TOP</a></div>

As you can probably guess, the above is the link that appears when you scroll down the page a certain distance. It's pretty much safe to put this line anywhere, as in the CSS rule (shown later) it is defined with a fixed position and moved off screen. More on that later.

Next up is the nav element (if you're not using HTML5 then a div could just as easily be used) with an id of "navbar", which will be useful when we target it with jQuery (although if you just have one nav element, then you could skip the id part). Overall the menu (the unordered lists) is as one would expect most menus to be; there is also a sub-list that is basically the dropdown.

At this point, I'm getting right into the jQuery and CSS styles, as the interaction between the two is key.

The first line in the code is:

$(window).scroll(function() {

and what it does is binds the anonymous function (a function with no name) to the scroll event handler. In other words, when the user starts scrolling on the webpage, that function will execute its code block. So what's in this block?

if (window.pageYOffset > 100) {
        $("#navbar").addClass("fixed");
        $("article").addClass("articlepadding");

        $("#top").css("right", -6 + "px");

} else {
        $("#navbar").removeClass("fixed");
        $("article").removeClass("articlepadding");

        $("#top").css("right", -75 + "px");
}

That's it... that's actually the whole thing. Let's read through it. There's an if statement that examines how far the page has scrolled vertically using the pageYOffset property of the built-in JavaScript window object. If that offset is greater than 100 pixels, so if the user has scrolled the document a distance of 100 pixels, then those three commands in the if statement are executed. By the way, there is a compatibility note at the bottom of this post if you want to ensure that IE versions eight and below will work with this getup; I'm mean, I usually ignore most IE users (please switch to another browser if you're using IE!).

Where did I get this 100 value from? It is the height of the header as defined in its CSS rule. The way I've designed this particular example is that if the user scrolls the header out of view, I want the nav bar to lock into place at that exact moment. So if your header is a different size, then keep the height and that value in the if statement the same. If you have a header that changes its height dynamically, thus no specific value, then you can use the offsetHeight property of the element in the if statement instead.

So if the pageYOffset is greater than 100 pixels, a CSS class called "fixed" is added to the nav bar, and another CSS class called "articlepadding" is added to the article element on the page. The "fixed" class looks like this:

.fixed {
        width: 80%;
        position: fixed;
        top: 0;
        box-shadow: 2px 2px 10px black;
}

I've also defined a CSS rule for the nav element. Think of this as its default style:

nav {
        height: 25px;
        background: #00a;
       
        -webkit-transition: all .3s ease-out;
        -moz-transition: all .3s ease-out;
        -ms-transition: all .3s ease-out;
        -o-transition: all .3s ease-out;
        transition: all .3s ease-out;
}

The nav rule doesn't specify a position type for the nav element, thus the default static positioning is used. But when the "fixed" class is applied, it changes the positioning to the fixed type of course and that's basically what keeps the nav bar glued to the top of the document. Notice also the width has to be defined as well as the top position. I added the box-shadow in as an effect which beautifully animates due to the nav rule having the transition property set. Cool hey? We can use CSS to do the animation work for us; CSS automatically applies any transitions it needs to as we change the classes (or other CSS properties of elements) via jQuery.

And how about the classes for the article?

article {
        background-color: white;
}
   
.articlepadding {
        padding: 25px 0 0 0;
}

Similar to the nav element, the article has a basic rule with nothing more than a background color applied. The "articlepadding" class, added by jQuery when the document is scrolled more than 100 pixels, sets a top padding of 25 pixels. Why 25? Because it is the same as the height of the nav bar. Go ahead and comment out that padding line. Notice that odd "jump" as you scroll down? When the nav bar is set to fixed, it is removed from the normal flow of the document. Thus, the article element fills that void where the nav element used to be and "jumps" up 25 pixels. So the padding (could also be margin) compensates for the nav bar being removed from the normal document flow.

Now we're at the last line in the if statement's code block:

$("#top").css("right", -6 + "px");

When you use the css function in jQuery, you are basically getting or setting an inline CSS style. In this case, we are changing the right property of the div with the id of "top" and changing it to -6 pixels. There's also a css rule defining this element in general:

#top {
        position: fixed;
        background-color: #00a;
        border: 1px solid white;
        border-radius: 5px;
        width: 68px;
        height: 25px;
        right: -75px;
        top: 80%;
        box-shadow: 2px 2px 10px black;
       
        -webkit-transition: all .3s ease-out;
        -moz-transition: all .3s ease-out;
        -ms-transition: all .3s ease-out;
        -o-transition: all .3s ease-out;
        transition: all .3s ease-out;
}

Most of this is decoration. The important declarations are the position set to fixed, right set to -75px (keeps the element hidden just off the page) and the transition. The top set to 80% is the vertical position of the element; the percent unit keeps it relatively in the same spot, even if the browser window is resized.

So as you can probably tell at this point, when the user scrolls the distance of the header, jQuery changes the right position to -6px (use whatever value you want though for your specific needs). Because of the CSS transition declaration, we see a pretty animation occur rather than a sudden jump.

I believe that most people can figure out the else portion of the JavaScript code. If the user does not scroll the page more than 100 pixels, then we ensure the classes are removed from their respective elements and the right position of the "top" div is reset to -75px.

Easy.

And about that compatibility for IE browsers versions eight or less? If you checked out the documentation for window.pageYOffset (linked here and earlier) you'll see the the wonderful folks at Mozilla have provided some code to help make this as compatible as possible with older IE browsers. So for this example I could have added in this line before the if statement, with a minor update to the condition:

var verticalOffset = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;

if (verticalOffset > 100) {...}

Not only have you learned how to create sticky elements on a page, you've also seen how CSS transitions can help bring life to elements being moved around or that have various other properties changed via JavaScript.

Remember, you can make suggestions for future tutorials and I'll be happy to consider them; just leave a comment. L8r!

Friday, June 13, 2014

CSS Only Dropdown Menu that Works on Mobile Devices

Not only is it a full moon today, but it's also Friday the 13th. So it's only fitting that I tackle a scary topic: CSS only dropdown menus that work on mobile devices!

Ok, so really it's not that scary and I'll try to make it as painless as possible... don't mind the machete.

First off, it's nice to see what we're trying to accomplish. Because this example is geared for mobile type devices only you should be viewing this example on such a platform, or if you are using a desktop computer, pretend that you can only "tap": http://www.imrezbalint.com/blogexamples/csstargetmenu.html

The actual presentation of the example is not critical; you can create your own design in pretty much whatever manner you wish. The focus here is that on a mobile device one can tap a menu to see the dropdown list that will then stay open until the user taps another menu item or the "Close Menu" option.

Secondly, I have not bothered to create media queries to target small screen sizes, but if you are attempting to do this for yourself, then the CSS code should be targeted to the mobile realm, with a desktop version for those who have larger screens and mice to surf with (so in the desktop version you could do the typical open on hover actions). Now let's get started.

I generally like to start off with the HTML structure of the page, so let's examine that first:

<div id="wrapper">
    <nav>
        <ul>
            <li><a href="#">HOME</a></li>
           
            <li><a href="#about">ABOUT US</a>
                <ul id="about">
                    <li><a href="#">History</a></li>
                    <li><a href="#">Corporate Info</a></li>
                    <li><a href="#">Meet the Team</a></li>
                    <li>va href="#">Close Menu</a></li>
                </ul>
            </li>
           
            <li><a href="#products">PRODUCTS</a>
                <ul id="products">
                    <li><a href="#">Bicycles</a></li>
                    <li><a href="#">Canoes</a></li>
                    <li><a href="#">Longboards</a></li>
                    <li><a href="#">Close Menu</a></li>
                </ul>
            </li>
           
            <li><a href="#">CONTACT</a></li>
        </ul>
    </nav>
</div>

Overall there is nothing out of the ordinary. The div and nav elements are there to position the menu and would be more useful for a desktop version of the site. The "HOME" and "CONTACT" links are not dropdown menus and instead of a "#" for the href, one would normally place a link there like home.php or contact.html.

But the "ABOUT US" and "PRODUCTS" links have dropdown menus and their hrefs are important, "#about" and "#products", respectively. Notice how the unordered list right below each one has an id (without the #) that matches. This is where the CSS will come into play. As for the menu items that are part of the dropdown list, those should be actual links to real pages (since of course I have not made any additional pages, I've kept them the traditional "#").

So without further ado, let's work our way through the CSS.

* {
        margin: 0;
        padding: 0;
        border: 0;
    }
   
    #wrapper {
        width: 100%;
    }
   
    nav {
        /* background-color: grey;
        overflow: hidden; */
    }

Nothing too special here. There's a quick reset in the universal selector, a little sizing for the wrapper div (overall this wouldn't do much as block elements are 100% in width anyway, but imagine if there was a desktop version to this CSS where the width was set to 80%; we need to override that value so we can use up the whole width on the mobile device), and lastly the nav, which like the div would have a more significant part to play when targeted for desktops (hence why the properties are commented out).

ul li {
        min-width: 50%;
        list-style-type: none;
        float: left;
    }

This bit decorates the first level list items. The min-width set to 50% gives us the two column look and the list-style-type set to none removes the bullets. The float is required or else the list would be vertical.

ul li a {
        display: block;
        background-color: blue;
        color: white;
        text-decoration: none;
        text-align: center;
        font-weight: bold;
        font-family: sans-serif;
        padding: .25em 2em;
    }
   
    ul li a:hover {
        background-color: darkblue;
    }

Although these parts look like a lot, all they do is provide the look and feel for the links. The anchors are set to the block display type and when the first-level links are hovered over (at least on a desktop) they will appear dark blue in colour.

ul li ul {
        display: none;
        position: absolute;
        border: 1px solid black;
        min-width: 49%;
    }

This next section is quite significant in this example. If you follow the descendant selector, you'll notice that we are working with the dropdown menus. We don't want these to appear unless the user clicks on a top-level link, so the display is set to none. To ensure the menu opens up in a nice position, the typical setting for position is set to absolute. Since I added a border, min-width has to be a bit smaller than 50% or else the menu will look a little odd (try it). On a quick side note, I could have used the CSS calc function for the width (e.g. calc(49% - 2px), but it does not work for every browser equally and also causes the CSS validation to fail.

ul li ul li {
        float: none;
    }

This next little bit overrides the float for the list items in the dropdown menu. If you don't do this, the menu will appear horizontal.

ul li ul li a {
        margin: 0;
        padding: .25em;
        box-shadow: 2px 2px 3px black;
        text-align: left;
    }
   
    ul li ul li a:hover {
        background-color: purple;
    }

These two rules simply decorate the links in the dropdown menu. Lastly, here the magical part:

#about:target {
        display: block;
    }
   
    #products:target {
        display: block;
    }

Remember how we gave the dropdown lists (specifically the ul elements) an id? Here they are with the target pseudo-classs. The only property adjusted is display being set to block, which as can be guessed displays them on the click.

The way the target pseudo-class works is like a toggle. When someone clicks a link with the same name as the id (like "PRODUCTS" has an href of "#products"), CSS looks looks for the element with that id and executes the declarations in the related rules with the target pseudo-class. In our case, all that happens is that the display is set to block to show the dropdown list. Clicking other links will simply revert that element back to the non-target rule that applies to it, hence why when you click another menu item or the "Close Menu" link (with an href of "#"), the dropdown disappears. Read more about the target pseudo-class here: https://developer.mozilla.org/en-US/docs/Web/CSS/:target

Awesome stuff. I think one of the biggest pros to this method is that we don't have to use JavaScript. Not that I'm normally against it, and ultimately it wouldn't necessarily be wrong to use it, but for non-programmers and to reduce the number of requests made to a server, this is definitely a plus. The tap-to-open a menu is also going to be familiar to almost all mobile / tablet type device users too, so we're not sacrificing much in regard to the design of our website; it still pretty much looks and works like it would on a desktop system without having to completely redesign the navigation in some manner.

But as with almost everything there are some downsides. For one, since we haven't employed any JavaScript, the menus will stay open indefinitely unless they are manually shut by the user (hence the addition of the "Close Menu" item). In addition, on desktop computers where a mouse can be used, the menu does not open when the cursor is hovered over a link. Although it can be done, the menu starts acting goofy. For example, they'll open on hover but won't close when another menu opens, amongst some other things (there was another example of this where the menu had to be clicked first and then the hover would click in). Then again, the only time this would be an issue on a desktop is if media queries where not used.

So my suggestion is not to look for an all-encompassing method for dropdrop menus if you're going to use a CSS only technique. As responsive design is becoming the norm, create media queries and CSS files targeting various screen sizes and platforms and then it becomes a little more straightforward to design not only the look of your website, but it's behaviour in various cases.

So that's it for now. I have plenty more tutorials but if you have suggestions, please leave a comment. L8r!

Sunday, June 8, 2014

JavaScript / jQuery Page Identifier Tutorial

This is the first of hopefully many Web technology related blog posts I'll be writing. More specifically these articles will be involving JavaScript / jQuery and Cascading Style Sheets (CSS).

In this tutorial I will be using JavaScript to analyze which Web page a user is viewing and then apply a CSS style to the relevant menu element to provide the audience some visual feedback. Please download jQuery as it is requited for this example. I often use the library to simplify parts of my code and to gain some benefits like not having to worry as much about coding cross browser compatibility.

First off, let's take a look at exactly what we're trying to do (click the pic for a larger size).


Take a look at the URL bar. You'll see that it contains the text, "www.imrezbalint.com/videos.php". Now look at the navigation bar of my site. See how the VIDEOS link is highlighted? Well that's the work of the script we're about run through.

Now you're probably thinking, I could just as easily do this using CSS by placing an inline style on that link (or a class) on each page that needs it. True, manually you could. But you'd have to remember to do this on every new page you might at one time want to add to your site. Did I mention you have to do this manually? Yuck!

With JavaScript there's a cooler and better way (well I think it's better).

But before hitting the code, let's examine the markup first as this helps us understand what we'll be doing. The pertinent piece is really just the menu.

<nav>
    <a href="index.php" id="index">HOME</a>
    <a href="about.php" id="about">ABOUT</a>
    <a href="resume.php" id="resume">RESUME / C.V.</a>
    <a href="videos.php" id="videos">VIDEOS</a>
    <a href="music.php" id="music">MUSIC</a>
    <a href="resources.php" id="resources">RESOURCES</a>
    <a href="contact.php" id="contact">CONTACT</a>
</nav>

Nothing all that spectacular. Just a nav element and within it nested are a bunch of anchors. I have them styled as blocks to get the look I want for my navigation bar; that's why you see all those ids (I had to mess with the widths of a few of the menu items to get them to look just the way I wanted them).

Now let's take a peek at the script:

$(document).ready(function() {
    var docLoc   = document.location.toString();
    var index    = docLoc.lastIndexOf("/");
    var strPage  = docLoc.substring(index+1);
   
    if (docLoc.length < 28) {
        strPage  = "index.php";
    }
   
    $("nav a").each(function() {
        if ($(this).attr("href") == strPage) {
            $(this).css({"background-color": "#777"});
            return false;
        }
    });
});


Considering what is being accomplished, this is not a lot of code. First off we have:

$(document).ready(function() {

This is a fairly common method to start off a jQuery script to initialize things when the document object model is ready to be manipulated. In more plain English, we're saying that when the page is ready to be worked on, execute the following code in the anonymous function.

Next we have three variables to store some information we need. docLoc contains a string of the URL. We cannot just use "document.location", as this will only return the location object with other functionality we are not interested in for this purpose.

The next two variables are index and strPage. The variable index is used to get the position of the last forward slash in the URL, and strPage basically stores the file name we need by using the substring function. When passing one argument to the function, in this case a value of index+1, a new string is created starting from that position in the string and up to the end. Why the plus one? Because I don't want to include the slash. Take a look back above at the navigation HTML. The hrefs point to a file name like, "index.php". Anyway, when all is said an done, the strPage variable holds the file name to a page on my website.

if (docLoc.length < 28) {
    strPage  = "index.php";

}

This quick conditional statement performs a check to see if there's no file name in the URL. For example, someone might enter my website via, http://www.imrezbalint.com/. Notice how there is no file name at the end (no index.php). In such situations I'd still like to ensure that the HOME link on my website is highlighted. It just so happens that the URL as you just saw written is 27 characters, hence why I'm checking to see that if the length of the document location (stored in the variable docLoc) is less than 28 characters, then I set the strPage variable to the home page, index.php. We'll need this for the next section.

Here's where the fun and automation really kicks in. One the things I really like about jQuery is the CSS-like method of selecting elements on a page. Since I'm only interested in the links nested in the nav section of my pages, I use the $("nav a") selector.

The each function then allows me to loop through, you guessed it, each of the links in that nav section.

if ($(this).attr("href") == strPage)

With the if statement above I can then check the link's hyperlink reference to see if it equals whatever is in the strPage variable. If that condition is true:

 $(this).css({"background-color": "#777"});
return false;

These two lines finish the rest of the script off. The $(this) part of the code refers to the current link (in the nav section) that the each loop is working on. The css function then adds the highlight as a background colour to that link. The return; statement ends the each function from executing any further as it would be waste of processor time; the logic is that there is only one menu item that should match, so once it's found, stop the loop from continuing.

And there you have it. If you have a website developed in a similar fashion then this script might be helpful to you either almost as is or with a little modification if necessary.

I have several more of these tutorials lined up and I'm hoping to keep up a pace of one a week. We'll see how that goes. If you have any suggestions for future tutorials, I'm willing to listen so just leave a comment. L8r!