Reputation: 161
The problem I am having is that I feel my Javascript is not very efficient and that there is a much better way to accomplish what I am trying to do. To show my problem, I have created a smaller version of my project in JSFiddle.
The ultimate goal is to have the blocks on the left show info in the large grey box when clicked. This is achieved in the JSFiddle! The problem is (I think its a problem) is that I have a separate javascript function for each block. (this is a small scale example, my project has 70+ "blocks")
In each function, all of the previous clicked blocks are hidden and their "clicked" class is removed. (Clicked class is basically a flag that tells the function whether to hide/show the info)
Here is example of one of the functions...is this the correct way to go about this? Remember, my project is this times 70 so I go through and hide each info block every time.
$('.block1').on('click', function (e) {
if ($(this).hasClass('block1-clicked')) {
$('.block1-info').fadeOut();
}
else {
$('.block2-info').fadeOut();
$('.block2').removeClass('block2-clicked');
$('.block3-info').fadeOut();
$('.block3').removeClass('block3-clicked');
$('.block4-info').fadeOut();
$('.block4').removeClass('block4-clicked');
$('.block1-info').fadeIn();
}
$(this).toggleClass('block1-clicked') ;
});
Thank you!!
Upvotes: 0
Views: 130
Reputation: 3950
Fiddle: http://jsfiddle.net/3A5M6/
Your code had so much redundancy that I kinda went all out and removed as much as I could everywhere:
HTML
<div id="blocksAndInfo">
<div class="blockContainer" onmousedown="return false">
<div class="block" id="b1">Block 1</div>
<div class="block" id="b2">Block 2</div>
<div class="block" id="b3">Block 3</div>
<div class="block" id="b4">Block 4</div>
</div>
<div class="infoContainer">
<div class="info b1">Info about Block 1</div>
<div class="info b2">Info about Block 2</div>
<div class="info b3">Info about Block 3</div>
<div class="info b4">Info about Block 4</div>
</div>
</div>
JS
$('.block').on('click',function(){
if ($(this).hasClass('active')) {
//deactivate the clicked block, and hide its info
$(this).removeClass('active');
$('.info.'+$(this).attr('id')).fadeOut();
} else {
//first deactivate any possible active block, and hide its info
$('.block').removeClass('active');
$('.info').fadeOut();
//then activate the clicked block, and show its info
$(this).addClass('active');
$('.info.'+$(this).attr('id')).fadeIn();
}
});
CSS
#blocksAndInfo {
height: 190px;
margin: 20px 0px 0px 20px;
}
.blockContainer {
float: left;
width: 140px;
height: 100%;
}
.block {
width: 100%;
margin: 0px 0px 10px 0px;
padding: 5px 0px;
border: 2px solid transparent;
cursor: pointer;
}
.block:last-child {margin-bottom:0px;}
.block.active {border:2px solid black;}
.block#b1 {background-color:red;}
.block#b2 {background-color:blue;}
.block#b3 {background-color:green;}
.block#b4 {background-color:orange;}
.infoContainer {
position: relative;
float: left;
width: 140px;
height: 100%;
margin: 0px 0px 0px 20px;
padding 5px;
background-color: grey;
}
.info {
display: none;
position: absolute;
width: 100%;
height: 100%;
}
The CSS in the jsFiddle contains some more code though, some extra styling touches.
Upvotes: 0
Reputation: 43748
(This is my 2nd answer, and I think is more robust than my 1st one...)
This solution doesn't need any special numbered classes or IDs, so it is easy to just insert 70 sections without having to specify a class on every one.
Here is a working jsFiddle of what I think is a cleaner solution. It only uses 1 click handler for all the 'buttons' on the left, and uses CSS3 transitions to fade out/in the info boxes. It also ditches the data-target
and boxN-info
attributes and classes, and instead just uses the indexes, so it assumes the buttons and info blocks have the same number and are in the same order:
Html:
<ul class="block-buttons">
<li class="active">Block 1</div>
<li>Block 2</div>
<li>Block 3</div>
<li>Block 4</div>
</ul>
<div class="info-blocks">
<div class="active">Info about Block 1</div>
<div>Info about Block 2</div>
<div>Info about Block 3</div>
<div>Info about Block 4</div>
</div>
CSS
.block-buttons {
list-style: none;
margin: 0;
padding: 0;
float: left;
}
.block-buttons li {
border: 1px solid #777;
background-color: #eee;
cursor: pointer;
margin-bottom: 2px;
padding: 2px;
}
.block-buttons li.active {
background-color: #aaa;
}
.info-blocks {
position: relative;
min-width: 100px;
min-height: 100px;
border: 1px solid #777;
margin-left: 10px;
float: left;
}
.info-blocks > div {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 0;
transition: opacity 0.4s ease-in 0s;
-ms-transition: opacity 0.4s ease-in 0s;
-moz-transition: opacity 0.4s ease-in 0s;
-webkit-transition: opacity 0.4s ease-in 0s;
}
.info-blocks > div.active {
opacity: 1;
transition: opacity 0.4s ease-out 0.4s;
-ms-transition: opacity 0.4s ease-out 0.4s;
-moz-transition: opacity 0.4s ease-out 0.4s;
-webkit-transition: opacity 0.4s ease-out 0.4s;
}
JS:
$(".block-buttons").on("click", "li", function (clickEvent) {
var $clickedElement = $(clickEvent.target);
var index = $clickedElement.index();
// set class on selected button
$(".block-buttons > li").removeClass("active");
$clickedElement.addClass("active");
// set class on selected panel
$(".info-blocks > div")
.removeClass("active")
.eq(index)
.addClass("active");
});
Upvotes: 0
Reputation: 43748
One possibility is to wrap all the buttons and info blocks into wrapper elements, and use those to add a single click handler, and then hide all the child info block elements and show just the 1 you want.
I also used a data-*
attribute here to specify what info block each button should show.
<div id="block-buttons">
<div class="block1" data-target="block1-info">Block 1</div>
<div class="block2" data-target="block2-info">Block 2</div>
<div class="block3" data-target="block3-info">Block 3</div>
<div class="block4" data-target="block4-info">Block 4</div>
</div>
<div class="info-blocks">
<div class="block1-info">Info about Block 1</div>
<div class="block2-info">Info about Block 2</div>
<div class="block3-info">Info about Block 3</div>
<div class="block4-info">Info about Block 4</div>
</div>
Javascript:
$("#block-buttons").on("click", "div", function (clickEvent) {
var target = $(clickEvent.target).data("target");
$(".info-blocks > div").fadeOut();
$("." + target).fadeIn();
});
This could be cleaned up a lot more if you didn't have CSS styling per-element. If all the buttons were the same color you could eliminate all the block1
though block4
classes completely.
Also if you don't need to support IE9 and older, you should use a CSS transition instead of a jQuery fadein/out, because the CSS transitions can be hardware rendered by the browser.
Upvotes: 2
Reputation: 34426
For things like this we tend to make heavy use of custom data attributes (http://html5doctor.com/html5-custom-data-attributes/) -
<div class="selector" data-block="d1">Block 1</div>
<div class="selector" data-block="d2">Block 2</div>
<div class="selector" data-block="d3">Block 3</div>
<div class="selector" data-block="d4">Block 4</div>
<div class="info active"></div>
<div class="info" data-info="d1">Info about Block 1</div>
<div class="info" data-info="d2">Info about Block 2</div>
<div class="info" data-info="d3">Info about Block 3</div>
<div class="info" data-info="d4">Info about Block 4</div>
Which we can then use to dramatically shorten the code -
$('.selector').click(function() {
var currentSelection = $(this).data('block');
$('.info').removeClass('active');
$('.info[data-info=' + currentSelection + ']').addClass('active');
});
I find this to be far less elegant than what Moby's Stunt Double does with his though. He doesn't have to have extra lines of markup for each block.
Upvotes: 1
Reputation: 1695
Hope that it will be helpful to you.. fiddle
html
<div class="block1 block" value="Info about Block 1">Block 1</div>
<div class="block2 block" value="Info about Block 2">Block 2</div>
<div class="block3 block" value="Info about Block 3">Block 3</div>
<div class="block4 block" value="Info about Block 4">Block 4</div>
<div class="info"></div>
js
$(".block").click(function() {
var value = $(this).attr('value');
$(".info").html(value);
});
Upvotes: -1
Reputation: 2550
I've boiled everything down, removing the animation, to show you the basics of how to structure it and created a new fiddle for you here: http://jsfiddle.net/7E3ch/
Markup:
<div id="blocks">
<div id="block1" class="block" data-info="Info about Block 1">Block 1</div>
<div id="block2" class="block" data-info="Info about Block 2">Block 2</div>
<div id="block3" class="block" data-info="Info about Block 3">Block 3</div>
<div id="block4" class="block" data-info="Info about Block 4">Block 4</div>
</div>
<div id="info"></div>
Javascript:
$(".block").on("click", function (e) {
$("#info span").hide();
$("#info").append("<span>" + $(this).data("info") + "</span>");
});
CSS:
#blocks{
float:left;
}
.block {
margin-left:20px;
margin-top:10px;
height:40px;
width:140px;
display:block;
}
#block1 {
background-color:red;
}
#block2 {
background-color:blue;
}
#block3 {
background-color:green;
}
#block4 {
background-color:orange;
}
#info {
margin-left:180px;
margin-top:10px;
height:190px;
width:140px;
display:block;
position:absolute;
background-color:grey;
}
The following things are the big items:
I hope this helps. Good luck with your app!
Upvotes: 2
Reputation: 171698
Here's a simple approach using common classes and indexing:
HTML:
<div class="tab block1">Block 1</div>
<div class="tab block2">Block 2</div>
<div class="content block1-info">Info about Block 1</div>
<div class="content block2-info">Info about Block 2</div>
JS:
var $tabs=$('.tab'), $content=$('.content').hide();
$tabs.click(function(){
var index=$tabs.removeClass('clicked').index(this);
$(this).addClass('clicked')
$content.filter(':visible').fadeOut()
$content.eq(index).fadeIn();
});
Upvotes: 0
Reputation:
Instead of having multiple divs within the gray div, and checking whether a certain one is clicked or not, and then removing the clicked class, you could just use jQuery's .html()
or .text()
to set up the corresponding text within your gray div. Read about them more here:
Upvotes: 0
Reputation: 10864
Only one block has to be hidden at most (the one that was active) and only one block has to be shown whenever a change occurs. Therefore you just have to store which block was active before and then fade this one out and remove the class of it before fading the new block in.
Upvotes: 0
Reputation: 114437
There are a few ways to accomplish this. If you use the following pattern you can use the ID of the clicked unit to set the state of the related DIV. By using two classes you can use one to clear all of the items, and one to set an individual item
<div id="block1" class="block">Block 1</div>
. . .
<div class="info"></div>
<div class="blockinfo block1">Info about Block 1</div>
... etc
JS:
$('.block').on('click', function() {
var myId = $(this).attr('id');
$('.block').removeClass('clicked');
$(this).addClass('clicked');
$('.blockinfo').fadeout();
$('.' + myId).fadeIn();
})
Upvotes: 5