I am trying to generate a random number for the css opacity.
This is what I tried so far.
CSS
.test{
position : absolute;
width : 15px;
height : 15px;
border-radius:15px;
background-color : black;
}
Script
$(function() {
for (var i = 0; i < 300; i++) {
$("<div>", {
class: "test",
css: {
opacity: randomOpacity
}
}).appendTo("body");
}
function randomOpacity() {
var opac = 0;
opac = Math.random() < 1;
console.log(opac);
}
randomize();
});
The Fiddle
There are multiple errors with your fiddle:
You are spawning 300 divs that are all absolutely positioned. They stack on top of each other and so would appear black regardless.
You aren't actually calling the function (missing parentheses)
Math.random() < 1 is going to return True instead of a number.
You aren't returning opac from your function.
You were calling randomize(), which isn't defined.
Corrected version: http://jsfiddle.net/RucKd/1/
Math.random() already generates a random number between 0 and 1, so:
$(function() {
for (var i = 0; i < 100; i++) {
$("<div>", {
class: "test"
}).css('opacity', Math.random()).appendTo("body");
}
});
Fiddle
edit: Re-inserted your loop in my answer and removed absolute pos from the fiddle. Read #ChristopheBiocca (+1)'s answer for a more complete code review.
JS
$(function() {
for (var i = 0; i < 300; i++){
$("<div>", {
class : "test",
css : {
opacity : randomOpacity
}
}).appendTo("body");
}
function randomOpacity(){
var opac = 0;
opac = (Math.random());
return opac;
}
});
CSS
remove position : absolute;, with this css all your divs at the same place
.test{
width : 15px;
height : 15px;
border-radius:15px;
background-color : black;
}
The css functions alters a css attribute, Math.random() returns 0-1 so you can just drop it in. The following code alters the opac div's opacity.
<div id="opac">
lalalalala
</div>
<script>
$(document).ready(function(){
$("#opac").css('opacity', Math.random());
});
</script>
$(document).ready calls everything inside it once the page is loaded, good idea to use for things like this.
$('#foobar').css({ opacity: Math.random() });
Math.random() always returns a value between 0 and 1 and you can put it directly in the function that creates divs. Also, the position: absolute in your CSS places every div in the same place, so you are not able to see the result correctly. Try this:
CSS
.test{
width : 15px;
height : 15px;
border-radius:15px;
background-color : black;
}
JS
$(function() {
for (var i = 0; i < 300; i++) {
$("<div>", {
class: "test",
css: {
opacity: Math.random()
}
}).appendTo("body");
}
});
Anyway, the randomize() function is not defined.
Related
I found this code here: https://stackoverflow.com/a/29017677 .
This is fadeOut function. It's working
var s = document.getElementById('thing').style;
s.opacity = 1;
(function fade(){(s.opacity-=.1)<0?s.display="none":setTimeout(fade,100)})();
var s = document.getElementById('thing').style;
s.opacity = 1;
(function fade(){(s.opacity-=.1)<0?s.display="none":setTimeout(fade,100)})();
#thing {
background: red;
line-height: 40px;
}
<div id="thing">I will fade...</div>
now I am trying to write a function for fadeIn like the fadeOut function. But this function doesn't work. I do not understand why.
var s = document.getElementById('thing').style;
s.opacity = 0;
(function fade(){(s.opacity+=.1)>0.95?s.display="block":setTimeout(fade,100)})();
var s = document.getElementById('thing').style;
s.opacity = 0;
(function fade(){(s.opacity+=.1)>0.95?s.display="block":setTimeout(fade,100)})();
#thing {
background: red;
line-height: 40px;
}
<div id="thing">I will fade...</div>
The best solution for your case would be to use more CSS instead of JS, you can add the property transition to the #thing selector and then all you need to do is set the opacity via JS code(no need to add timeouts or any other form of complicated handling)
function hide() {
document.getElementById('thing').style.opacity = 0
}
function show() {
document.getElementById('thing').style.opacity = 1
}
#thing {
transition: .4s
}
<div id="thing">I am showing up</div>
<button onclick="hide()">hide</button>
<button onclick="show()">show</button>
The += is trying to append, so keep with -= and set your .1 to negative value as -0.1 and it will works.
var s = document.getElementById('thing').style;
s.opacity = 0;
(function fade() {
(s.opacity -= -0.1) < 1 && setTimeout(fade,100);
}
)();
#thing{
background: red;
line-height: 40px;
opacity: 0;
}
<div id="thing">I will fade...</div>
Complementing my answer with Chris G's comment:
The opacity is a string, so appending 0.1 works the first time: "0" + "0.1" is "00.1" which checks out. Then it's "0.1" + 0.1 which is "0.10.1" and gets parsed back into "0.1" - Chris G
I have a series of images I want to transition from 0 opacity to 1 opacity when they come into the view port. I have the viewport check part done and the adding classes, however I would like them to be on an interval, so once the first 3 images come into the view port they appear 1, 2, 3 every .5seconds or so. Instead of all 3 at the same time.
here's a JS fiddle of how it works currently
reveal();
function reveal() {
var reveal = document.querySelectorAll(".reveal");
window.onscroll = function() {
for(var i = 0; i < reveal.length; i++) {
if(checkVisible(reveal[i]) === true) {
reveal[i].classList.add("fade");
}
}
}
};
function checkVisible(elm) {
var rect = elm.getBoundingClientRect();
var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);
return !(rect.bottom < 0 || rect.top - viewHeight >= -200);
}
https://jsfiddle.net/u04sy7jb/
I've modified your code to add a transition-delay of an additional .5 seconds for each element after the first one, in each "group" that is revealed as you scroll. I left comments in the JavaScript so you can understand the changes.
Let me know if you have any questions!
Live demo:
reveal();
function reveal() {
var reveal = document.querySelectorAll(".reveal");
window.onscroll = function() {
// start a new count each time user scrolls
count = 0;
for (var i = 0; i < reveal.length; i++) {
// also check here if the element has already been faded in
if (checkVisible(reveal[i]) && !reveal[i].classList.contains("fade")) {
// add .5 seconds to the transition for each
// additional element currently being revealed
reveal[i].style.transitionDelay = count * 500 + "ms";
reveal[i].classList.add("fade");
// increment count
count++;
}
}
}
};
function checkVisible(elm) {
var rect = elm.getBoundingClientRect();
var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight);
return !(rect.bottom < 0 || rect.top - viewHeight >= -200);
}
.container {
width: 100%;
height: 1200px;
background-color: orange;
}
.reveal {
display: inline-block;
width: 32%;
margin: 0 auto;
height: 400px;
background-color: pink;
border: 1px solid black;
opacity: 0;
}
.fade {
opacity: 1;
transition: 1s;
}
<div class="container">
<div class="reveal"></div>
<div class="reveal"></div>
<div class="reveal"></div>
<div class="reveal"></div>
<div class="reveal"></div>
<div class="reveal"></div>
<div class="reveal"></div>
<div class="reveal"></div>
<div class="reveal"></div>
</div>
You could be able to stick your reveal[i].classList.add("fade"); inside of a setTimeout that executes as a function of your ith element so they show up how you're describing. Here is an example of adding short function to add the class and using it in a setTimeout to make this happen, although you could change it up to meet any additional needs.
function reveal() {
var reveal = document.querySelectorAll(".reveal");
window.onscroll = function() {
for(var i = 0; i < reveal.length; i++) {
if(checkVisible(reveal[i]) === true) {
addMyFadeClass(reveal[i], i)
}
}
}
};
function addMyFadeClass(element, i) {
setTimeout(function() {
element.classList.add("fade");
}, i * 500)
}
You can also use :nth-child CSS selectors without the need to change the JS:
.reveal:nth-child(3n+1).fade {
opacity: 1;
transition: 1s;
}
.reveal:nth-child(3n+2).fade {
opacity: 1;
transition: 1.5s;
}
.reveal:nth-child(3n).fade {
opacity: 1;
transition: 2s;
}
JSFiddle: https://jsfiddle.net/u04sy7jb/8/
Is it possible for li elements animation from here:
http://jsfiddle.net/8XM3q/light/
to animate when there is show/hide function used instead of remove?
When i have changed "remove" to "hide" elements didn't move: http://jsfiddle.net/8XM3q/90/
I wanted to use this function for my content filtering animations - thats why i have to replace "remove" to "hide/show".
I'm not good at JS but i think that it counts all elements, even when they are hidden:
function createListStyles(rulePattern, rows, cols) {
var rules = [], index = 0;
for (var rowIndex = 0; rowIndex < rows; rowIndex++) {
for (var colIndex = 0; colIndex < cols; colIndex++) {
var x = (colIndex * 100) + "%",
y = (rowIndex * 100) + "%",
transforms = "{ -webkit-transform: translate3d(" + x + ", " + y + ", 0); transform: translate3d(" + x + ", " + y + ", 0); }";
rules.push(rulePattern.replace("{0}", ++index) + transforms);
}
}
var headElem = document.getElementsByTagName("head")[0],
styleElem = $("<style>").attr("type", "text/css").appendTo(headElem)[0];
if (styleElem.styleSheet) {
styleElem.styleSheet.cssText = rules.join("\n");
} else {
styleElem.textContent = rules.join("\n");
}
So my question is how to adapt that part of code to count only "show" (displayed) elements?
If you want to have the animation and still have all of the data then use detach() function instead of remove: jQuery - detach
And to count or select elements try to do this using css's class attached to each element.
I edited your jsFiddle:
http://jsfiddle.net/8XM3q/101/
notice that I changed this line:EDIT: http://jsfiddle.net/8XM3q/101/
$(this).closest("li").remove();
to this:
$(this).closest("li").hide("slow",function(){$(this).detach()});
This means hide the item, speed = slow, when done hiding remove it.
Hope this is what you meant.
EDIT: Included detach.
As per your comment:
I wanted to use this function for my content filtering animations -
thats why i have to replace "remove" to "hide/show" I don't want to
remove elements at all. Im sorry if I mislead You with my question.
What you can do is to use a cache to store the list-items as they are hidden when you do the content filtering. Later when you need to reset the entire list, you can replenish the items from the cache.
Relevant code fragment...
HTML:
...
<button class="append">Add new item</button>
<button class="replenish">Replenish from cache</button>
<div id="cache"></div>
JS:
...
$(this).closest("li").hide(600, function() {
$(this).appendTo($('#cache'));
});
...
$(".replenish").click(function () {
$("#cache").children().eq(0).appendTo($(".items")).show();
});
Demo Fiddle: http://jsfiddle.net/abhitalks/8XM3q/102/
Snippet:
$(function() {
$(document.body).on("click", ".delete", function (evt) {
evt.preventDefault();
$(this).closest("li").hide(600, function() {
$(this).appendTo($('#cache'));
});
});
$(".append").click(function () {
$("<li>New item <a href='#' class='delete'>delete</a></li>").insertAfter($(".items").children()[2]);
});
$(".replenish").click(function () {
$("#cache").children().eq(0).appendTo($(".items")).show();
});
// Workaround for Webkit bug: force scroll height to be recomputed after the transition ends, not only when it starts
$(".items").on("webkitTransitionEnd", function () {
$(this).hide().offset();
$(this).show();
});
});
function createListStyles(rulePattern, rows, cols) {
var rules = [], index = 0;
for (var rowIndex = 0; rowIndex < rows; rowIndex++) {
for (var colIndex = 0; colIndex < cols; colIndex++) {
var x = (colIndex * 100) + "%",
y = (rowIndex * 100) + "%",
transforms = "{ -webkit-transform: translate3d(" + x + ", " + y + ", 0); transform: translate3d(" + x + ", " + y + ", 0); }";
rules.push(rulePattern.replace("{0}", ++index) + transforms);
}
}
var headElem = document.getElementsByTagName("head")[0],
styleElem = $("<style>").attr("type", "text/css").appendTo(headElem)[0];
if (styleElem.styleSheet) {
styleElem.styleSheet.cssText = rules.join("\n");
} else {
styleElem.textContent = rules.join("\n");
}
}
createListStyles(".items li:nth-child({0})", 50, 3);
body { font-family: Arial; }
.items {
list-style-type: none; padding: 0; position: relative;
border: 1px solid black; height: 220px; overflow-y: auto; overflow-x: hidden;
width: 600px;
}
.items li {
height: 50px; width: 200px; line-height: 50px; padding-left: 20px;
border: 1px solid silver; background: #eee; box-sizing: border-box; -moz-box-sizing: border-box;
position: absolute; top: 0; left: 0;
-webkit-transition: all 0.2s ease-out; transition: all 0.2s ease-out;
}
div.cache { display: none; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul class="items">
<li>Monday delete
</li><li>Tuesday delete
</li><li>Wednesday delete
</li><li>Thursday delete
</li><li>Friday delete
</li><li>Saturday delete
</li><li>Sunday delete</li>
</ul>
<button class="append">Add new item</button>
<button class="replenish">Replenish from cache</button>
<div id="cache"></div>
EDIT: There is a simpler way without adding any classes, is to use the :visible selector
You need to understand a concept is Javascript, which is that functions are considered objects. You can pass a function to another function, or return a function from a function.
Let's check the documentation on jQuery for the hide function
.hide( duration [, easing ] [, complete ] )
It says that it accepts a function as an argument for complete, which is called when the hide animation is complete.
The function hide does not remove the element from the DOM but simply "hides" it as the name suggests. So what we want to do, is hide the element then when the animation of hiding is done, we add a class "removed" to the list element.
We will accomplish that by passing a function (complete argument) like so :
$(this).closest("li").hide(400, function() {
$(this).addClass('removed');
});
When you want to select the list elements that are not "removed", use this selector $('li:not(.removed)')
With my code, i am using a script that generates a random set of 4 images that gets displayed on refresh. I wish to only display the text when the cursor is hovering over the respective image.
Any pointers on doing this with CSS? I am not sure where to put the hover element. http://jsfiddle.net/sugarcraving/Af72K/1/
Javascript
$(document).ready(function() {
$(".champ").hide();
var elements = $(".champ");
var elementCount = elements.size();
var elementsToShow = 4;
var alreadyChoosen = ",";
var i = 0;
while (i < elementsToShow) {
var rand = Math.floor(Math.random() * elementCount);
if (alreadyChoosen.indexOf("," + rand + ",") < 0) {
alreadyChoosen += rand + ",";
elements.eq(rand).show();
++i;
}
}
});
CSS
div.champ {
display: none;
float: left;
color: red;
}
Following what Rouse02 said, add something around the context you want to hide/show.
<p>Celebi, the 251</p>
Then in your css, hide the content and only show it when you hover on the div (you could do it on the image hover as well).
p {
visibility:hidden;
}
.champ:hover p {
visibility:visible;
}
Demo: http://jsfiddle.net/bzTnR/1/
div.champ: hover{
visibility: hidden;
}
div.champ: hover p{
visibility: visible
}
Where p is the element within your div that holds the text.
Hope this helps.
V/R
Maybe this could help! Let me know if this wasn't you were looking for
JsFiddle
I changed CSS to like this : div.text{display:none} div.champ:hover div.text{display:block}
i've updated your fiddle..here..try this
div.champ p{
visibility: hidden;
}
div.champ:hover p{
visibility: visible;
}
updated fiddle
Please take a look at this fiddle: http://jsfiddle.net/dhcyA/
Try clicking on a block. What I want is that when the other elements disapear, the selected block will animate/ease to his giving position instead of just jumping like it does now. Then the same animation repeats itself when clicking again on the box, but then back to place.
Maybe to keep in mind:
I'm using a reponsive design, which means those blocks can be vertical and horizontal after scaling the window.
Any redevisions on the fiddle or suggustions would be great!
Here is my solution.
On your existing markup, I added a wrapper division to calculate the position of boxes inside the wrapper. Like this
<div id="wrapper">
<div class="block">
<h2>I'm block 1</h2>
</div>
....
</div>
To maintain the fluidness of the block, I created a function to position the block on the wrapper. Here is the function for position of the blocks:
var reposition = function() {
wrapper = $("#wrapper");
console.log(wrapper.innerWidth());
pLeft = 0;
pTop = 0;
maxRowHeight = 0;
$(".block").each(function(){
if($(this).data('active')) {
$(this).data('top', pTop);
$(this).data('left', pLeft);
} else {
$(this).stop(0,0).animate({
'top' : pTop + 'px',
'left' : pLeft + 'px'
});
}
pLeft += $(this).outerWidth() + parseInt($(this).css('marginLeft'));
if($(this).height() > maxRowHeight) maxRowHeight = $(this).outerHeight() + parseInt($(this).css('marginTop')); //Find out the longest block on the row
if(pLeft + $(this).next().outerWidth() + parseInt($(this).next().css('marginLeft')) >= wrapper.innerWidth()) {
pLeft = 0;
pTop += maxRowHeight;
maxRowHeight = 0;
}
});
};
Finally, the script to toggle the block
$(".block").click(function() {
$(this).siblings().slideToggle('slow'); //Toggle other blocks
if(!$(this).data('active')){ //if the block is not active
$(this).data('left', $(this).position().left); //sets its left
$(this).data('top', $(this).position().top); // and top position
$(this).animate({ //animate at the top and bottom
top:0,
left:0
},'slow');
$(this).data('active',true);
}else{
$(this).animate({ //animate to its last known position
top:$(this).data('top'),
left:$(this).data('left')
},'slow');
$(this).data('active',false);
}
});
Demos
Demo[Full] (Resize this to see the fluidness maintained)
Demo[Full] (version showing variable heights)
Here is what this solutions gives:
Remembers the last position and gradually animate to/from this position
Block positions are calculated and animated on load and every resize
Repositioning happens on $(window).resize() thus maintaining the fluid nature of the block, despite the use of position absolute
Support variable heights
Minor change on existing markup & CSS
Also fixed two issues extended by Gaby
Accounts for each block margin independently
Recalculates the position of the element after resize
Final Update
Here is a full working solution (pretty straight forward in my opinion) with JS to set the positioning (a simple calculation) and CSS transitions for the rest..
Demo at http://jsfiddle.net/gaby/pYdKB/3/
It maintains the fluidity of float:left and works with any number of elements, and you can keep the :nth-child for the styling, and it will also work if you want to leave more than one element visible..
javascript
var wrapper = $('.wrapper'),
boxes = wrapper.children(),
boxWidth = boxes.first().outerWidth(true),
boxHeight = boxes.first().outerHeight(true);
function rePosition(){
var w = wrapper.width(),
breakat = Math.floor( w / boxWidth ); // calculate fluid layout, just like float:left
boxes
.filter(':not(.go)')
.each(function(i){
var matrixX = ((i)%breakat)+1,
matrixY = Math.ceil((i+1)/breakat);
$(this).css({
left:(matrixX-1) * boxWidth ,
top: (matrixY-1) * boxHeight
});
});
}
$('.box').click(function(){
$(this)
.siblings()
.toggleClass('go');// just add the go class, and let CSS handle the rest
rePosition(); // recalculate final positions and let CSS animate the boxes
});
$(window).resize(rePosition);
$(window).trigger('resize');
CSS
.wrapper{
position:relative;
}
.box{
width:200px;
height:100px;
position:absolute;
margin:5px;
cursor:pointer;
overflow:hidden;
text-align: center;
line-height: 100px;
-moz-transition-property: top,left,width,height;
-webkit-transition-property: top,left,width,height;
-ms-transition-property: top,left,width,height;
-o-transition-property: top,left,width,height;
transition-property: top,left,width,height;
-moz-transition-duration: 1s;
-webkit-transition-duration: 1s;
-ms-transition-duration: 1s;
-o-transition-duration: 1s;
transition-duration: 1s;
}
.go{
height:0;
width:0;
}
note: As #Athari correctly mentioned in the comments, you should include all browser prefixes for the widest support. (my initial answer only included moz / webkit and the standard)
Original Answer
You can not do it directly with your current HTML structure. The floated concept does not support it.
But if you can afford an extra wrapper, then it is no problem..
Just slide the contents of your extra wrapper element..
Put the float code on the wrapper element and use
$(document).ready(function() {
$(".block-wrapper").click(function() {
$(this).siblings().find('.block').slideToggle("slow");
});
});
Demo at http://jsfiddle.net/gaby/t8GNP/
Update #1
If you need to move the clicked element to the top left and back, then you cannot really do it with CSS.
You will need to manually position them (through JS), set CSS transitions (or jquery), and apply the new positions once you click.
Later on you might want more than one to remain visible and reposition as well..
So you might want to take a look at the great Isotope plugin which can handle this and a multitude of more situations/layouts
Here is my version:
http://jsfiddle.net/selbh/dhcyA/92/
(only javascript is changed, and it's responsive)
$(document).ready(function() {
$(".block").click(function() {
var $this = $(this);
var pos = $this.offset();
var $siblings = $(this).siblings().add(this);
var marginTop = $this.css('marginTop').replace(/[^-\d\.]/g, '');
var marginLeft = $this.css('marginLeft').replace(/[^-\d\.]/g, '');
var $clone = $this.clone();
$siblings.slideToggle("slow");
$clone.css({
position: 'absolute',
left: pos.left - marginLeft,
top: pos.top - marginTop,
'background-color': $this.css('background-color')
});
$('body').append($clone);
$this.css('opacity', 0);
$clone.animate({
'left': 0,
'top': 0
});
$clone.click(function() {
$siblings.slideToggle("slow", function() {
$clone.remove();
$this.css('opacity', 1);
});
$clone.animate({
left: pos.left - marginLeft,
top: pos.top - marginTop
});
});
});
});
I'm kind of sleepy(It's 2:30 AM here) so I leave the half done answer here to give you an idea (I did it in 30 minutes so I guess with 30 minutes more you can get something really nice)
http://jsfiddle.net/LuL2s/2/
The trick comes by the block-holder which make the ease animation and making a difference between when they appear and disappear
JS
$(document).ready(function() {
var open = true;
$(".block").click(function() {
var $this = $(this);
var count = 0;
if (open) {
$this.parent().siblings().children().slideToggle("slow", function(){
if (count++ == 2) {
$this.parent().siblings().animate({width: 'toggle', height:'toggle'});
}
});
} else {
$this.parent().siblings().animate({width: 'toggle', height:'toggle'}, function(){
if (count++ == 2) {
$this.parent().siblings().children().slideToggle("slow");
}
});
}
open = !open;
});
});
HTML
<div class="block-holder">
<div class="block">
<h2>I'm block 1</h2>
</div>
</div>
<div class="block-holder">
<div class="block">
<h2>I'm block 2</h2>
</div>
</div>
<div class="block-holder">
<div class="block">
<h2>I'm block 3</h2>
</div>
</div>
<div class="block-holder">
<div class="block">
<h2>I'm block 4</h2>
</div>
</div>
CSS
.block {
width: 100%;
height: 100%;
text-align: center;
line-height: 100px;
cursor: pointer;
}
.block-holder:nth-child(1) .block {
background: green;
}
.block-holder:nth-child(2) .block {
background: red;
}
.block-holder:nth-child(3) .block {
background: orange;
}
.block-holder:nth-child(4) .block {
background: pink;
}
.block-holder {
width: 200px;
height: 100px;
float: left;
margin: 20px;
}
Great Challenge!
New Version:
Here is a much better version as it makes the blocks stay in their rows. I added a css function so that your nth-child styles could be applied even in the rows. Even maintains same HTML Structure.
Demo: http://jsfiddle.net/MadLittleMods/fDDZB/23/
The jQuery for this new revision looks like:
$('.block').on('click', function() {
var block = $(this);
// Keep the blocks in line
makeRows($('body'));
$('.block').not(this).each(function() {
// If sibling on the same level, horizontal toggle
// We also want ignore the toggleMethod if it is shown because we might need to reassign
if (($(this).position().top == block.position().top && (($(this).data('toggle') == -1) || $(this).data('toggle') == null)) || ($(this).data('toggle') != -1 && $(this).data('toggleMethod') == 'side'))
{
$(this).data('toggleMethod', 'side');
// Hide block
if ($(this).data('toggle') == -1 || $(this).data('toggle') == null)
{
// Set properties for later use in show block
$(this).data('overflowBefore', $(this).css('overflow'));
$(this).css('overflow', 'hidden');
$(this).data('marginBefore', $(this).css('margin'));
var width = $(this).width();
$(this).animate({
width: 0,
margin: 0
}, function() {
$(this).data('toggle', width);
});
}
// Show block
else
{
$(this).css('overflow', $(this).data('overflowBefore'));
$(this).animate({
width: $(this).data('toggle'),
margin: $(this).data('marginBefore')
}, function() {
$(this).data('toggle', -1);
});
}
}
// Do a normal vertical toggle
else
{
$(this).data('toggleMethod', 'top');
$(this).slideToggle('slow');
}
});
});
// Make rows to make the blocks in line
function makeRows(container)
{
// Make rows so that the elements stay where they should
var containerWidth = container.width();
var currentRowWidth = 0;
// Add styles first so nothing gets messed up
container.children().each(function() {
var itemCSS = css($(this));
$(this).css(itemCSS);
});
// Now assemble the rows
container.children().each(function() {
var blockWidth = $(this).outerWidth() + parseInt($(this).css('margin-left')) + parseInt($(this).css('margin-right'));
if((currentRowWidth + blockWidth) < containerWidth)
{
currentRowWidth += blockWidth;
}
else
{
Array.prototype.reverse.call($(this).prevUntil('.row')).wrapAll('<div class="row"></div>');
$(this).prev().append('<div class="row_clear" style="clear: both;"></div>');
currentRowWidth = 0;
}
});
}
// Remove the rows added
function deleteRows()
{
var content = $('.row').contents()
$('.row').replaceWith(content);
$('.row_clear').remove();
}
$(window).resize(function() {
deleteRows();
});
// Functions courtesy of marknadal
// https://stackoverflow.com/a/5830517/796832
function css(a)
{
var sheets = document.styleSheets, o = {};
for(var i in sheets) {
var rules = sheets[i].rules || sheets[i].cssRules;
for(var r in rules) {
if(a.is(rules[r].selectorText)) {
o = $.extend(o, css2json(rules[r].style), css2json(a.attr('style')));
}
}
}
return o;
}
function css2json(css)
{
var s = {};
if(!css) return s;
if(css instanceof CSSStyleDeclaration) {
for(var i in css) {
if((css[i]).toLowerCase) {
s[(css[i]).toLowerCase()] = (css[css[i]]);
}
}
} else if(typeof css == "string") {
css = css.split("; ");
for (var i in css) {
var l = css[i].split(": ");
s[l[0].toLowerCase()] = (l[1]);
};
}
return s;
}
I added a makeRows and deleteRows functions so that the blocks would stay in their rows instead of getting smaller and moving into the row above. I call deleteRows whenever the window resizes so that it can maintain a responsive layout. Then if the blocks need to be toggled, I recreate the rows.
css and css2json functions are courtesy of marknadal
Old version:
I came up with a solution with .animate so that it could ease horizontally.
Here is a demo: http://jsfiddle.net/MadLittleMods/fDDZB/8/
The jQuery looks like:
$('.block').on('click', function() {
var block = $(this);
$(this).siblings().each(function() {
// If sibling on the same level, horizontal toggle
// We also want ignore the toggleMethod if it is shown because we might need to reassign
if (($(this).position().top == block.position().top && ($(this).data('toggle') == -1) || $(this).data('toggle') == null) || ($(this).data('toggle') != -1 && $(this).data('toggleMethod') == 'side'))
{
$(this).data('toggleMethod', 'side');
// Hide block
if ($(this).data('toggle') == -1 || $(this).data('toggle') == null)
{
// Set properties for later use in show block
$(this).data('overflowBefore', $(this).css('overflow'));
$(this).css('overflow', 'hidden');
$(this).data('marginBefore', $(this).css('margin'));
var width = $(this).width();
$(this).animate({
width: 0,
margin: 0
}, function() {
$(this).data('toggle', width);
});
}
// Show block
else
{
$(this).css('overflow', $(this).data('overflowBefore'));
$(this).animate({
width: $(this).data('toggle'),
margin: $(this).data('marginBefore')
}, function() {
$(this).data('toggle', -1);
});
}
}
// Do a normal vertical toggle
else
{
$(this).data('toggleMethod', 'top');
$(this).slideToggle('slow');
}
});
});
The key was to separate the blocks that were toggled with .slideToggle and .animate because you have to apply the same when they show and hide.