I'm probably in deep water here, and I'm new to JavaScript and jQuery,
But I would like to attempt to create a panel system sort of like how spotify has it done.
Have a look at this picture:
On the Spotify player website, when you click on something such as an artist or song/album,
It slides in a topmost panel onto the right side of the screen, if you click something else,
A new one will appear in its place, and the previous panel will get added to what I call the panel stack on the left side of the screen.
Here is a look at Spotify:
I'd like to understand how to do this in JavaScript/jquery, does anyone have some input to share?
What I've tried: (http://jsfiddle.net/k0Lh3ama/)
I understand my attempt was pretty poor, this is why I'm here
var idx = 0;
var panels = [];
var numpanels = 0;
function panel () {
this.id = 0;
this.active = false;
this.container = {};
}
var panelmgr = new function () {
this.Create = function (lpPanel) {
//set all current panels to inactive
//and then push them left
for (var i = 0; i < panels.length; i++) {
panels[i].active = false;
panels[i].container.css("left", "10%");
}
//set the newest panel to Active and Top Most
lpPanel.container.css("z-index", 10000);
lpPanel.container.css("left", "25%");
//setup some info for the new panel
lpPanel.id = numpanels++;
lpPanel.active = true;
//add it to array
panels.push(lpPanel);
};
}
$(".box").click(function (e) {
var id = $(this).attr("id");
var selected = -1;
//find the panel we've selected and retrieve the index
for (var i = 0; i < panels.length; i++) {
if (id == panels[i].container.attr("id")) {
selected = I;
break;
}
}
//do we have a valid selected panel?
if (selected >= 0) {
//Make all panels not selected
for (var i = 0; i < panels.length; i++) {
panels[i].active = false;
panels[i].container.css("left", "10%");
}
//now make the panel we selected
//the top most panel
panels[selected].active = true;
panels[selected].container.css("z-index", 10000);
panels[selected].container.css("left", "25%");
}
});
$(document).ready(function () {
var p1 = new panel();
var p2 = new panel();
var p3 = new panel();
var p4 = new panel();
var p5 = new panel();
p1.container = $("#panel1");
p2.container = $("#panel2");
p3.container = $("#panel3");
p4.container = $("#panel4");
p5.container = $("#panel5");
panelmgr.Create(p1);
panelmgr.Create(p2);
panelmgr.Create(p3);
panelmgr.Create(p4);
panelmgr.Create(p5);
});
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);
}
This isn't exactly what you're looking for, nor is it at all related to the code you posted. But heres something I threw together that might be a good resource for you to look at. It doesn't implement the stack, but you could apply the concepts fairly easily. Check it out.
You could get rid of the createPanel function and pre-create/assign all the panels in an init function, and then have controllers that use those panels as a base. Then it's just a matter of controlling whats showing in the DOM
display: none
will effectively remove an element from the DOM remember, and can be used to hide certain panels.
Check this fiddle : http://jsfiddle.net/k0Lh3ama/1/
Do the css stuff. I am not master in it..
Your CSS ::
.sidebar{
width:10%;
background-color:#000;
color:#FFF;
height:800px;
float:left;
}
.box{
width:7%;
float:left;
height:800px;
background-color:red;
border:1px solid #000;
}
.active{
width:64%;
float:right !important;
-webkit-transition: all 1s ease-in-out;
-moz-transition: all 1s ease-in-out;
-o-transition: all 1s ease-in-out;
transition: all 1s ease-in-out;
}
Your Html Code
SideBar
<div id="panel2" class="box active">Panel 1 </div>
<div id="panel3" class="box">Panel 2 </div>
<div id="panel4" class="box">Panel 3</div>
<div id="panel5" class="box">Panel 4 </div>
Jquery
$(function(){
$(".box").click(function(){
$(".box").removeClass("active");
$(this).addClass("active");
});
});
You probably shouldn't be changing the CSS. Basically, what you want are two divs, one that holds a list of "panels" and one that holds a detailed view of a single "panel". I see this problem as breaking down into a number of sub-problems:
1) Get a list of panel objects from the server, and dynamically populate the left div, more than likely with sub-divs that hold the panel info. In other words, at page load some code will need to run that will loop through the list and add divs to the left div, one for each panel, and CSS for those pieces should handle how they get laid out and everything. A simple place to start might be to just add a single button for each item in the list, or a single image, etc.
2) Event handler so that when you click on a panel in the list it loads the detailed view into the right div. This may also remove the panel from the list, or change it visually in some way (gray it out), or whatever.
3) Code to display detailed info in the right div if an item has been loaded into it, and doing some other default if not.
One way to cheat a little to simplify all this might be to have the panels and detailed views be pre-built pages, and load them into the main page in iframes or the like.
Related
I have these sections on this side scrolling site. And want to add a class which will change styling depending if you're on a certain section.
I'm working on this function. The top is what determines the section of the side scroller you are viewing.
The let variables and below is where it stops working. I'm trying to have it so if a nonHome ID section is clicked, for example "slide-1", then add the class 'nav-visibilty'. If they are a match "slide-2" and "slide-2" then remove said class. Am I close?
https://codepen.io/mikayp-the-styleful/pen/NWPxoXR?editors=1111
setTimeout(function(){
for (i=0; i < nonHome.length; i++ ){
if (nonHome[i].id != nonHomeID){
nonHome[i].classList.add("nav-visibility");
console.log('add')
} else{
nonHomeID.classList.remove("nav-visibility");
console.log('rem')
}
}
I am still not totally clear on the behavior that you want, but there are two errors in the code that can be fixed:
It seems like you are always using 'slide-2' instead of the slideId in your event handler.
As mentioned in a comment, nonHomeID is being used incorrectly in your comparison (it is either a string or an element, but you are using it as if it was a string in the if condition, and as the element in the else branch.) Here I have kept it as an element and renamed it for clarity.
Fixing these errors results in code that applies the nav-visibility class to all slides except the one selected by the button. Is that the desired behavior?
let nonHome = document.querySelectorAll(".slide-container section");
let nonHomeSelected = document.getElementById(slideId);
var i;
setTimeout(function() {
for (i = 0; i < nonHome.length; i++) {
if (nonHome[i] != nonHomeSelected) {
nonHome[i].classList.add("nav-visibility");
console.log("add");
} else {
nonHome[i].classList.remove("nav-visibility");
console.log("rem");
}
}
}, 1000);
Edit to add: If the goal is to add nav-visibility to all only the specific slideId, you should not be adding in a loop, i.e. you need to pull your check for whether the slide is Home outside the loop. There are conceptually two steps here: remove the class from all elements that are no longer to have it, then add the class to the element that needs it.
let slideToAddVisibilityTo = document.getElementById(slideId)
let homeSlide = document.getElementById('slide-2')
let allSlides = document.querySelectorAll(".slide-container section")
for (let i = 0; i < allSlides.length; ++i)
allSlides[i].classList.remove('nav-visiblity')
if (slideToAddVisibilityTo != homeSlide)
slideToAddVisibilityTo.classList.add('nav-visibility')
Just hide them all, then show the clicked one:
function showSection(id) {
var sections = document.getElementsByTagName("section");
for(var i=0; i<sections.length; i++) sections[i].classList.remove("nav-visibility");
var current = document.getElementById(id);
current.classList.add("nav-visibility");
}
Example: showSection("foo") will remove nav-visibility from all sections, then add it to the section with id foo.
I have a grid with a good amount of data which the user can filter or show/hide groups of columns by using combo boxes. Some of the column switches take a long time to load and I want to call setLoading(true,true) on the grid or the combobox to show the user that the browser is working.
I have a listener function that is called when the user makes a selection on the combo box. I call combo.setLoading(true,true) before starting any of the code that takes a while to execute. Then I call combo.setLoading(false,false) at the very bottom of the function.
The loading only shows up for a split second after the code between the two setLoading calls has executed. If I take out the call to remove the loading icon, the icon still only shows up after the code is executed.
Anyone have an idea what is happening? This seems very odd to me.
categorycomboselect: function(combo, records){
combo.setLoading(true,true);
var panel = combo.up('panel');
console.log(panel);
var category = records[0].data.name;
var grid = Ext.ComponentQuery.query('grid')[1];
Ext.suspendLayouts();
grid.suspendedLayout=true;
var columns = grid.columns;//.getView().getGridColumns();
//slow code that shows/hides columns
grid.suspendedLayout=false;
Ext.resumeLayouts(true);
combo.setLoading(false,false);
},
UPDATE
Here is my code with Trimboli's suggestion, it still isn't working. I'm showing/hiding the columns based on a string in their ID. I did it this way because the categories I want to show/hide the columns on are dynamic and the columns are dynamic.
categorycomboselect: function(combo, records){
combo.setLoading(true,true);
setTimeout( function(){
var panel = combo.up('panel');
var category = records[0].data.name;
var grid = Ext.ComponentQuery.query('grid')[1];
Ext.suspendLayouts();
grid.suspendedLayout=true;
var columns = grid.columns;
if(category=='All grade items'){
for(var i = 0; i< columns.length; i++){
columns[i].setVisible(true);
}
}else{
for(var i = 0; i< columns.length; i++){
columns[i].hide();//setVisible(false);
if(!(typeof columns[i].itemId ==='undefined')){
if((columns[i].itemId).indexOf(category)>=0){
columns[i].show();
}
}
}
}
grid.suspendedLayout=false;
Ext.resumeLayouts(true);
combo.setLoading(false,false);
},1);
},
Also, I wasnt sure if
Ext.suspendLayouts();
grid.suspendedLayout=true;
do the same thing or not. And if not, which is better.
What you're essentially doing is this:
showMask();
for (i = 0; i < 10000000; ++i) {
// busy loop
}
hideMask();
If you show/hide a mask without giving the browser a "break" for it to update the DOM, you won't see anything because it all gets flushed out at the end once you relinquish control back to the browser.
If you wanted to let the mask actually show, you need to give a slight delay before continuing on with your busy loop:
showMask();
setTimeout(function() {
// Busy stuff
hideMask();
}, 1);
I am writing a function that will be executed on multiple views of an application, and each view can have up to 50 instances of the same element: '.console'. I need to be able to perform an action every time the viewport scrolls to each instance. I have the following code setting up the variables:
//Create empty array with variable values, up to 50
var console = [];
//Find each instance of ".console" and populate the array with its pixel position.
$('.console').each(function() {
console.push($(this)[0].offsetTop);
});
//Determine the current pixel position of the scroll
var scroll = $(document).scrollTop();
Those variables all work fine and dandy, but after hours of pouring over jquery docs I can't figure the if statement out. Here is what I have that works well for the first item in the array:
if (scroll == console[0]){
$('.container').show();
} else {
$('.container').hide();
}
However, I want it to be anytime the scroll position matches each of the values in that array, hopefully something like this:
if (scroll == console[0-50])
Here is the full chunk as is:
$(document).on('scroll', function(){
//Create empty array with variable values, up to 50
var console = [];
//Find each instance of ".console" and populate the array with its pixel position.
$('.console').each(function() {
console.push($(this)[0].offsetTop);
});
//Determine the current pixel position of the scroll
var scroll = $(document).scrollTop();
//Anytime the scroll matches any of the instances of console, show a div
if (scroll == console[0]){
$('.container').show();
} else {
$('.container').hide();
}
});
Any help would be appreciated. I am pretty new to Javascript/JQuery so if I'm approaching the problem in the wrong way altogether, please let me know. Thanks!
Since you said it works for the first one, I'm guessing this may work.
// cache the container
var container = $('.container');
$(document).on('scroll', function(){
//Determine the current pixel position of the scroll
var scroll = $(document).scrollTop();
//Create empty array with variable values, up to 50
var console = [];
//Find each instance of ".console" and populate the array with its pixel position.
$('.console').each(function(index) {
console.push($(this)[0].offsetTop);
if (scroll == console[index]){
$(container).show();
} else {
$(container).hide();
}
});
});
You may wish to take a look at Waypoints. It's a jQuery plugin that is well suited for what you're trying to accomplish.
I whipped up a quick jsFiddle to show it in action: http://jsfiddle.net/dmillz/4xqMb/
$(".console").waypoint(function(direction) {
// Hide or show your ".container" object
});
More Waypoint examples: http://imakewebthings.com/jquery-waypoints/#get-started
Hopefully I understand your problem, which is as follows:
You have a bunch of elements with the .console class, and you want to appear as soon as they are in the viewport. When these elements aren't in the viewport you want them to dissapear?
Since you're interested in when these objects with the .console class are in the viewport, I suggest using this jQuery plugin
http://plugins.jquery.com/appear/
https://github.com/morr/jquery.appear
I suggest wrapping each of the .console objects in a container with another class, and then as these containers appear and disappear show and hide them.
At document ready just do the following:
$(document).ready(function() {
$('<.container-class>').appear();
$('<.container-class>').on('appear', function() { $(this).find('.console').show(); });
$('<.container-class>').on('disappear', function() { $(this).find('.console').hide(); });
});
To answer the question, you could do this:
var cons = $.map($('.console'), function(el) {
return $(el).offset().top;
});
$(document).on('scroll', function(){
var scroll = $(window).scrollTop();
$('.container').toggle( $.inArray(scroll, cons) != -1 );
});
But creating something for a range, considering the height of each element, the height of the window etc. would be a lot more involved.
While the problem was solved via another answer, figuring out how to perform a loop for each value in the array wasn't really solved ... UNTIL NOW!
This is probably a really gross and bloated way to do it, but if you essentially count how many items are in the array, you can then run a loop that many times, putting in the index for each value in the array. Code below:
//Create empty array with variable values
var console = [];
//Find each instance of ".console" and populate the array with its pixel position.
$('.console').each(function() {
console.push($(this)[0].offsetTop);
});
//Count the number of items in the array
var consoleIndex = console.length - 1;
$(document).on('scroll', function(){
//Determine the current pixel position of the scroll
var scroll = $(document).scrollTop();
//Anytime the scroll matches any of the instances of console, show a div
for (var i = 0; i <= consoleIndex; i++) {
if (scroll = console[i]) {
$('.container').toggle();
}
}
});
let me just give a quick story. I have made a page. (VERY simple - two divs with a different background image, see here.)
Anyway, I need to make it so that when a new page loads, the two divs that I have load in a random order over and over, filling the entire screen content. So there's no pattern of the first div and then the second, it's just randomly generated. Sort of like a huge grid, with the two divs repeated with no pattern.
My question is...is that possible? I assume I'd need to know PHP, but I have no knowledge of it.
Thanks guys, I appreciate all help!
http://jsfiddle.net/uYPRq/
jquery
var div1 = '<div class="one">';
var div2 = '<div class="two">';
var len =
Math.floor(window.innerWidth/30)*Math.floor(window.innerHeight/30);
for (x = 0; x < len; x++) {
if ( Math.random() > 0.5 ) {
$(div1).appendTo('body');
}
else {
$(div2).appendTo('body');
}
}
css
div.one, div.two {
height:30px;
width:30px;
float:left;
}
div.one { background-color:#EBE1E4; }
div.two { background-color:#F0F5DF; }
edit:
changed screen.availWidth to window.innerWidth
Something like so? Just loop through how ever many times you like and add elements in.
for (i = 0; i < 300; i++) {
var type1 = document.createElement("div");
var type2 = document.createElement("div");
type1.innerHTML = "div1";
type2.innerHTML = "div2";
type1.setAttribute("class", "type1");
type2.setAttribute("class", "type2");
document.body.appendChild(type1);
document.body.appendChild(type2);
}
No PHP needed. This can be done client-side using Javascript (Jquery might be easier).
I was taking a look at http://www.zenfolio.com/zf/features.aspx and I can't figure out how they are doing the accordion style expand and collapse when you click on the orange links. I have been using the Web Developer Toolbar add-on for firefox, but I have not been able to find anything in the source of the page like JavaScript that would be doing the following. If anyone knows how they are doing it, that would be very helpful.
This is actually unrelated, but if all you answers are good, who do I give the answer too?
They're setting the .display CSS property on an internal DIV from 'none' to '', which renders it.
It's a bit tricky, as the JS seems to be in here that's doing it:
http://www.zenfolio.com/zf/script/en-US/mozilla5/windows/5AN2EHZJSZSGS/sitehome.js
But that's basically how everyone does this. It's in there, somewhere.
EDIT: Here it is, it looks like:
//... (bunch of junk)
zf_Features.prototype._entry_onclick = function(e, index)
{
var cellNode = this.dom().getElementsByTagName("H3")[index].parentNode;
while (cellNode.tagName != "TD") cellNode = cellNode.parentNode;
if (this._current != null) this.dom(this._current).className = "desc";
if ("i" + index != this._current)
{
cellNode.className = "desc open";
cellNode.id = this.id + "-i" + index;
this._current = "i" + index;
}
else this._current = null;
zf_frame.recalcLayout();
return false;
};
Basically, what they're doing is a really roundabout and confusing way of making the div inside of TD's with a class "desc" change to the class "desc open", which reveals its contents. So it's a really obtuse roundabout way to do the same thing everyone does (that is, handling onclick to change the display property of a hidden div to non-hidden).
EDIT 2:
lol, while I was trying to format it, others found it too. =) You're faster than I ;)
Using jQuery, this effect can be built very easily:
$('.titleToggle').click(function() {
$(this).next().toggle();
});
If you execute this code on loading the page then it will work with any markup that looks like the following:
<span class="titleToggle">Show me</span>
<div style="display:none">This is hidden</div>
Notice that this code will work for any number of elements, so even for a whole table/list full of those items, the JavaScript code does not have to be repeated or adapted in any way. The tag names (here span and div) don't matter either. Use what best suits you.
It is being done with JavaScript.
When you click a link, the parent td's class changes from 'desc' to 'desc open'. Basically, the expanded text is always there but hidden (display: none;). When it gets the css class of 'open' the text is no longer being hidden (display: block;).
If you look in the sitehome.js and sitehome.css file you can get an idea about how they are doing that.
btw I used FireBug to get that info. Even though there is some feature duplication with Web Developer Toolbar it's worth the install.
They're using javascript. You can do it too:
<div id="feature">
Feature Name
<div id="desc" style=="display:none;">
description here...
</div>
</div>
<script type="text/javascript">
function toggle()
{
var el=document.getElementById('desc');
if (el.style.display=='none')
el.style.display='block'; //show if currently hidden
else
el.style.display='none'; //Hide if currently displayed
}
</script>
The function above can be written using Jquery for smooth fade in/fade out animations when showing/expanding the descriptions. It has also got a SlideUp and Slidedown effect.
There is a lot of obfuscated/minified JS in their master JS include. It looks like they are scraping the DOM looking for H3's and checking for table cells with class desc and then processing the A tags. ( or some other order, possibly ) and then adding the onclick handlers dynamically.
if (this._current != null) this.dom(this._current).className
= "desc"; if ("i" + index != this._current) { cellNode.className = "desc open"; cellNode.id = this.id
+ "-i" + index; this._current = "i" + index; }
http://www.zenfolio.com/zf/script/en-US/safari3/windows/5AN2EHZJSZSGS/sitehome.js
The script is here.
The relevant section seems to be (re-layed out):
// This script seems over-complicated... I wouldn't recommend it!
zf_Features.prototype._init = function()
{
// Get a list of the H3 elements
var nodeList = this.dom().getElementsByTagName("H3");
// For each one...
for (var i = 0; i < nodeList.length; i++)
{
// ... set the onclick to be the function below
var onclick = this.eventHandler(this._entry_onclick, i);
// Get the first <a> within the H3 and do the same
var node = nodeList[i].getElementsByTagName("A")[0];
node.href = "#";
node.onclick = onclick;
// And again for the first <span>
node = nodeList[i].getElementsByTagName("SPAN")[0];
node.onclick = onclick;
}
};
zf_Features.prototype._entry_onclick = function(e, index)
{
// Get the parent node of the cell that was clicked on
var cellNode = this.dom().getElementsByTagName("H3")[index].parentNode;
// Keep going up the DOM tree until we find a <td>
while (cellNode.tagName != "TD")
cellNode = cellNode.parentNode;
// Collapse the currently open section if there is one
if (this._current != null)
this.dom(this._current).className = "desc";
if ("i" + index != this._current)
{
// Open the section we clicked on by changing its class
cellNode.className = "desc open";
cellNode.id = this.id + "-i" + index;
// Record this as the current one so we can close it later
this._current = "i" + index;
}
else
this._current = null;
// ???
zf_frame.recalcLayout();
return false;
};
Edit: added some comments
Unfortunately their code is in-lined and hard to read (http://www.zenfolio.com/zf/script/en-US/mozilla5/windows/5AN2EHZJSZSGS/sitehome.js), but this looks quite simple to implement... something along these lines (using prototypejs):
<script>
var showHide = {
cachedExpandable: null
,init: function() {
var containers = $$(".container");
for(var i=0, clickable; i<containers.length; i++) {
clickable = containers[i].getElementsByClassName("clickable")[0];
Event.observe(clickable, "click", function(e) {
Event.stop(e);
showHide.doIt(containers[i]);
});
}
}
,doIt: function(container) {
if(this.cachedExpandable) this.cachedExpandable.hide();
var expandable = container.getElementsByClassName("expandable")[0];
if(expandable.style.display == "none") {
expandable.show();
} else {
expandable.hide();
}
this.cachedExpandable = expandable;
}
};
window.onload = function() {
showHide.init();
};
</script>
<div class="container">
<div class="clickable">
Storage Space
</div>
<div class="expandable" style="display: none;">
Description for storage space
</div>
</div>
<div class="container">
<div class="clickable">
Galleries
</div>
<div class="expandable" style="display: none;">
Description for galleries
</div>
</div>
Its also caching the earlier expandable element, so it hides it when you click on a new one.