I have a string like
content = "content1<br/>content2<br/>"
and I add content3 into string the result gonna be like
content1
content2
content3
I want the container of content animate height while showing new content.
I have tried
document.getElementById("detail").innerHTML = content;
$("detail").animate({
height: 'auto'
}, 1000);
it's just appear without animate
It's a tricky to animate to unknown height. I would try to make use of the combination of CSS transition and javascript helper function to calculate new width.
Of course you can still go with just animate but you will still need to calculate new height and animate to new height.
var content = "content1<br/>content2<br/>";
var height = getHeight(content);
$("#detail").css('max-height', height).html(content);
setTimeout(function() {
content += 'content3<br/>';
var height = getHeight(content);
$("#detail").css('max-height', height).html(content);
}, 1000);
function getHeight(str) {
var $tmp = $("<div>").html(content).css({position: 'absolute', top: -1000}).appendTo('body'),
height = $tmp.height() + 10 + 'px';
$tmp.remove();
return height;
}
#detail {
background: #EEE;
overflow: hidden;
max-height: 0;
transition: max-height 1s ease;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="detail"></div>
That's because jQuery .Animate() only works with numbers.
for example your current height is: 20px and you make it 30px in animate function.
jQuery will rise 20 to 30 in the declared time and set it to the element continuously until it gets to 30.
on the other hand CSS acts more quickly than JS and makes the element auto-heighted automatically.
So you have to use numbers to animate the new height (and the previous height has to be a number too) OR you can use CSS3 transitions.
FIDDLE here
Related
I have the following CSS snippet:
.Test-Content {
transition: height 2s;
}
and the following HTML code:
<div id="mydiv" class="Test-Content">foo</div>
Using pure JavaScript, I am trying to change the height of the div like that:
myId = document.getElementById("mydiv");
myId.style.cssText = "height: 0;";
/* ... Lots of code and situations where the browser re-renders the page ... */
myId.style.cssText = "height: 100px";
This indeed changes the div's height from whatever it was to 0 and then to 100px, but without any animation, i.e. immediately. I don't understand why this happens. After all, the div has .Test-Content in its class list, so any change of its height should trigger the transition.
Could anybody please explain why this is not the case?
When I change this to the following (very weird and worrying) code, it works as expected:
CSS:
.Test-Content-A {
transition: height 2s;
height: 0;
}
.Test-Content-B {
transition: height 2s;
height: 10px; /* or whatever number you prefer */
}
HTML:
<div id="mydiv" class="Test-Content-A Test-Content-B">foo</div>
JavaScript: (Same as above)
It seems that I can trigger a transition by setting an element's style.cssText directly only if this element also has two classes with different height properties in its class list.
I have that problem in Firefox and Chrome (didn't test others so far), both at current patch level at the time of writing this.
There are a couple things you can be running into as illustrated by the following snippet. The first time you click "Expand" or "Collapse", it won't animate, but then clicking "Expand" or "Collapse" after that will trigger an animation.
document.getElementById("expand").addEventListener('click', () => {
myId = document.getElementById("mydiv");
myId.style.cssText = "height: 100px";
});
document.getElementById("collapse").addEventListener('click', () => {
myId = document.getElementById("mydiv");
myId.style.cssText = "height: 0;";
});
document.getElementById("unset").addEventListener('click', () => {
myId = document.getElementById("mydiv");
myId.style.cssText = "";
});
document.getElementById("setandexpand").addEventListener('click', () => {
myId = document.getElementById("mydiv");
myId.style.cssText = "height: 0;";
setTimeout(() => { myId.style.cssText = "height: 100px"; });
});
.Test-Content {
transition: height 2s;
}
<div id="mydiv" class="Test-Content">foo</div>
<button id="expand">Expand</button>
<button id="collapse">Collapse</button>
<button id="unset">Unset</button>
<button id="setandexpand">Set and expand</button>
Issues you may be experiencing:
1. CSS can only transition from a value to a value.
Going from cssText = "" (the initial value) to cssText = "height: 100px" doesn't animate. You can reproduce this by clicking "Unset" then clicking "Expand" or "Collapse".
2. If CSS is set multiple times in a block of code, the browser will only process the last one.
The browser doesn't render changes immediately when they're set but instead switches between executing all executable JavaScript and updating the page based on what everything is set to. You can get around this by breaking it into two discrete steps. The best will probably be setting style="height: 0" in the HTML or adding a class with zero height.
Otherwise, you can do something like:
myId.style.cssText = "height: 0";
setTimeout(() => { myId.style.cssText = "height: 100px"; });
This code sets the height to zero, lets the browser update the style, then executes the new code setting the height to 100px, which the browser can animate. Of course, you only need to call cssText = "height: 0" because as soon as it's been rendered the browser will be able to animate.
You can see this in the snippet by clicking "Unset" followed by "Set and expand". The element will immediately decrease to zero and then expand to 100px. Clicking multiple times shouldn't appear to do anything because each time the browser will start animating down to zero then start animating back up to 100px within milliseconds.
One of the easiest ways to trigger CSS animations with JS is changing the class.
CSS:
.Test-Content {
height: 0;
transition: height 2s;
}
.Test-Content.taller {
height: 10px;
}
And then, with Javascript:
1) For adding the class (animating forwards)
document.getElementById("mydiv").classList.add('taller');
2) For removing the class (animating backwards)
document.getElementById("mydiv").classList.remove('taller');
This may not answer your question about why the transition isn't working with style.cssText. However, if you're wanting the div to change height right when the page loads, I recommend using an animation instead like the following:
.Test-Content {
transition: height 2s;
height: 100px;
animation: grow 2s;
}
#keyframes grow {
from {
height: 0
}
to {
height: 100px;
}
}
This eliminates the need to use JavaScript and better helps to keep JavaScript from doing what CSS can do.
The closest solution I found is Show div on scrollDown after 800px.
I'm learning HTML, CSS, and JS, and I decided to try to make a digital flipbook: a simple animation that would play (ie, load frame after frame) on the user's scroll.
I figured I would add all the images to the HTML and then use CSS to "stack them" in the same position, then use JS or jQuery to fade one into the next at different points in the scroll (ie, increasing pixel distances from the top of the page).
Unfortunately, I can't produce the behavior I'm looking for.
HTML (just all the frames of the animation):
<img class="frame" id="frame0" src="images/hand.jpg">
<img class="frame" id="frame1" src="images/frame_0_delay-0.13s.gif">
CSS:
body {
height: 10000px;
}
.frame {
display: block;
position: fixed;
top: 0px;
z-index: 1;
transition: all 1s;
}
#hand0 {
padding: 55px 155px 55px 155px;
background-color: white;
}
.frameHide {
opacity: 0;
left: -100%;
}
.frameShow {
opacity: 1;
left: 0;
}
JS:
frame0 = document.getElementById("frame0");
var myScrollFunc = function() {
var y = window.scrollY;
if (y >= 800) {
frame0.className = "frameShow"
} else {
frame0.className = "frameHide"
}
};
window.addEventListener("scroll", myScrollFunc);
};
One of your bigger problems is that setting frame0.className = "frameShow" removes your initial class frame, which will remove a bunch of properties. To fix this, at least in a simple way, we can do frame0.className = "frame frameShow", etc. Another issue is that frame0 is rendered behind frame1, which could be fixed a variety of ways. ie. Putting frame0's <img> after frame1, or setting frame0's CSS to have a z-index:2;, and then setting frame0's class to class="frame frameHide" so it doesn't show up to begin with. I also removed the margin and padding from the body using CSS, as it disturbs the location of the images. I have made your code work the way I understand you wanted it to, here is a JSFiddle.
It depends on your case, for example, in this jsFiddle 1 I'm showing the next (or previous) frame depending on the value of the vertical scroll full window.
So for my case the code is:
var jQ = $.noConflict(),
frames = jQ('.frame'),
win = jQ(window),
// this is very important to be calculated correctly in order to get it work right
// the idea here is to calculate the available amount of scrolling space until the
// scrollbar hits the bottom of the window, and then divide it by number of frames
steps = Math.floor((jQ(document).height() - win.height()) / frames.length),
// start the index by 1 since the first frame is already shown
index = 1;
win.on('scroll', function() {
// on scroll, if the scroll value equal or more than a certain number, fade the
// corresponding frame in, then increase index by one.
if (win.scrollTop() >= index * steps) {
jQ(frames[index]).animate({'opacity': 1}, 50);
index++;
} else {
// else if it's less, hide the relative frame then decrease the index by one
// thus it will work whether the user scrolls up or down
jQ(frames[index]).animate({'opacity': 0}, 50);
index--;
}
});
Update:
Considering another scenario, where we have the frames inside a scroll-able div, then we wrap the .frame images within another div .inner.
jsFiddle 2
var jQ = $.noConflict(),
cont = jQ('#frames-container'),
inner = jQ('#inner-div'),
frames = jQ('.frame'),
frameHeight = jQ('#frame1').height(),
frameWidth = jQ('#frame1').width() + 20, // we add 20px because of the horizontal scroll
index = 0;
// set the height of the outer container div to be same as 1 frame height
// and the inner div height to be the sum of all frames height, also we
// add some pixels just for safety, 20px here
cont.css({'height': frameHeight, 'width': frameWidth});
inner.css({'height': frameHeight * frames.length + 20});
cont.on('scroll', function() {
var space = index * frameHeight;
if (cont.scrollTop() >= space) {
jQ(frames[index]).animate({'opacity': 1}, 0);
index++;
} else {
jQ(frames[index]).animate({'opacity': 0}, 0);
index--;
}
});
** Please Note that in both cases all frames must have same height.
I've been trying to solve this problem for a week now and it seems basic, so maybe I'm missing something.
I want to have a div centered on the screen (or its container), and then insert a second div to the right of it, so that afterwards the two of them are centered (space on each side is equal).
Inserting the second div is not a problem, but I need the first block to slide over to where its going to be after the new block is inserted.
http://jsfiddle.net/rdbLbnw1/
.container {
width:100%;
text-align:center;
}
.inside {
border:solid 1px black;
width:100px;
height:100px;
display:inline-block;
}
$(document).ready(function() {
$("#add").click(function() {
$(".container").append("<div class='inside'></div>");
});
});
<div class="container">
<div class="inside"></div>
</div>
<input id="add" type="button" value="add"/>
Do I need to explicitly calculate where the original box is going to end up and then animate that, or is there a better way to do it?
I like your question so decide to write this:
$(document).ready(function() {
var isInAction = false;
var intNumOfBoxes = 1;
var intMargin = 10;
$containerWidth = $(".container").width();
$insideWidth = $(".inside").width();
$(".inside").css('margin-left',($containerWidth - $insideWidth - intMargin)/2 + 'px');
$("#add").click(function() {
if (!isInAction){
isInAction = true;
intNumOfBoxes +=1;
$(".current").removeClass("current");
$(".container").append("<div class='inside current'></div>");
$('.inside').animate({
'margin-left': '-=' + ($insideWidth + intMargin)/2 + 'px'
}, 300, function () {
$(".current").css('margin-left',($containerWidth + ((intNumOfBoxes - 2) * ($insideWidth + intMargin)))/2 + 'px');
$(".current").fadeIn(500,function(){
isInAction = false;
});
});
}
});
});
Also add this class in CSS:
.current {
display:none;
}
You don't need to change variables in JS code except intMargin. you can change this var to set margin between boxes.
Note: This code works fine on older browsers too and not need to support CSS3 features like transition.
Update: Some bugs like repeated clicks fixed.
Check JSFiddle Demo
First, we can animate only things that have explicit numerical values such as width, left or margin. We can't animate things like alignment (which actually use the same margin property but implicitly, never mind). So if we know width of inserted div let's just add it to our container.
1) Let's centre container itself and add transition to it
.container {
width: 102px; /* set explicit width; 102 - because of borders */
margin: auto; /* set margin to 'auto' - that will centre the div */
transition: width 0.5s;
}
2) Then increase the width when add div
$(".container").width($(".container").width() + 102);
3) But wait! If we add div to too narrow container it will be added to bottom not to right. So we need another container set to appropriate width before.
See final example on JSFiddle.
BTW, remove all line breaks and tabs from your code when you use inline-block, because it will cause spaces between your blocks.
I am trying get this background to fade in on click. I found one tutorial that was helpful, and I ended up created the code so it has two images, and they fade in and out on click to bring up the picture.
Here's the work: http://www.mccraymusic.com/bgchangetest.html
Only a couple of issues though:
How do I make this work without the images getting selected at random? I'd like it to just switch from the plain black image to the image with the drum set. (And cross-fade to if possible, but not necessary)
How do I center the image on the page, so the image of the drums are centered?
I'm guessing this is what you're after:
$(function() {
var images = ["black.jpg","bg.jpg"];
$('<img>').attr({'src':'http://www.mccraymusic.com/assets/images/'+images[0],'id':'bg','alt':''}).appendTo('#bg-wrapper').parent().fadeIn(0);
$('.entersite').click(function(e) {
e.preventDefault();
var image = images[1];
$('#bg').parent().fadeOut(200, function() {
$('#bg').attr('src', 'http://www.mccraymusic.com/assets/images/'+image);
$(this).fadeIn(1000);
});
$(this).fadeOut(1000, function() {
$(this).remove();
});
});
});
DEMONSTRATION
Also added :
display: block;
margin-left: auto;
margin-right: auto;
to your #bg element to center the image.
Alright, assuming you use JQuery
You have #backgroundid and #imageid
Begin by setting
$('#backgroundid').css('opacity',1);
$('#imageid').css('opacity',0); // setting opacity (transparency) to 0, invisible
Now you have #buttonid.
Set up a jquery event so that when it's clicked, you fade out the background, and fade in the image using JQuery's animate.
$('#buttonid').click(function() {
$('#backgroundid').animate(function() {
opacity : 0 // fade it to 0 opacity, invisible
}, 1000); // animation will take 1000ms, 1second
$('#imageid').animate(function() {
opacity : 1 // fade it to full opacity, solid
}, 1000);
});
Now about that image centering.
You can either let css manage it with
body { /* Body or #imageid parent */
text-align : center;
}
#imageid {
margin: 0px auto;
}
Or you can stick to a JQuery solution, using absolute/fixed positioning.
First, use some css to fix the position of your image
#imageid {
position: absolute; // or fixed, if you want
}
Now use JQuery to reposition it
function positionImage() {
var imagewidth = $('#imageid').width();
var imageheight = $('#imageid').height();
$('#imageid').css('left', ($(window).width() - imagewidth) / 2);
$('#imageid').css('top', ($(window).height() - imageheight) / 2);
}
$(document).ready(positionImage); // bind the ready event to reposition
$(window).resize(positionImage); // on window resize, reposition image too
if you keep a div element with height and width as 100% and bgcolor as black. And then change the opacity of the div as desired to get the fade in/out effect, that should generate the same effect. I guess..
You are better off using any available jQuery plugin as they would have optimized and fixed bugs for multiple browsers.
Try lightBoxMe plugin
http://buckwilson.me/lightboxme/
This is the simplest plugin available!
I want to put a bar on the bottom of my page containing a varying number of pictures, which (if wider than the page) can be scrolled left and right.
The page width is varying, and I want the pane to be 100% in width.
I was trying to do a trick by letting the middle div overflow and animate it's position with jquery.animate().
Like this:
Here is a fiddle without the js: http://jsfiddle.net/SoonDead/DdPtv/7/
The problems are:
without declaring a large width to the items holder it will not overflow horizontally but vertically. Is this a good hack? (see the width: 9000px in the fiddle)
I only want to scroll the middle pane if it makes sense. For this I need to calculate the width of the overflowing items box (which should be the sum of the items' width inside), and the container of it with the overflow: hidden attribute. (this should be the width of the browser window minus the left and right buttons).
Is there a way to calculate the length of something in js without counting all of it's childrens length manually and sum it up?
Is there a way to get the width of the browser window? Is there a way to get a callback when the window is resized? I need to correct the panes position if the window suddenly widens (and the items are in a position that should not be allowed)
Since the window's width can vary I need to calculate on the fly if I can scroll left or right.
Can you help me with the javascript?
UPDATE: I have a followup question for this one: Scroll a div vertically to a desired position using jQuery Please help me solve that one too.
Use white-space:nowrap on the item container and display:inline or display:inline-block to prevent the items from wrapping and to not need to calculate or set an explicit width.
Edit:: Here's a live working demo: http://jsfiddle.net/vhvzq/2/
HTML
<div class="hscroll">
<ol>
<li>...</li>
<li>...</li>
</ol>
<button class="left"><</button>
<button class="right">></button>
</div>
CSS
.hscroll { white-space:nowrap; position:relative }
.hscroll ol { overflow:hidden; margin:0; padding:0 }
.hscroll li { list-style-type:none; display:inline-block; vertical-align:middle }
.hscroll button { position:absolute; height:100%; top:0; width:2em }
.hscroll .left { left:0 }
.hscroll .right { right:0 }
JavaScript (using jQuery)
$('.hscroll').each(function(){
var $this = $(this);
var scroller = $this.find('ol')[0];
var timer,offset=15;
function scrollLeft(){ scroller.scrollLeft -= offset; }
function scrollRight(){ scroller.scrollLeft += offset; }
function clearTimer(){ clearInterval(timer); }
$this.find('.left').click(scrollLeft).mousedown(function(){
timer = setInterval(scrollLeft,20);
}).mouseup(clearTimer);
$this.find('.right').click(scrollRight).mousedown(function(){
timer = setInterval(scrollRight,20);
}).mouseup(clearTimer);
});
Thanks Phrogz for this part -- give the image container the white-space: nowrap; and display: inline-block;.
You can calculate the width without having to calculate the width of the children every time but you will need to calculate the width of the children once.
//global variables
var currentWidth = 0;
var slideDistance = 0;
var totalSize = 0;
var dispWidth = (winWidth / 2); //this should get you the middle of the page -- see below
var spacing = 6; //padding or margins around the image element
$(Document).Ready(function() {
$("#Gallery li").each(function () {
totalSize = totalSize + parseFloat($(this).children().attr("width"));// my images are wrapped in a list so I parse each li and get it's child
});
totalSpacing = (($("#Gallery li").siblings().length - 1) * spacing); //handles the margins between pictures
currentWidth = (parseFloat($("#Gallery li.pictureSelected").children().attr("width")) + spacing);
maxLeftScroll = (dispWidth - (totalSize + totalSpacing)); //determines how far left you can scroll
});
function NextImage() {
currentWidth = currentWidth + (parseFloat($("#Gallery li.pictureSelected").next().children().attr("width")) + spacing); //gets the current width plus the width of the next image plus spacing.
slideDistance = (dispWidth - currentWidth)
$("#Gallery").animate({ left: slideDistance }, 700);
}
There is a way to get the browser window with in javascript (jQuery example).
and there is a way to catch the resize event.
var winWidth = $(window).width()
if (winWidth == null) {
winWidth = 50;
}
$(window).resize(function () {
var winNewWidth = $(window).width();
if (winWidth != winNewWidth) {
window.clearTimeout(timerID);
timerID = window.setInterval(function () { resizeWindow(false); }, 100);
}
winWidth = winNewWidth;
});
On my gallery there's actually quite a bit more but this should get you pointed in the right direction.
You need to change your #items from
#items
{
float: left;
background: yellow;
width: 9000px;
}
to
#items {
background: yellow;
}
Then calculate the width very easily with jQuery
// #items width is calculated as the number of child .item elements multiplied by their outerWidth (width+padding+border)
$("#items").width(
$(".item").length * $(".item").outerWidth()
);
and simply declare click events for the #left and #right elements
$("#left").click(function() {
$("#middle").animate({
scrollLeft: "-=50px"
}, 'fast');
});
$("#right").click(function() {
$("#middle").animate({
scrollLeft: "+=50px"
}, 'fast');
});
jsFiddle link here
EDIT
I overlooked that detail about the varying image widths. Here is the correct way to calculate the total width
var totalWidth = 0;
$(".item").each(function(index, value) {
totalWidth += $(value).outerWidth();
});
$("#items").width(totalWidth);