Project – A few ways to created “tabbed” content
If you haven’t already, be sure and check out the rest of the Mootools 1.2 tutorials in our 30 days series.
Welcome back to 30 days of Mootools. From here on out, we are going to loosen our publishing schedule, instead of every day, you can expect new tutorials every few days because we are pretty busy. Today, we are going to break away from the our coverage of the library and programming basics to do a short project. Using what we have learned so far, there are several ways we can use tabs (and other li’s) to create content that will only show on hover or on click.
Simple “Extra Info” Tabs
Tabs with info on hover
For this first step, we are going to create a simple menu that will reveal additional info when you hover over a list item. First, set up the html – lets do a ul with four items, then create four divs (one corresponding to each list item):
//here is our menu
<ul id="tabs">
<li id="one">One</li>
<li id="two">Two</li>
<li id="three">Three</li>
<li id="four">Four</li>
</ul>
//and here are our content divs
<div id="contentone" class="hidden">content for one</div>
<div id="contenttwo" class="hidden">content for two</div>
<div id="contentthree" class="hidden">content for three</div>
<div id="contentfour" class="hidden">content for four</div>For now, let’s not worry about making this pretty. In the css, all we need to do is hide the content boxes:
.hidden { display: none; }
Now, for the Mootools. If we want the content to show when someone mouses over it and hide when they leave, we need to set up some functions:
var showFunction = function() { this.setStyle('display', 'block'); } var hideFunction = function() { this.setStyle('display', 'none'); }
and some events:
window.addEvent('domready', function() { //here we can pass our container elements to a var var elOne = $('contentone'); $('one').addEvents({ //for the mousenter event, we call showFunction //and bind elOne, so we can pass the element to the function 'mouseenter': showFunction.bind(elOne), 'mouseleave': hideFunction.bind(elOne) }); });
Now, we just repeat this pattern for each tab and the corresponding content. Here it is complete:
//here are our functions to change the styles var showFunction = function() { this.setStyle('display', 'block'); } var hideFunction = function() { this.setStyle('display', 'none'); } window.addEvent('domready', function() { //here we turn our content elements into vars var elOne = $('contentone'); var elTwo = $('contenttwo'); var elThree = $('contentthree'); var elFour = $('contentfour'); //add the events to the tabs $('one').addEvents({ //set up the events types //and bind the function with the variable to pass 'mouseenter': showFunction.bind(elOne), 'mouseleave': hideFunction.bind(elOne) }); $('two').addEvents({ 'mouseenter': showFunction.bind(elTwo), 'mouseleave': hideFunction.bind(elTwo) }); $('three').addEvents({ 'mouseenter': showFunction.bind(elThree), 'mouseleave': hideFunction.bind(elThree) }); $('four').addEvents({ 'mouseenter': showFunction.bind(elFour), 'mouseleave': hideFunction.bind(elFour) }); });
As you can see, this is all very familiar and setting this up doesn’t require anything we havn’t covered so far.
- One
- Two
- Three
- Four
Tabs that show content on click
Taking the idea above, we can easily adjust it to reveal content on click. Let’s use the same html as above, and adjust the Mootools code for a click event.
First, we are going to need to adjust our functions. Since we can’t hide the content on mouseleave, we need to find another way to switch between the divs. Perhaps the easiest option is to hide them all on click, and just show “this” one (being whichever one is passed on click):
var showFunction = function() { $$('.hiddenB').setStyle('display', 'none'); this.setStyle('display', 'block'); }
Now, when we pass the function an element using bind, it will hide the others and reveal that element.
Next, we need to adjust events. First, we only need a single event, so we will use .addEvent();, and next we need to change the event type to ‘click.’
window.addEvent('domready', function() { var elOneB = $('contentoneB'); var elTwoB = $('contenttwoB'); var elThreeB = $('contentthreeB'); var elFourB = $('contentfourB'); $('oneB').addEvent('click', showFunction.bind(elOneB)); $('twoB').addEvent('click', showFunction.bind(elTwoB)); $('threeB').addEvent('click', showFunction.bind(elThreeB)); $('fourB').addEvent('click', showFunction.bind(elFourB)); });
- One
- Two
- Three
- Four
Morph Content Tabs
Extending on the code we have above, we can add some morph functionality when our hidden content is displayed. To begin, we can set up an Fx.Morph effect just like the previous example, except instead of setting the styles, we will morph them. Of course, we also have to create our morph objects:
var showFunction = function() { //resets all the styles before it morphs the current one $$('.hiddenM').setStyles({ 'display': 'none', 'opacity': 0, 'background-color': '#fff', 'font-size': '16px' }); //here we start the morph and set the styles to morph to this.start({ 'display': 'block', 'opacity': 1, 'background-color': '#d3715c', 'font-size': '31px' }); } window.addEvent('domready', function() { var elOneM = $('contentoneM'); var elTwoM = $('contenttwoM'); var elThreeM = $('contentthreeM'); var elFourM = $('contentfourM'); //creat morph object elOneM = new Fx.Morph(elOneM, { link: 'cancel' }); elTwoM = new Fx.Morph(elTwoM, { link: 'cancel' }); elThreeM = new Fx.Morph(elThreeM, { link: 'cancel' }); elFourM = new Fx.Morph(elFourM, { link: 'cancel' }); $('oneM').addEvent('click', showFunction.bind(elOneM)); $('twoM').addEvent('click', showFunction.bind(elTwoM)); $('threeM').addEvent('click', showFunction.bind(elThreeM)); $('fourM').addEvent('click', showFunction.bind(elFourM)); });
If we use the same html that we have above, we will get something like this:
- One
- Two
- Three
- Four
Note: If you click on the above example quickly you will see that it pushes out multiple content divs. Basically, if showFunction is called before the last one finishes tweening, it will not register it when it hides all content divs. To solve this, we are going to need to break out of this exact formula, and play a bit with Fx.Elements.
Example
This example works just like the above example, except when you click on two tabs quickly, it will not “stack” the content divs.
//create a "hide all" function //create a parameter so you can pass the element var hideAll = function(fxElementObject){ fxElementObject.set({ '0': { 'display': 'none' }, '1': { 'display': 'none' }, '2': { 'display': 'none' }, '3': { 'display': 'none' } }); } //here we create a function for each content element var showFunctionOne = function() { //first, call the hideAll function //then pass "this" as the Fx.element object hideAll(this); //start the Fx.element morph for the index that corresponds to the click event this.start({ '0': { 'display': ['none', 'block'], 'background-color': ['#fff', '#999'], 'font-size': ['16px', '25px'] } }); } var showFunctionTwo = function() { hideAll(this); this.start({ '1': { 'display': ['none', 'block'], 'background-color': ['#fff', '#999'], 'font-size': ['16px', '25px'] } }); } var showFunctionThree = function() { hideAll(this); this.start({ '2': { 'display': ['none', 'block'], 'background-color': ['#fff', '#999'], 'font-size': ['16px', '25px'] } }); } var showFunctionFour = function() { hideAll(this); this.start({ '3': { 'display': ['none', 'block'], 'background-color': ['#fff', '#999'], 'font-size': ['16px', '25px'] } }); } window.addEvent('domready', function() { //create your array to pass to Fx.elements var morphElements = $$('.hiddenMel'); //create a new Fx.Element object var elementEffects = new Fx.Elements(morphElements, { //set the "link" option to cancel link: 'cancel' }); $('oneMel').addEvent('click', showFunctionOne.bind(elementEffects)); $('twoMel').addEvent('click', showFunctionTwo.bind(elementEffects)); $('threeMel').addEvent('click', showFunctionThree.bind(elementEffects)); $('fourMel').addEvent('click', showFunctionFour.bind(elementEffects)); });
- One a
- Two
- Three
- Four
To Learn More…
This one is mostly a review and an application of the stuff we covered in the previous tutorials. As such, I am going to recommend that you read over the docs, in full, if you haven’t already. It’s more fun than it sounds. If you are new to the library and have been learning along with this tutorial, you may be surprised at how much you understand.
Download a zip of the final example
Along with everything you need to get started.
Tomorrow’s Tutorial
Questions, Comments or Suggestions
In particular, it would be great to hear of ways around the problem posed above. But, if you have something else on your mind, please don’t hesitate. Hope you have found this review useful.
Tags: 30 days of mootools, javascript, Mootools 1.2, tabs, tutorials
once again thanks for the tut
is it possible to do a tutorial for parsing rss feeds with cross domain compatibility? using proxies? this would be awesome, I would be very happy to see one tut regarding this
thanks once again for the great tuts
addition:
also it would be cool if there would be more in depth material, like how to strip parts. for example only show titles and publish date or title with short description and how to toggle it?
Brad, thanks for the tutorial suggestions, we’ll see what we can do. Regarding your second post, what you’re looking to do is pretty simple. Check out the tuts on event handling and style properties (they are all listed on day 1). Between the two of those, you should be able to figure something out. Basically, just set up a click event on a link or a button. Then call a function with that event that changes the style of a certain class (like “hiddenTitleAndSuch”) from “display: block” to “display: none.” Then you have have another button that will set the style back to “display: block.” I think that may take care of it.
Thanx.
I may be missing something here, but one case that has driven me insane with this example is that if you do a transition inside your hideAll:
var hideAll = function(thingie){
thingie.start({
‘0′: { //let’s change the first element’s opacity and width
‘height’: ['20px', '0px'],
‘display’: ['block', 'none']
},
‘1′: { //let’s change the first element’s opacity and width
‘display’: ['block', 'none'],
‘height’: ['20px', '0px']
}
});
}
var showFunctionOne = function() {
hideAll(this);
this.start({
‘0′: { //let’s change the first element’s opacity and width
‘display’: ['none', 'block'],
‘height’: ['0px', '20px']
}
});
}
var showFunctionTwo = function() {
hideAll(this);
this.start({
‘1′: { //let’s change the first element’s opacity and width
‘display’: ['none', 'block'],
‘height’: ['0px', '20px']
}
});
}
var hideFunction = function() {
hideAll(this);
}
window.addEvent(‘domready’, function() {
var morphElements = $$(‘.hiddenMel’);
var elementEffects = new Fx.Elements(morphElements, {
link: ‘cancel’
});
$(‘oneMel’).addEvent(‘mouseenter’, showFunctionOne.bind(elementEffects));
$(‘twoMenu’).addEvent(‘mouseenter’, showFunctionTwo.bind(elementEffects));
$(‘endMenu’).addEvent(‘mouseenter’, hideFunction.bind(elementEffects));
});
It skips the transition unless you remove the display, and when you remove the display it does the transition but doesn’t clear the display. Is there a way to chain these events together to do them smoothly and so the complete in order to continue to the next mouseevent?
Also, related to this is that if you do more than one of these transitions near each other, they tend to fire off without completing. So for example, if I have 3 things on my menu and I mouseover the first and then the second, it shows both the first and second underneath each other when you do this:
Menu1
Menu2
Menu List 1 – Menu List 2 – Menu List 3
Menu List 1 – Menu List 2 – Menu List 3
And my CSS:
.hiddenMenu {
height: 0px;
overflow: hidden;
width: 1020px;
}
#content_wrapMenu {
background-image: url(blackbg.jpg);
color: #FFFFFF;
padding-left: 10px;
}
#wrapMenu {
width: 1020px;
}
#endMenu {
width: 2000px;
}
Is there a workaround to keep things from getting “hung up” when another event is triggered?
Thanks for everything you’ve done with this blog, I’ve learned more about mootools here than anywhere else!
Great turorial. Just one thing, how do I get the first item to display when the page loads? At the moment everything is hidden.
Thanks
please continue with the work.
Troy,
Thanks for the article. I’m using this for yet another project I’m working on.
Justin
Yolanda,
This is what I did to ensure the first tab is always displayed. I hope it helps.
In your HTML add “display_first” in your class declaration. Example:
Note I also have a class called “routing_display_box”. In my scenario, this class is applied to all the divs that will be tab browsable.
Use the function below to start off with hiding all the “routing_display_box” divs. Then, it looks for any that have the class “display_first”. It then makes that div visible. Finally, it removes the “display_first” class from that div. This allows future clicks on other tabs to not mistakenly show the first div again.
function navTabs()
{
/**
* Setup links to hide and show different dpcs of order.
*/
var showFunction = function()
{
// Hide all the orders
$$(‘.routing_display_box’).setStyle(‘display’, ‘none’);
// If any order has the class “display_first”, change it’s style to block
// and then remove the class “display_first”. This allows the first tab to display
// on initial load
$$(‘.display_first’).setStyle(‘display’, ‘block’);
$$(‘.display_first’).removeClass(‘display_first’);
// Display the clicked order
this.setStyle(‘display’, ‘block’);
}
$$(‘.order_click’).each(showFunction(),el);
}
Add this to your html HEAD:
window.addEvent(‘domready’, function() {
navTabs();
});
Oops! Disregard all that code. It doesn’t work properly. I should have confirmed that prior to posting! Sorry. Back to the drawing board…
Yolanda, I did something similar to what you are looking for with a client site recently. Check out http://www.saffroniabaldwin.com.
The landing page has a gallery that reverts back to the first pic.
Hope this helps.
This tutorial totally saved me… thanks so much!! As a designer, the coding is taking me a bit longer. I got this tutorial to work as is but was curious…is there a way once you click on tab 1 – image appears then click tab 2 and click 1 disappears and click 2 image appears…argh hope that made sense! the only thing i can guess is a toggle but doesnt work with this tutorial.
Thanks
@12 – Im sorry Tracy, I’m really not sure what you mean. Are you talking about an image gallery? or do you mean that the content will tab through automatically?
Sorry about that…lemme try again…
your last example above, i clicked on “one a tab” and content animated in. next, i clicked on “two tab” and the “one a content” disappeared (no fade/animation) and “two content” faded in.
is there a way once current content is shown, it can animate out once a user clicks another tab and “new” content animates in?
argh, i hope that made better sense
[...] A Few MooTools Tabs [...]
[...] A Few Mootools Tabs [...]
[...] A Few Mootools Tabs [...]
[...] A Few Mootools Tabs [...]
No hope of a working example I guess. I’m already very familiar with tabs. But, sometimes a working example can give a good visual idea where the tutorial will lead me and it helps me decide if I want to go there or not. There’s tons of info to slog through on the internet if one decides to do so…
[...] A Few Mootools Tabs [...]
great, been looking for somthing like this for ages.
can anyone give me some insight as to how make the first tab open, and the tabs active when each content is showing
[...] MooTools Tabs [...]
[...] A Few MooTools Tabs [...]
[...] ??MooTools?? [...]
easy to convey the content…
Nice work dude….
H! Great article… I am having trouble with one thing and was wondering if someone could assist?
When you click the tab, how would can I have the js know which was clicked. Goal is to remove “inactive” class and add “active” class to the one which was clicked.
Thanks for any tips!!
You could use toggleClass() in the click event. This will add a class if there isn’t one, or remove it if it already is there. Hope this helps.
Thanks Troy. That is a start, but it still doesn’t tell me which was clicked. Any other ideas?
Hmmm… I am not sure if I fully understand your need here, but one more step would be to remove class from all list elements, then just add it to the current one being clicked. That would make the selected class be only on the one that was clicked.
Forgive me, I am not used to MooTools… I just need to know which item was clicked, the function:
addEvent(‘click’, showFunction.bind(elOneB));
how do I pass the clicked element into the showFunction?
elOneB is the content to be shown.
You could use an anonymous function in the event, then pass the event, so something like:
element.addEvent(‘click’, function(e){
current_element = e.target;
});
Yep! That is what I was looking for… thanks so much Troy!