I must note this disclaimer! I don’t own any of the images used in this
tutorial, nor do you. They are copyright to their vendors, whether it
be Apple, inc, Adobe, etc. Using icons is a bit of an integrity issue,
so don’t abuse it!
Adding to Our Leopard Desktop with jQuery
Preface
I must note this disclaimer! I don’t own any of the images used in this
tutorial, nor do you. They are copyright to their vendors, whether it
be Apple, inc, Adobe, etc. Using icons is a bit of an integrity issue,
so don’t abuse it!
Secondly, a new jQuery.UI file will replace the draggable js file. This is basically all the interaction packs.Make sure you expand that into it’s own directory! We’ll be adding onto that. A whole lot if images are needed too.
Make sure you expand that zip into the ‘images’ directory, so that any
new folders are merged with their counter parts from last week. I
apologise for the confusion with this. Stupid file structure, my bad.
So.
Just the same, the jQuery.ui links need editing.
- <script src="js/jquery.ui.interaction.min.js" type="text/javascript"></script>
<script src="js/jquery.ui.interaction.min.js" type="text/javascript"></script>
Plan of Attack
Though it might not look like it, a whole lot of code is needed for these few things:
- Stacks
- Dashboard
- Opening/Closing the Adding Widgets Panel
- Dragging Widgets onto the Dashboard list
- Closing Widgets
- Some extra bits (improving dock, adding desktop items)
Changes
Just before we start, I really do apologise, but there were a few
things that needed changing from last week. the #dock css should read:
- #dock{
- position: fixed;
- margin: 0 auto;
- bottombottom: 38px;
- left: 40%;
- z-index: 0;
- list-style: none;
- }
#dock{
position: fixed;
margin: 0 auto;
bottom: 38px;
left: 40%;
z-index: 0;
list-style: none;
}
The #closeZone’s zIndex in dashboard.js on line 24 should be 99 not 9999
Step 1 – Stacks
So lets dive right into it, and start with Stacks. Or more a Stack.
Unfortunately due to the way that jqDock works, it’s impossible to
nestle stacks within the jqDock without editing the core js, which is
much further than this tutorial intends. So instead, we’ll be creating
a stack to the bottom right of the page. The harder parts of coding
stacks is a) the incrementing height for each item, and the curve.
Luckily, a loop combined with maths can do this hard work for us.
Step 1:1 – HTML
Lets begin with adding the HTML structure of the stack. Now due to
the nature of the stack, if you wish to use it on a different website,
you can! Basically anything inline within the <li>s work. The
positioning just needs tweaking. Regardless, we use spans (and you
could always wrap the images in <a>s!
- <div class="stack">
- <img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/stack.png" alt="stack"/>
- <ul id="stack1">
- <li><span>Acrobat</span><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/adobeAcrobat.png" alt="" /></li>
- <li><span>Aperture</span><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/aperture.png" alt="" /></li>
- <li><span>Photoshop</span><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/photoshop.png" alt="" /></li>
- <li><span>Safari</span><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/safari.png" alt="" /></li>
- <li><span>Finder</span><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/finder.png" alt="" /></li>
- </ul>
- </div>
<div class="stack">
<img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/stack.png" alt="stack"/>
<ul id="stack1">
<li><span>Acrobat</span><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/adobeAcrobat.png" alt="" /></li>
<li><span>Aperture</span><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/aperture.png" alt="" /></li>
<li><span>Photoshop</span><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/photoshop.png" alt="" /></li>
<li><span>Safari</span><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/safari.png" alt="" /></li>
<li><span>Finder</span><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/finder.png" alt="" /></li>
</ul>
</div>
The first image is the folder placeholder. This is what activates
the dock, so it’s necessary. (When we use the jQuery selectors,
however, I’m sure you could use :first to get the first dock item if
you /really/ don’t want a containing basket).
Step 1:2 – CSS
Contrary to the first tutorial, I’m going to include the CSS and
jQuery for each step, just so the design doesn’t muddle up completely.
Open up style.css from last week and add to the bottom:
- .stack{
- position: absolute;
- bottombottom: 0;
- rightright: 100px;
- }
-
- .stack ul{
- list-style: none;
- position: absolute;
- top: -30px;
- z-index: -9;
- }
-
- .stack ul li{
- position: absolute;
- }
-
- .stack ul li span{
- display: none;
- }
-
-
- .stack .openStack li span{
- display:block;
- position:absolute;
- top: 17px;
- rightright:60px;
- height: 14px;
- line-height: 14px;
- border: 0;
- background-color:#000;
- color: #fcfcfc;
- text-align: center;
- opacity: .85;
- padding: 3px 10px;
- border-radius: 10px;
- -webkit-border-radius: 10px;
- -moz-border-radius: 10px;
- opera-border-radius: 10px;
- text-shadow: #000 1px 1px 1px;
- }
.stack{
position: absolute;
bottom: 0;
right: 100px;
}
.stack ul{
list-style: none;
position: absolute;
top: -30px;
z-index: -9;
}
.stack ul li{
position: absolute;
}
.stack ul li span{
display: none;
}
/*I'm for the jquery*/
.stack .openStack li span{
display:block;
position:absolute;
top: 17px;
right:60px;
height: 14px;
line-height: 14px;
border: 0;
background-color:#000;
color: #fcfcfc;
text-align: center;
opacity: .85;
padding: 3px 10px;
border-radius: 10px;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
opera-border-radius: 10px;
text-shadow: #000 1px 1px 1px;
}
Your stack will now look like a closed stack, but you can’t open it.
This just stacks (hah, no pun intended) all the icons on top of each
other, so they’re compressed into a small square. The last selector is
for the jQuery. When the stack is opened, the class ‘openStack is added
to the ul. I apologise to those CSS3 haters, It’s the fastest most
efficient way to get it right in most modern browsers.
Step 1:3 – jQuery
In plain english, the stack needs to open when the img is clicked,
pushing each (hint…) li up in incrememnts, and to the right a little,
whilst resizing to a smaller size. Then when clicked again, everything
returns to normal.
- $('.stack>img').toggle(function(){
-
-
-
-
- }, function(){
-
-
- });
$('.stack>img').toggle(function(){
//this function, for each element increases the top position to by 50px,
//and across using, the equation: value = (value+1)*2. Both variables
//start at 0.
}, function(){
//this one just reverses the above.
});
The second function is easy, but the first is a pain.
- var vertical = 0;
- var horizontal = 0;
- $('~ul>li'this).each(function(){
- $(this).animate({top: '-' +vertical + 'px', left: horizontal + 'px'}, 300);
- vertical = vertical + 50;
- horizontal = (horizontal+1)*2;
- });
- $('~ul', this).animate({top: '-50px', left: '10px'}, 300).addClass('openStack');
- $('~ul>li>img', this).animate({width: '50px', marginLeft: '9px'}, 300);
var vertical = 0;
var horizontal = 0;
$('~ul>li'this).each(function(){
$(this).animate({top: '-' +vertical + 'px', left: horizontal + 'px'}, 300);
vertical = vertical + 50;
horizontal = (horizontal+1)*2;
});
$('~ul', this).animate({top: '-50px', left: '10px'}, 300).addClass('openStack');
$('~ul>li>img', this).animate({width: '50px', marginLeft: '9px'}, 300);
Woo, jampacked with string interrupting, variables and math.
Interesting selectors, huh? The ~ is ’siblings of’ Erg. Math. Let me
explain. The first 2 variables are for the vertical position and the
horizontal position (curve).
Top incrementing is the same each time, where as unless you want a
horizontal straight line, each horizontal position has to be slightly
more than the rest. In this case, it increases by the previous number
plus one, times 2. so it’ll go 2, 6, 14, 30, 62, 126, etc. I know
they’re weird numbers, but it works. Use any equation you like!
The ‘each’ function is similar to, say a WordPress loop. This
function happens every time the next element is used. The equation
‘value = (value+1)*2′, means ‘new value equals old value plus one, then this times two.
The first animate line adds the variables (within the plus) every
time it’s looped via string splitting. The last two lines are just the
size. The other half of the toggle function, just resets everything
back to normal:
- $('~ul', this).removeClass('openStack').children('li').animate({top: '20px', left: '-10px'}, 300);
- $('~ul>li>img', this).animate({width: '79px', marginLeft: '0'}, 300);
$('~ul', this).removeClass('openStack').children('li').animate({top: '20px', left: '-10px'}, 300);
$('~ul>li>img', this).animate({width: '79px', marginLeft: '0'}, 300);
Simple! Now your jQuery stacks will successfully animate, even
curving! Unfortunately rotation is a tad more difficult. Though when
HTML5 comes out in 2022 (-_-) the canvas tag might have full support
for that.
Step 2 – Dashboard
So we’re gonna add to the Dashboard a bit. Firstly, an Add Widgets
Panel (Wont do the actual adding til later). After that, closing of the
widgets will be possible when this panel is open. Finally, being able
to add your own widgets from this panel. Uses some very different
selecting methods. Adding the widgets also covers Droppables
extensively, as the drop function is rather large.
Step 2:1 – Add Widget Panel
Firstly, the HTML. Add this just before the closing #dashboardWrapper div.
- <div id="addWidgets">
- <span id="openAddWidgets">Add/remove widgets</span>
- <div id="dashPanel">
- <ul>
- <li><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/widgets/thumbs/sticky.png" alt="" id="sticky" class="widgetThumb" /><span>Sticky</span></li>
- <li><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/widgets/thumbs/clock.png" alt="" id="clock" class="widgetThumb" /><span>Clock</span></li>
- <li><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/widgets/thumbs/weather.png" alt="" id="weather" class="widgetThumb" /><span>Weather</span></li>
- </ul>
- </div>
- </div>
<div id="addWidgets">
<span id="openAddWidgets">Add/remove widgets</span>
<div id="dashPanel">
<ul>
<li><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/widgets/thumbs/sticky.png" alt="" id="sticky" class="widgetThumb" /><span>Sticky</span></li>
<li><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/widgets/thumbs/clock.png" alt="" id="clock" class="widgetThumb" /><span>Clock</span></li>
<li><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/widgets/thumbs/weather.png" alt="" id="weather" class="widgetThumb" /><span>Weather</span></li>
</ul>
</div>
</div>
The ‘openAddWidgets’ is the little cross/plus that opens and closes
the Panel. The list items are the available widgets (create as many as
you’d like!). The images you downloaded are the little thumbs. These
will become draggables, and you will be able to drop them onto the
#closeZone and eventually, widgets appended to the #widgets list.
At the moment, this looks like a bit of a mess;
But with some CSS we’ll fix that right up.
- #addWidgets{
- position: absolute;
- z-index: 9999;
- bottombottom: 0;
- left: 0;
- width: 96%;
- height: 164px;
- background: url(images/dashpanel.png) bottombottom repeat-x;
- padding: 0 2%;
- }
-
- #openAddWidgets{
- display: block;
- width: 36px;
- height: 36px;
- background: url(images/opendashpanel.png) center;
- position: relative;
- z-index: 9999;
- text-indent: -9999em;
- }
-
- #dashPanel ul{
- list-style: none;
- margin-top: 27px;
- }
-
- #dashPanel ul li{
- float: left;
- padding-right: 30px;
- }
-
- #dashPanel ul li img{
- display: block;
- }
-
- #dashPanel ul li span{
- width: 74px;
- display: block;
- text-align: center;
- font-weight: bold;
- text-shadow: #fff 1px 0 1px;
- color: #17243e;
- padding-top: 10px;
- }
#addWidgets{
position: absolute;
z-index: 9999;
bottom: 0;
left: 0;
width: 96%;
height: 164px;
background: url(images/dashpanel.png) bottom repeat-x;
padding: 0 2%;
}
#openAddWidgets{
display: block;
width: 36px;
height: 36px;
background: url(images/opendashpanel.png) center;
position: relative;
z-index: 9999;
text-indent: -9999em;
}
#dashPanel ul{
list-style: none;
margin-top: 27px;
}
#dashPanel ul li{
float: left;
padding-right: 30px;
}
#dashPanel ul li img{
display: block;
}
#dashPanel ul li span{
width: 74px;
display: block;
text-align: center;
font-weight: bold;
text-shadow: #fff 1px 0 1px;
color: #17243e;
padding-top: 10px;
}
Heavy on with positioning, z-indexing and floating, this should gather an effect like this (Panel’s there, not hidden):
Finally, the jQuery to hide and show it. Add the following under the
comment ‘//draggables definition’ (for the sake of organisation):
- $('#addWidgets ul li img').draggable({helper: 'clone'});
$('#addWidgets ul li img').draggable({helper: 'clone'});
And add this under the ‘//initial hiding of dashboard + addition of ‘closeZone” block:
-
- $('#addWidgets').css({bottom: '-118px'});
//initial hiding of #dashPanel and addable widgets
$('#addWidgets').css({bottom: '-118px'});
Now for the toggle code. In english, when the ‘open’ button is
clicked, slide the panel up. When it’s clicked again, slide the panel
down. Let’s start with the toggle.
-
- $('#openAddWidgets').toggle(function(){
-
-
- }, function(){
-
-
- });
//open/closing of the dashpanel
$('#openAddWidgets').toggle(function(){
//this opens the dashboard, animation and all
}, function(){
//opposite to above
});
Thus the opening function will be in the first gap, whilst the closing in the second. The first:
- $(this).css({background: 'url(images/closedashpanel.png)'});
- $('#addWidgets').animate({bottom: '0px'}, 500);
$(this).css({background: 'url(images/closedashpanel.png)'});
$('#addWidgets').animate({bottom: '0px'}, 500);
And the second, reversing above:
- $(this).css({background: 'url(images/opendashpanel.png)'});
- $('#addWidgets').animate({bottom: '-118px'}, 500);
$(this).css({background: 'url(images/opendashpanel.png)'});
$('#addWidgets').animate({bottom: '-118px'}, 500);
Finally, like Leopard, it should close when the user returns to the
Desktop, right? Add this to //#closeZone’s job: closing the Dashboard’
function (within it!):
- ('#openAddWidgets').css({background: 'url(images/opendashpanel.png)'});
- $('#addWidgets').animate({bottom: '-118px'}, 500);
$('#openAddWidgets').css({background: 'url(images/opendashpanel.png)'});
$('#addWidgets').animate({bottom: '-118px'}, 500);
Now if you click the little plus in the bottom left when the Dashboard is open, it should animate! Awesome!
Step 2:2 – Adding Widgets to the Dashboard List
This proved a beast and a half. Lots of code for this… Yay! Luckily,
It’s only jQuery! Let’s start by defining the Droppable; #closeZone.
Place this under the Draggables definitions:
-
- $('#closeZone').droppable({
- accept: '.widgetThumb',
- drop: function(ev, ui){
-
- }
- });
//droppable definition
$('#closeZone').droppable({
accept: '.widgetThumb',
drop: function(ev, ui){
}
});
Basically, #closeZone can now accept the thumbs in the Panel as
droppables, and we’re about to delve into what happens on the drop.
In understandable language, this is how it goes. Variables for the
mouse position need to be found so the position of the drop can be
where we want it. Another variable for the type of widget to append is
needed. On drop, the Widget needs to be appended, a different image
depending on the widgetType variable. Now to be different, the stickies
will be editable (No way!). A textarea will be appended, to allow
writing. Because all the draggable definitions happen on the load of
the document, they’ll need to be re-defined every time a widget is
appended to the DOM, so that it’s applied to the newest one of such.
We’ll start with the variables.
- var x = ev.clientX - 100;
- var y = ev.clientY - 50;
- var widgetType = $(ui.draggable).attr('id');
var x = ev.clientX - 100;
var y = ev.clientY - 50;
var widgetType = $(ui.draggable).attr('id');
Sadly we can’t get the width and height of the image that’s about to
be appended too easily (to center the drop). So instead we need to
guess, by offsetting the position of the mouse by 100 and 50, so it’s
not in the top left. The JavaScript variables ‘cleintX’ and ‘clientY’
are basically the mouse position. And that interesting selector;
ui.draggable, is the element that has just been dragged! Thanks
jQuery.ui! Now for appendage:
- $('#widgets').append('<li class="widget '+widgetType+'Widget" style="left: '+ x +'px; top: '+ y +'px;"><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/widgets/'+widgetType+'.png" alt="" /></li>');
- $('.stickyWidget').append('<textarea></textarea>');
- $('.widget').draggable();
$('#widgets').append('<li class="widget '+widgetType+'Widget" style="left: '+ x +'px; top: '+ y +'px;"><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/widgets/'+widgetType+'.png" alt="" /></li>');
$('.stickyWidget').append('<textarea></textarea>');//needed to add textarea to newest DOM member
$('.widget').draggable(); //needed to 'draggable' the newest DOM member
Allow me to explain how the variables work in the appending. To give
a class to the new widget for customisation, adding
“…’+widgetType+’Widget’…” will return a class similar to ’stickyWidget’
or ‘weatherWidget’. The inline style (so sorry it’s inline! Don’t shoot
me!) determines the absolute position of the widget by the variables,
which are of course the mouse coordinates. As I mentioned, the newest
DOM members need any jQuery modifications or appendages [made on the
document load] re-applied, as jQuery doesn’t recognise new DOM members.
For the last two lines, jQuery needs to append the textarea (so you can
edit the text) and the new Widget needs to become a draggable.
In order for all this to work, some CSS is needed. Replace, in style.css, the ‘.widget’ selector and attributes with:
- .widget{
- position: absolute;
- z-index: 9999;
- float: left;
- margin: 1em;
- list-style: none;
- }
-
- .stickyWidget{
- padding: 15px 20px;
- width: 185px;
- height: 155px;
- background: url(images/widgets/sticky.png) no-repeat center;
- }
-
- .stickyWidget>img{
- display: none;
- }
-
- .stickyWidget textarea{
- height: 100%;
- width: 100%;
- background: 0;
- border: 0;
- outline: 0;
- font-size: 16px;
- font-family: 'Marker Felt';
- overflow: hidden;
- }
.widget{
position: absolute;
z-index: 9999;
float: left;
margin: 1em;
list-style: none;
}
.stickyWidget{
padding: 15px 20px;
width: 185px;
height: 155px;
background: url(images/widgets/sticky.png) no-repeat center;
}
.stickyWidget>img{
display: none;
}
.stickyWidget textarea{
height: 100%;
width: 100%;
background: 0;
border: 0;
outline: 0;
font-size: 16px;
font-family: 'Marker Felt';
overflow: hidden;
}
This makes the sticky all looking like a sticky. The you either will
or wont have the Marker Felt font, that’s what the actually sticky
widget uses. For the original widget to remain pretty, wrap the text,
rather than in <p>s but with:
- <textarea rows="10" cols="10">
- ...
- <textarea>
<textarea rows="10" cols="10">
...
<textarea>
And give the li an extra class of ’stickyWidget’ to match the css (The li’ll now have 2 classes).
All goes according to plan, you should now be able to a) edit stickies, and b) add new widgets to the Dashboard.
Step 2:3 – Closing widgets
Why give this part a whole section to itself? Because the workings
of this are weaved into all of the previous functions, clicks, and
appendages. So instead of being confused adding this throughout all the
parts, why not keep it in one?
Right. So basically a small span’ll be applied to widgets when the
panel opens, and when a new widget is added to the Dashboard. When this
is clicked, the parent widget will disappear! Awesome, huh? When the
Add Widgets Panel closes, the crosses will disappear into the realms of
.hide().
Working down the document for integrating the close button, we begin
with the #closeZone’s function. Underneath the #addWidget’s
disappearing act (code), add:
- $('.closeWidget').hide();
$('.closeWidget').hide();
Next up, within the droppable definition. This snippet of code will
apply a close widget button and it’s function to all widgets when a new
one is dragged on. Underneath the newest draggable definition for the
newly created widget (within the drop function), add:
- $('.widget').append('<span class="closeWidget"><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/closebox.png" alt=""/></span>');
-
- $('.closeWidget').click(function(){
- $(this).parent().animate({opacity: '0'}, 300).animate({left: '-9999em'},1);
- });
$('.widget').append('<span class="closeWidget"><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/closebox.png" alt=""/></span>');
//click function of newest DOM element.
$('.closeWidget').click(function(){
$(this).parent().animate({opacity: '0'}, 300).animate({left: '-9999em'},1);
});
Finally, the open/close panel function is where it really matters,
as this will append the thing to all Widgets on open of the Panel (like
Leopard). Below both animates, add respectively:
- $('.widget').append('<span class="closeWidget"><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/closebox.png" alt=""/></span>');
-
- $('.closeWidget').click(function(){
- $(this).parent().animate({opacity: '0'}, 300).animate({left: '-9999em'},1);
- });
$('.widget').append('<span class="closeWidget"><img src="http://nettuts.s3.amazonaws.com/082_leopard2/images/closebox.png" alt=""/></span>');
//click function of newest DOM element.
$('.closeWidget').click(function(){
$(this).parent().animate({opacity: '0'}, 300).animate({left: '-9999em'},1);
});
and
- $('.closeWidget').hide();
$('.closeWidget').hide();
Now, when the Panel is opened, a little clickable cross goes to the
bottom right of the Widget, and when you drag a new Widget on, it’ll
seemingly duplicate. To fix all this, add this CSS:
- .closeWidget{
- position: absolute;
- z-index: 99999;
- top: -5px;
- left: -5px;
- }
.closeWidget{
position: absolute;
z-index: 99999;
top: -5px;
left: -5px;
}
And TADA! You now have widgets that close, and can reform when you want them too! Awesome stuff!
Step 3 – Some Extra Bits
This is really just for visuals sake, but we’ll be adding a Desktop
Item that you can create your own function when double clicked, and
making the Dock a bit speedier.
Step 3:1 – Desktop Item
Add some HTML, make this the first thing after the opening #wrapper div:
- <ul id="desktopItems">
- <li id="macin
|