In short there are several issues with your original code. Below are my attempts at getting the current code set working (with suggestions for improvement at the bottom. Please see my original fiddle for working code without changing to much of your original code. I also modified the original fiddle (with sliding) to use css transitions to give a slide effect)
1) You are constantly writing over your panels because their css top
value is set to zero for all. You can get around this by setting the top value for all non-active panels. (I assumed you wanted the item to dissappear from the stack, since you were moving it over to the current when it was selected)
//Increment height
var ii = 0;
for (var i = 0; i < panels.length; i++) {
if(panels[i].active == false){
panels[i].container.css("top", ii*30+"px");
ii++;
}
}
2) Your Z-index and width are causing the panel stack
panels and the current panel
to overlap, defaulting to whatever dom element was created last.
you can get around this by setting the z-index
value to a lower value in the `//make all panels not selected`` loop
panels[i].container.css("z-index", "1");
3) You'd likely also want to hide the current panels
contents when it is in the stack, otherwise your stack is going to get rather messy. You can get around this by splitting up your DOM panel's into something like:
<div id="panel5" class="box">
<div class="panelTitle">Panel 4</div>
<div class="contents">Stuff to display</div>
</div>
and adding the following code to the following loops:
//Make all panels not selected
for (var i = 0; i < panels.length; i++) {
//other code emitted for space...
//hide the contents
panels[i].container.find(".contents").css("display","none");
}
//now make the panel we selected
//the top most panel
panels[selected].active = true;
panels[selected].container.css("z-index", 2);
panels[selected].container.css("left", "25%");
panels[selected].container.css("top","0px");
//it's active, so display the contents
panels[selected].container.find(".contents").css("display","block");
4) In the event you want the panels to slide around on the clicks, I added CSS transitions - only in the second fiddle I added a transition rule to your .box
selector that will put a second delay on all css changes for elements that have the class box
. Please see css3 transitions for more on customizing transitions.
transition: all 1s;
-webkit-transition: all 1s;
-moz-transition: all 1s;
-o-transition: all 1s;
Suggestions for improvement/better development
To be honest I'd probably do a large rewrite. DOM manipulation gets the job done and is a fair start, but if things get complex it'd be much easier to track the data you need for each panel in the javascript and generate the DOM elements fresh when they are needed. I'd probably do something along the lines of the following disclaimer, I didn't test the following code as a whole, meant to be a guideline:
1) Define your data structure containing all relevant information to a panel and implement it in the javascript. This is similar to what you already have, but at a minimum I'd add a panel title and panel contents of sort.
Similar to what you have but I would add to it and do some parameter checking and remove the container
function Panel (jobj) {
if(typeof jobj === "undefined") throw new ReferenceError("Bad value passed to constructor");
if(typeof jobj.id === "undefined") throw new ReferenceError("Missing an id");
this.id = 0;
this.active = false;
if(typeof jobj.title === "undefined") throw new ReferenceError("Missing panel title");
this.title = jobj.title;
//set to passed value, or if doesn't exist, default to empty array;
this.tracks = jobj.tracks || [];
}
as a bonus you could make use of prototyping and construct different types of panels with different forms of parameter checking... I'm pretty sure there are other ways of doing this, but I've found the following works for me.
var DiscoverPanel = function(jsonObj){
if(typeof jsonObj === "undefined")throw new ReferenceError("Expecting a JSON object but received none");
var panel = new Panel(jsonObj);
if(typeof jsonObj.genre === "undefined") throw new ReferenceError("Missing genre!")
panel.genre = jsonObj.genre;
return panel;
}
2) create the HTML for a stack and a current panel, so instead of the 5 div elements you currently have, I'd narrow it down to 2. Don't forget to style it to your liking
<ul id="stack"></ul>
<div id="current"></div>
3) Define your panels in the javascript instead of the html. Once you have working examples of the underlying json objects that you pass to the constructors, you might find it beneficial to write a server side service that you call via AJAX that returns the necessary JSON.
the constructor would be called like so:
//there are plenty of ways to generate the id within the constructor if hard coding
//it bugs you, your post showed a means of doing so.
var p1 = new Panel({title:"Panel 1",id:"p1"});
var p2 = new Panel({title:"Panel 2",id:"p2"});
var dp = new DiscoverPanel({title:"discover",id:"dp",genre:"rock"});
//etc.
var pn = new Panel({title:"Panel n"});
var panels = [p1,p2,dp,...,pn];
4) Onload/domready generate the innerHTML for the inactive panels, from potentially the panel titles.
$(document).ready(function () {
generateList();
}
function generateList(){
var stack = document.getElementById("stack");
stack.innerHTML = "";
panels.forEach(function(panel){
var item = document.createElement("li");
item.setAttribute("id",panel.id)
item.innerHTML = panel.title;
item.onclick = function(){
//load this specific panel to the current
loadCurrent(panel);
//regenerate the stack
generateList();
}
stack.appendChild(item);
});
}
5) Proceed to generate the innerHTML for the current panel. This would be very similar to the above method of creating the element and the generating it's content off of the panel's data.
function loadCurrent(panel){
panel.active = true;
var cur = document.getElementById("current");
//Figured table was a good example, but you could get as creative as you want
//using floats, divs, etc. Could also check the types and handle different types of panels
var table = document.createElement("table");
panel.tracks.forEach(function(track){
var row = document.createElement("tr");
var td = document.createElement("td");
td.innerHTML = track.number;
row.appendChild(td);
//repeat for whatever other values you have for a track
//you'd likely add a play function or something of that sort to each.
table.appendChild(row);
});
table.appendChild(cur);
}