I am trying to sessionStorage a display state (block/none) of a div filter-panel--content when my site refreshes. I have multiple div's with that class. Inside that div are some checkboxes that refreshes the site after they change state.
That works perfectly, but when my site refreshes this div stays closed 'display:none'. But I try to sessionStorage the display state.
This is a cutting of my sidebar:
<div class="filter-panel filter--property facet--property" data-filter-type="value-list" data-field-name="f">
<div class="filter-panel--flyout" style="border:none;">
<div id="label-it" class="label-it"><label class="filter-panel--title"><h3 class="rums">LED</h3><div id="klapp" class="klapp"></div></label></div>
<div class="filter-panel--content">
[...]
</div>
<div class="filter-panel filter--property facet--property" data-filter-type="value-list" data-field-name="f">
<div class="filter-panel--flyout" style="border:none;">
<div id="label-it" class="label-it"><label class="filter-panel--title"><h3 class="rums">Housing style</h3><div id="klapp" class="klapp"></div></label></div>
<div class="filter-panel--content">
[...]
</div>
When the div "label-it" is clicked, the div "filter-panel--content" toggles display:none and display:block, inside "filter-panel--content" are some checkboxes that refreshes the site.
And at this point I want to store the display property of "filter-panel--content"
This is my first solution of getting the sessionStorage:
sessionStorage.setItem("filterstate", $(this).closest(".filter-panel--flyout").find(".filter-panel--content div").css("display"));
This is my first solution of setting the sessionStorage, it's in the toggle function:
$('.label-it').click(function() {
$(this).find(".filter-panel--title div").toggleClass('klapp klappe');
$(this).closest(".filter-panel--flyout").find(".filter-panel--content div").toggle(350)
sessionStorage.setItem("filterstate", $(this).closest(".filter-panel--flyout").find(".filter-panel--content div").css("display"));
});
I need some help :)
2nd Try:
I tried to implement localStorage unfortunately it doesn't work.. I rly need some help..
SET:
$('.label-it').click(function() {
$(this).find(".filter-panel--title div").toggleClass('klapp klappe');
$(this).closest(".filter-panel--flyout").find(".filter-panel--content div").toggle(350)
var panel = $(this).closest(".filter-panel--flyout").find(".filter-panel--content div");
if ( panel.style.display === "block") {
localStorage.setItem("isOpen", true);
} else {
localStorage.setItem("isOpen", false);
}
});
GET:
$(document).ready(function() {
var isOpen = localStorage.getItem("isOpen");
if (isOpen === "true") {
$(this).closest(".filter-panel--flyout").find(".filter-panel--content div").css('display', 'block');
} else {
$(this).closest(".filter-panel--flyout").find(".filter-panel--content div").css('display', 'none');
}
});
With sessionStorage.setItem("filterstate", $(this).closest(".filter-panel--flyout").find(".filter-panel--content div").css("display")); you're setting filterstate to the display-value like block or none.
This at its own couldnt do what you want to. You have to know wich element you want to set to this saved display state.
For this you should better set an unique id="[...]" to each panel you would
like to use for this and setting the selected ids into the storage.
Then you could check your sessionStorage for the ids, search your
DOM for its elements and toggle them.
Furthermore your getting is a setting. To get something you have to use in your example sessionStorage.getItem('filterstate');
For example:
set: sessionStorage.setItem("filterstate", $(this).closest(".filter-panel--flyout").find(".filter-panel--content div").attr('id'));
get: $('#' + sessionStorage.setItem('filterstate').css('display', 'block');
As you said, you cant set unique ids. Here is an example using jQuerys .index():
set: sessionStorage.setItem("filterstate", $(this).parent().index(this);
get: $('[ ... Parent ... ]').eq(sessionStorage.setItem('filterstate')).css('display', 'block');
(Not tested)
The following is a tested example for
somethinge where more than one panel could be open at the same time. (demo)
Note the comments to learn more:
$('.clickable').click(function() {
var panel = $(this).closest(".wrapper").find(".childToShow"); // get panel
panel.toggle(350, 'swing', function() { // toggle panel and execute function !!!after!!! toggle
var storage = localStorage.getItem('indexes'); // get localStorage
var index = $(this).parent().index(); // get index of childWrapper (parent of .clickable and .childToShow)
if ($(panel).css('display') === "block") { // check if panel is showen
storage = storage ? storage + ';' + index : index; // if panel is showen !!!add!!! its .wrappers index to the localStorage
// you should not overwirte the storage with the index, becaus more than one panel could be open at the same time.
} else { //if panel is closed remove index from localStorage
var newStorage = ''; // initialize new string for localStorage
var storageArr = storage.split(';'); // split actual storage string into an array
storageArr.splice(storageArr.indexOf(index), 1); // remove from storage array
for (var i = 0; i < storageArr.length; i++) {
newStorage = i === 0 ? newStorage + storageArr[i] : newStorage + ';' + storageArr[i]; // build new storage string
}
storage = newStorage; // set storage variable to the new one
}
localStorage.setItem('indexes', storage); // write into storage
});
});
$(document).ready(function() {
console.log('Your "indexes" storage looks like: ', localStorage.getItem("active2"))
var storage = localStorage.getItem("indexes") // get localStorage
if (storage) { // check if storage is not empty
storage = localStorage.getItem("indexes").split(';'); // get storage array
for (var i = 0; i < storage.length; i++) {
$('.wrapper').eq(storage[i]).find(".childToShow").css('display', 'block'); // show each saved wrappers (its index is in the storage) .childToShow
}
}
});
Related
I'll try to keep it at simple as I can.
I have a JSON object that is pulled via AJAX. I am displaying a list of icons in a main div from the data dynamically which can be toggled on or off.
I have a secondary div where the selected items are appearing, while the main div icon receives a class of active.
I want the end user to be able to remove any of them by clicking on them on either the main div or secondary div.
Most of this is working, but I'm having trouble figuring out the best way to map them together so that I have 2 separate click events which can control the same outcome.
I think it may have something to do with the fact that I'm dynamically creating elements... which create more elements... which have to alter the initial elements.
My structure so far is to map the current selection inside of an array. This gives me control over keeping a code-based list of everything that is selected (there is much more data than in the example I'll be providing).
So, here is how I have it so far:
HTML:
<div id="options"></div>
<div id="selectedOptions"></div>
Javascript/jQuery:
// Simple simulated AJAX data
var ourData = {
"color1": "yellow",
"color2": "blue"
};
var $options = $('#options');
var $selectedOptions = $('#selectedOptions');
// Array to keep track of which objects are selected
var selectedOptions = [];
// Makes the initial option dynamic list
makeOptions(ourData, $options);
// If an option from the main div is clicked, handle class changing
$('button').on('click', function(){
pickOption($(this));
});
/* This function is the problem. The option gets removed from the array, and from the secondary div, but the class of active still occurs on the main div. */
$selectedOptions.on('click', '.optionActive', function(){
var option = $(this).data('color');
var optionPosition = jQuery.inArray(option, selectedOptions);
selectedOptions.splice(optionPosition, 1);
displayOptions(selectedOptions, $selectedOptions);
});
// Creates initial icons (buttons in this case) to the DOM and applies a data-attribute for the color
function makeOptions(options, $container){
var $results = $('<div id="results">');
$.each(options, function(key, value){
var $optionButton = $('<button>' + key + ':' + value + '</button>');
$optionButton.data('color', value);
$results.append($optionButton);
});
$container.append($results);
}
/* Handler for selecting an option from the Main Div. Handling the class active.
I am not using a simple classToggle because there are many situations where a click is not allowed */
function pickOption($option){
var selectedOption = $option.data('color');
// If the option returns true, or that it doesn't exist yet
if(modifyOptions(selectedOption, selectedOptions)){
$option.addClass('active');
} else {
$option.removeClass('active');
}
// Recreate our current selected options
displayOptions(selectedOptions, $selectedOptions);
}
/* Searches array to see if the option exists. Returns true or false and either pushes or splices the option from the array */
function modifyOptions(option){
var optionPosition = jQuery.inArray(option, selectedOptions);
if(optionPosition == -1){
selectedOptions.push(option);
return true;
} else {
selectedOptions.splice(optionPosition, 1);
return false;
}
}
/* Displays all currently selected options that exist in our array */
function displayOptions(selectedOptions, $container){
$container.empty();
$.each(selectedOptions, function(option, value){
var $optionTile = $('<div class="optionActive">');
$optionTile.data('color', value)
.text(option + ":" + value)
.css('background', value);
$container.append($optionTile);
});
}
So, to summarize, I want some some way to remove the .active class from the main div equivalent element when the item from the second div is clicked.
I tried removing the class active by searching for any elements with the data-color=data-color of the selected item, but I couldn't get that to work.
ex:
$('*[data-color="' + $(this).data('color') + '"]').removeClass('active');
I would really like some data approach to this, such as removing the class active if it had data-color="yellow" for instance.
Playground:
https://jsfiddle.net/c75xcLha/
EDIT:
Both Are Selected, working as designed:
Clicked Yellow Div. Yellow Button is still active:
Should remove the active class from the button when the yellow div OR the button is pressed, as shown here:
You are assigning data-* property using .data(PROP), not attribute hence element having data-* property could not be accessed/selected using attribute-selector, assign attribute using .attr('data-color') instead of .data(property)
Attribute Equals Selector [name=”value”], Selects elements that have the specified attribute with a value exactly equal to a certain value.
.data( key, value ), Store arbitrary data associated with the matched elements.
When you use .data() to update a data value, it is updating internal object managed by jQuery, so it will not be updated in the data-* attribute[Ref]
// Simple simulated AJAX data
var ourData = {
"color1": "yellow",
"color2": "blue"
};
var $options = $('#options');
var $selectedOptions = $('#selectedOptions');
// Array to keep track of which objects are selected
var selectedOptions = [];
// Makes the initial option dynamic list
makeOptions(ourData, $options);
// If an option from the main div is clicked, handle class changing
$('button').on('click', function() {
pickOption($(this));
});
/* This function is the problem. The option gets removed from the array, and from the secondary div, but the class of active still occurs on the main div. */
$selectedOptions.on('click', '.optionActive', function() {
var option = $(this).data('color');
var optionPosition = jQuery.inArray(option, selectedOptions);
selectedOptions.splice(optionPosition, 1);
$('[data-color="' + $(this).data('color') + '"]').removeClass('active');
displayOptions(selectedOptions, $selectedOptions);
});
// Creates initial icons (buttons in this case) to the DOM and applies a data-attribute for the color
function makeOptions(options, $container) {
var $results = $('<div id="results">');
$.each(options, function(key, value) {
var $optionButton = $('<button>' + key + ':' + value + '</button>');
$optionButton.attr('data-color', value);
//___________^^^^^^^^^^^^^^^^^^Little trick here!
$results.append($optionButton);
});
$container.append($results);
}
/* Handler for selecting an option from the Main Div. Handling the class active.
I am not using a simple classToggle because there are many situations where a click is not allowed */
function pickOption($option) {
var selectedOption = $option.data('color');
// If the option returns true, or that it doesn't exist yet
if (modifyOptions(selectedOption, selectedOptions)) {
$option.addClass('active');
} else {
$option.removeClass('active');
}
// Recreate our current selected options
displayOptions(selectedOptions, $selectedOptions);
}
/* Searches array to see if the option exists. Returns true or false and either pushes or splices the option from the array */
function modifyOptions(option) {
var optionPosition = jQuery.inArray(option, selectedOptions);
if (optionPosition == -1) {
selectedOptions.push(option);
return true;
} else {
selectedOptions.splice(optionPosition, 1);
return false;
}
}
/* Displays all currently selected options that exist in our array */
function displayOptions(selectedOptions, $container) {
$container.empty();
$.each(selectedOptions, function(option, value) {
var $optionTile = $('<div class="optionActive">');
$optionTile.data('color', value)
.text(option + ":" + value)
.css('background', value);
$container.append($optionTile);
});
}
.active {
background: #CCF;
}
.optionActive {
min-width: 100px;
min-height: 100px;
display: inline-block;
margin: 1em;
background: #eee;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div id="options"></div>
<div id="selectedOptions"></div>
Updated Fiddle
Edit: If you still want to stick with .data method, use .filter to select target element.
$('button').filter(function(){
return $(this).data('color')=='yellow';
}).removeClass('active');
Simply put, my goal is to make the "next" button add a class active to the previous list item, then if the active class exists, also prepend a YES to that list item, not all list items as it is currently doing in my code.
Currently, my attempt is adding YES to all list items, I want to add YES only to the previous items after each click.
My attempt is here:
var $tabs = $('li');
$('.next').on('click', function () {
$tabs.next('li').addClass("active").css("background-color","yellow");
if ($($tabs).next().hasClass("active")){
$($tabs).prepend('YES').prev();
} else {
$($tabs).append('NO');
}
});
Code can be seen here:
http://codepen.io/anon/pen/yrjvb
I'm not sure if I understood you correctly, but this might be what you're looking for :
var $tabs = $('li');
var count = 0;
$('.next').click(function () {
var tab = $tabs.eq(count);
var link = tab.find('a[data-toggle="tab"]');
link.addClass("active").css("background-color","yellow");
if (link.hasClass("active")) {
tab.prepend('YES');
count++;
} else {
tab.append('NO');
}
});
Forked Codepen : http://codepen.io/anon/pen/pnKAc?editors=101
Basically, you were selecting all the <li> items. In my code, I select an item one-by-one.
Surely there is a better way to do it (selecting directly the next element etc., but I guess with this code you can see what's different.
Also, you say you want to add, did you mean "append", then ?
How about this approach (codepen): Store the current one, clear its active state, pick the next one ?
$('.next').on('click', function () {
var $current = $('li.active');
if(!$current.length){
$current = $('li:first');
$current.addClass('active');
}else{
$current.removeClass('active');
$current.next('li').addClass('active');
}
});
I have a lot of click handler functions which are almost (textually and functionally) identical. I've got a menu with maybe 10 items in it; when I click on an item, the click handler simply makes one div visible, and the other 9 div's hidden. Maintaining this is difficult, and I just know there's got to be a smart and/or incomprehensible way to reduce code bloat here. Any ideas how? jQuery is Ok. The code at the moment is:
// repeat this function 10 times, once for each menu item
$(function() {
$('#menuItem0').click(function(e) {
// set 9 divs hidden, 1 visble
setItem1DivVisible(false);
// ...repeat for 2 through 9, and then
setItem0DivVisible(true);
});
});
// repeat this function 10 times, once for each div
function setItem0DivVisible(on) {
var ele = document.getElementById("Item0Div");
ele.style.display = on? "block" : "none";
}
Create 10 div with a class for marking
<div id="id1" class="Testing">....</div>
<div id="id2" class="Testing">....</div>
<div id="id3" class="Testing">....</div>
and apply the code
$('.Testing').each(function() {
$(this).click(function() {
$('.Testing').css('display', 'none');
$(this).css('display', 'block');
}
}
$(document).ready(function (){
$("div").click(function(){
// I am using background-color here, because if I use display:none; I won't
// be able to show the effect; they will all disappear
$(this).css("background-color","red");
$(this).siblings().css("background-color", "none");
});
});
Use .siblings() and it makes everything easy. Use it for your menu items with appropriate IDs. This works without any for loops or extra classes/markup in your code. And will work even if you add more divs.
Demo
Fiddle - http://jsfiddle.net/9XSJW/1/
It's hard to know without an example of the html. Assuming that there is no way to traverse from the menuItem to ItemDiv - you could use .index and .eq to match up the elements based on the order they match with the selector.
var $menuItems = $("#menuItem0, #menuItem1, #menuItem2, ...");
var $divs = $("#Item0Div, #Item1Div, #Item2Div, ...");
$menuItems.click(function(){
var idx = $(this).index();
// hide all the divs
$divs.hide()
// show the one matching the index
.eq(idx).show();
})
Try
function addClick(i) {
$('#menuItem'+i).click(function(e) {
// set nine divs hidden, 1 visble
for( var j = 0; j < 10; ++j ) {
var ele = document.getElementById("Item"+j+"Div");
ele.style.display = (i == j ? "block" : "none");
}
});
}
// One click function for all menuItem/n/ elements
$('[id^="menuItem"]').on('click', function() {
var id = this.id; // Get the ID of the clicked element
$('[id^="Item"][id$="Div"]').hide(); // Hide all Item/n/Div elements
$('#Item' + id + 'Div').show(); // Show Item/n/Div related to clicked element
});
Obviously this would be much more logical if you were using classes instead:
<elem class="menuItem" data-rel="ItemDiv-1">...</elem>
...
<elem class="ItemDiv" id="ItemDiv-1">...</elem>
$('.menuItem').on('click', function() {
var rel = $(this).data('rel'); // Get related ItemDiv ID
$('.ItemDiv').hide(); // Hide all ItemDiv elements
$('#' + rel).show(); // Show ItemDiv related to clicked element
});
Save the relevant Id's in an array - ["Item0Div", "Item1Div", ...]
Create a generic setItemDivVisible method:
function setItemDivVisible(visible, id) {
var ele = document.getElementById(id);
ele.style.display = visible ? "block" : "none";
}
And set your click handler method to be:
function(e) {
var arrayLength = myStringArray.length;
for (var i = 0; i < idsArray.length; i++) {
setItemDivVisible(idsArray[i] === this.id, idsArray[i]);
}
}
I think this will do the trick
I have a snippet of code that applies a highlighting effect to list items in a menu (due to the fact that the menu items are just POST), to give users feedback. I have created a second step to the menu and would like to apply it to any element with a class of .highlight. Can't get it to work though, here's my current code:
[deleted old code]
The obvious work-around is to create a new id (say, '#highlighter2) and just copy and paste the code. But I'm curious if there's a more efficient way to apply the effect to a class instead of ID?
UPDATE (here is my updated code):
The script above DOES work on the first ul. The second ul, which appears via jquery (perhaps that's the issue, it's initially set to hidden). Here's relevant HTML (sort of a lot to understand, but note the hidden second div. I think this might be the culprit. Like I said, first list works flawlessly, highlights and all. But the second list does nothing.)?
//Do something when the DOM is ready:
<script type="text/javascript">
$(document).ready(function() {
$('#foo li, #foo2 li').click(function() {
// do ajax stuff
$(this).siblings('li').removeClass('highlight');
$(this).addClass('highlight');
});
//When a link in div is clicked, do something:
$('#selectCompany a').click(function() {
//Fade in second box:
//Get id from clicked link:
var id = $(this).attr('id');
$.ajax({
type: 'POST',
url: 'getFileInfo.php',
data: {'id': id},
success: function(msg){
//everything echoed in your PHP-File will be in the 'msg' variable:
$('#selectCompanyUser').html(msg)
$('#selectCompanyUser').fadeIn(400);
}
});
});
});
</script>
<div id="selectCompany" class="panelNormal">
<ul id="foo">
<?
// see if any rows were returned
if (mysql_num_rows($membersresult) > 0) {
// yes
// print them one after another
while($row = mysql_fetch_object($membersresult)) {
echo "<li>"."".$row->company.""."</li>";
}
}
else {
// no
// print status message
echo "No rows found!";
}
// free result set memory
mysql_free_result($membersresult);
// close connection
mysql_close($link);
?>
</ul>
</div>
<!-- Second Box: initially hidden with CSS "display: none;" -->
<div id="selectCompanyUser" class="panelNormal" style="display: none;">
<div class="splitter"></div>
</div>
You could just create #highlighter2 and make your code block into a function that takes the ID value and then just call it twice:
function hookupHighlight(id) {
var context = document.getElementById(id);
var items = context.getElementsByTagName('li');
for (var i = 0; i < items.length; i++) {
items[i].addEventListener('click', function() {
// do AJAX stuff
// remove the "highlight" class from all list items
for (var j = 0; j < items.length; j++) {
var classname = items[j].className;
items[j].className = classname.replace(/\bhighlight\b/i, '');
}
// set the "highlight" class on the clicked item
this.className += ' highlight';
}, false);
}
}
hookupHighlight("highliter1");
hookupHighlight("highliter2");
jQuery would make this easier in a lot of ways as that entire block would collapse to this:
$("#highlighter1 li, #highlighter2 li").click(function() {
// do ajax stuff
$(this).siblings('li').removeClass('highlight');
$(this).addClass('highlight');
});
If any of the objects you want to click on are not initially present when you run this jQuery code, then you would have to use this instead:
$("#highlighter1 li, #highlighter2 li").live("click", function() {
// do ajax stuff
$(this).siblings('li').removeClass('highlight');
$(this).addClass('highlight');
});
change the replace in /highlight/ig, it works on http://jsfiddle.net/8RArn/
var context = document.getElementById('highlighter');
var items = context.getElementsByTagName('li');
for (var i = 0; i < items.length; i++) {
items[i].addEventListener('click', function() {
// do AJAX stuff
// remove the "highlight" class from all list items
for (var j = 0; j < items.length; j++) {
var classname = items[j].className;
items[j].className = classname.replace(/highlight/ig, '');
}
// set the "highlight" class on the clicked item
this.className += ' highlight';
}, false);
}
So all those guys that are saying just use jQuery are handing out bad advice. It might be a quick fix for now, but its no replacement for actually learning Javascript.
There is a very powerful feature in Javascript called closures that will solve this problem for you in a jiffy:
var addTheListeners = function (its) {
var itemPtr;
var listener = function () {
// do AJAX stuff
// just need to visit one item now
if (itemPtr) {
var classname = itemPtr.className;
itemPtr.className = classname.replace(/\bhighlight\b/i, '');
}
// set the "highlight" class on the clicked item
this.className += ' highlight';
itemPtr = this;
}
for (var i = 0; i < its.length; i++) {
its[i].addEventListener ('click', listener, false);
}
}
and then:
var context = document.getElementById ('highlighter');
var items = context.getElementsByTagName ('li');
addTheListeners (items);
And you can call add the listeners for distinct sets of doc elements as many times as you want.
addTheListeners works by defining one var to store the list's currently selected item each time it is called and then all of the listener functions defined below it have shared access to this variable even after addTheListeners has returned (this is the closure part).
This code is also much more efficient than yours for two reasons:
You no longer iterate through all the items just to remove a class from one of them
You aren't defining functions inside of a for loop (you should never do this, not only for efficiency reasons but one day you are going to be tempted to use that i variable and its going to cause you some problems because of the closures thing I mentioned above)
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.