Compute div style only when it completes rendering an image - javascript

In my script I am loading an image from an array into a div, then I calculate some style elements to be tuned.
The problem is that this works only if I use a timeout function before calculating the styles, like this:
$('#forecastImg').attr('src',get_ImageItemLocation(imageArray[0]));
setTimeout(function(){
var himg= $("#forecastImg").height();
var hest = $("#esterno").height();
var margin= ((hest-himg)/2)-$(".header").height()-$(".forecastdate").height();
if (margin>0){
$("#forecastImg").css('margin-top',(margin+'px'));
}
},240);
How can I get rid of the timeout and be sure that the height() value is correct?
If I remove the timeout, I alwyas get a height()=0 value.

Well the problem here is that the image is not loaded when you're trying do calculate the margin, that's why $('#forecastImg').height() is not returning the expected height when you try to make the calculations right away (in your case, you're doing a wait for the loading with a setTimeout)
However, you can use $().load method instead of a unreliable setTimeout to consistently run your calculations after the loading of the image. To do that, you must first bind the .load callback method with those calculations and only then change the src value of it, otherwise the image might load immediately without calling our .load callback:
$('#forecastImg')
.one('load', function () {
var himg = $("#forecastImg").height(),
hest = $("#esterno").height(),
margin = ((hest-himg) / 2) - $(".header").height() - $(".forecastdate").height();
if (margin > 0) {
$("#forecastImg").css('margin-top',(margin+'px'));
}
})
.attr('src', get_ImageItemLocation(imageArray[0]));

You need to wait for the image has loaded, so the timeout is correct, but if you would like to use a different solution, you can use load event instead:
$("#forecastImg").one("load", function() {
var himg= $(this).height();
var hest = $("#esterno").height();
var margin= ((hest-himg)/2)-$(".header").height()-$(".forecastdate").height();
if (margin>0){
$("#forecastImg").css('margin-top',(margin+'px'));
}
});

try the load event of JQuery
$('#forecastImg').load(function() {
var himg = $("#forecastImg").height();
var hest = $("#esterno").height();
var margin = ((hest-himg)/2)-$(".header").height()-$(".forecastdate").height();
if (margin > 0) {
$("#forecastImg").css('margin-top',(margin+'px'));
}
});

Related

Javascript code not getting executed on initial page load

I have written a Javascript function for resizing the Carousel according to the screen width. It's doing the job properly but the only issue is that it doesn't get initialized on the initial page load. The Carousel is only present on the homepage and when I visit it for the first time, the code doesn't get executed. However, if I reload the page it does get executed. It even gets executed when I resize my browser window. But I would like it to initialize on the first first load as well. Here's the code:
let carousel = document.querySelector('#carousel');
if (carousel) {
const size = 0.8;
window.addEventListener('resize', () => {
let width = carousel.clientWidth || carousel.offsetWidth;
let carouselHeight = (width * size) + 'px';
carousel.querySelector(".slide").style.height = carouselHeight;
}, false);
}
You're adding a listener, but the callback won't get called unless it is triggered.
You can instead declare the callback as a function, add the listener with the function as callback, and then call the function directly for the initial load.
function resizeCarousel(size) {
let width = carousel.clientWidth || carousel.offsetWidth;
let carouselHeight = (width * size) + 'px';
carousel.querySelector(".slide").style.height = carouselHeight;
}
let carousel = document.querySelector('#carousel');
if (carousel) {
const size = 0.8;
window.addEventListener('resize', () => resizeCarousel(size), false);
resizeCarousel(size);
}
You’re adding an event listener on the page resize. First of all, this should all be wrapped in an on load event listener so you can be sure that the element #carousel is even available. Then, I would execute the resize function manually by maybe defining the function in the on load event function. Then set it for resizing events. I’m on my phone so it’s hard to type an example, for for example:
document.addEventListener("DOMContentLoaded", function(event) {
function resize() { /* Resize Function. */ }
var carousel = document.querySelector('#carousel');
window.addEventListener(“resize”, resize);
resize(); // Call it now as well as onresize.
});

getBoundingClientRect in each: undefined is not a function

So I am trying to call some functions when fullscreen sections are in the viewport. Let's say I have 7 sections, then I want something to happen when a certain section is inside the viewport (I have a function that snaps the sections into the viewport so there can never be multiple sections in the viewport, but I am trying to find out which section is visible in the viewport).
Here is a fiddle: http://jsfiddle.net/h7Hb7/2/
function isInViewport() {
$("section").each(function () {
var $this = $(this),
wHeight = $(window).height(),
rect = $this.getBoundingClientRect(); // Error in console
// Borrowed from http://stackoverflow.com/a/7557433/5628
if (rect.top >= 0 && rect.bottom <= wHeight) {
console.log($this.attr("id") + "in viewport");
}
});
}
$(window).scroll(function () {
// Other functions are called inside the setTimeout function, can't remove
clearTimeout($.data(this, "scrollTimer"));
$.data(this, "scrollTimer", setTimeout(function () {
isInViewport();
}, 1200));
});
I don't know where to start looking but I am guessing it's to do with the each function. Is it the each function that poses a problem? It can't be a CSS issue, because the problem occurs on scroll when the CSS has already loaded.
You could stick with jQuery and use the array [] notation ie:
var myClient = $(currentGrid)[0].getBoundingClientRect();
alert(myClient.top)
jQuery object doesn't have getBoundingClientRect method, you should get the HTMLElement object and then call the method or:
this.getBoundingClientRect();
As a suggestion, if using a plugin is an option, you can consider using the jquery.inview plugin.
You can pass event through function and use
e.target.getBoundingClientRect() function. It will Work

jQuery event listener for fully loaded DOM with dynamic content

I'm trying to use jQuery script to align height of two divs. Everything works fine until I have some dynamic content in one of divs.
When I hardcode some static content in one of divs like:
<br>asd<br>asd<br> x 20
both divs has the same height property, but when I load some data from DB to one of divs, they are different.
I guess that the problem is in .ready() listener. Documentation says that it fires when DOM is fully loaded but it looks like it's not the truth.
My question is: what kind of listener or other 'trick' should I use? I think that jquery/javascript solution is cleaner than messing with css and I would like to have this kind of solution.
Thanks in advance.
jquery script:
$(document).ready(function(){
var difference = $("#layout-navigation-wrapper").height() - $("#layout-content-wrapper").height();
if(difference<0)
{
var height = $("#layout-content-wrapper").height() -1;
$("#layout-navigation-wrapper").height(height);
}
else if(difference >= 0)
{
var height = $("#layout-navigation-wrapper").height() -2;
$("#layout-content-wrapper").height(height);
}
});
jquery in the base work with event document.ready is means when all DOM is ready until here make the jquery code. is for don't have a option to render jquery code without render jquery library
if you want to add event just when all the dom is loaded include content and images you need to do this
$(document).ready(function(){
$(window).load(function(){
var difference = $("#layout-navigation-wrapper").height() - $("#layout-content-wrapper").height();
if(difference<0)
{
var height = $("#layout-content-wrapper").height() -1;
$("#layout-navigation-wrapper").height(height);
}
else if(difference >= 0)
{
var height = $("#layout-navigation-wrapper").height() -2;
$("#layout-content-wrapper").height(height);
}
});
});
You can use window.onload to execute a script once a web page has completely loaded all content including images, script files, CSS files, etc.
window.onload = function() {
var difference = $("#layout-navigation-wrapper").height() - $("#layout-content-wrapper").height();
if(difference<0)
{
var height = $("#layout-content-wrapper").height() -1;
$("#layout-navigation-wrapper").height(height);
}
else if(difference >= 0)
{
var height = $("#layout-navigation-wrapper").height() -2;
$("#layout-content-wrapper").height(height);
}
};

how to get the final size of the element in javascript based animation

In our page,we often use the javascript based animation to make the element move/resize,most of them use the setTimeout or setInterval to change the position or size of the element:
For example:
function open() {
var w=parseInt(foo.style.width);
if(w>=200) clearTimeout(t);
else{
foo.style.width = +1+'px';
t=setTimeout(open,20); // call doMove in 20msec
}
}
function init() {
foo = document.getElementById('dv'); // get the "foo" object
foo.style.width = '0px'; // set its initial position to 0px
open(); // start animating
}
The above code want to show the div#foo slowly,but as you see,I set the maxwidth of the div to 200px to stop the animation.
But how about if the real size of the content div should more than 200?
That's to say I can not get the final width of the element.
What is the general solution?
Here is the live exmple:
Use the modified code as shown below. It will continue expanding until the element has reached its full width.
function open() { //See comment at OP
var w = foo.offsetWidht; // offsetWidht NOT style.width
var realWidth = foo.scrollWidth;
if(w < 200 || w < realWidth) {
foo.style.width = +1+'px';
setTimeout(open, 20); // call doMove in 20msec
}
}
Also, you don't need clearTimeout, since no timeout has been set when the function is called again.
Use one of the various methods of computing styles. Google "JavaScript compute width" and there will be lots of results. Try the property offsetWidth first. I can't quite remember its compatibility table but it definitely works in Chrome, Safari, and IE8/9.

How to improve image cross-fade performance?

I want to be able to do a cross fade transition on large images whose width is set to 100% of the screen. I have a working example of what I want to accomplish. However, when I test it out on various browsers and various computers I don't get a buttery-smooth transition everywhere.
See demo on jsFiddle: http://jsfiddle.net/vrD2C/
See on Amazon S3: http://imagefader.s3.amazonaws.com/index.htm
I want to know how to improve the performance. Here's the function that actually does the image swap:
function swapImage(oldImg, newImg) {
newImg.css({
"display": "block",
"z-index": 2,
"opacity": 0
})
.removeClass("shadow")
.animate({ "opacity": 1 }, 500, function () {
if (oldImg) {
oldImg.hide();
}
newImg.addClass("shadow").css("z-index", 1);
});
}
Is using jQuery animate() to change the opacity a bad way to go?
You might want to look into CSS3 Transitions, as the browser might be able to optimize that better than Javascript directly setting the attributes in a loop. This seems to be a pretty good start for it:
http://robertnyman.com/2010/04/27/using-css3-transitions-to-create-rich-effects/
I'm not sure if this will help optimize your performance as I am currently using IE9 on an amped up machine and even if I put the browser into IE7 or 8 document mode, the JavaScript doesn't falter with your current code. However, you might consider making the following optimizations to the code.
Unclutter the contents of the main photo stage by placing all your photos in a hidden container you could give an id of "queue" or something similar, making the DOM do the work of storing and ordering the images you are not currently displaying for you. This will also leave the browser only working with two visible images at any given time, giving it less to consider as far as stacking context, positioning, and so on.
Rewrite the code to use an event trigger and bind the fade-in handling to the event, calling the first image in the queue's event once the current transition is complete. I find this method is more well-behaved for cycling animation than some timeout-managed scripts. An example of how to do this follows:
// Bind a custom event to each image called "transition"
$("#queue img").bind("transition", function() {
$(this)
// Hide the image
.hide()
// Move it to the visible stage
.appendTo("#photos")
// Delay the upcoming animation by the desired value
.delay(2500)
// Slowly fade the image in
.fadeIn("slow", function() {
// Animation callback
$(this)
// Add a shadow class to this image
.addClass("shadow")
// Select the replaced image
.siblings("img")
// Remove its shadow class
.removeClass("shadow")
// Move it to the back of the image queue container
.appendTo("#queue");
// Trigger the transition event on the next image in the queue
$("#queue img:first").trigger("transition");
});
}).first().addClass("shadow").trigger("transition"); // Fire the initial event
Try this working demo in your problem browsers and let me know if the performance is still poor.
I had the same problem too. I just preloaded my images and the transitions became smooth again.
The point is that IE is not W3C compliant, but +1 with ctcherry as using css is the most efficient way for smooth transitions.
Then there are the javascript coded solutions, either using js straight (but need some efforts are needed to comply with W3C Vs browsers), or using libs like JQuery or Mootools.
Here is a good javascript coded example (See demo online) compliant to your needs :
var Fondu = function(classe_img){
this.classe_img = classe_img;
this.courant = 0;
this.coeff = 100;
this.collection = this.getImages();
this.collection[0].style.zIndex = 100;
this.total = this.collection.length - 1;
this.encours = false;
}
Fondu.prototype.getImages = function(){
var tmp = [];
if(document.getElementsByClassName){
tmp = document.getElementsByClassName(this.classe_img);
}
else{
var i=0;
while(document.getElementsByTagName('*')[i]){
if(document.getElementsByTagName('*')[i].className.indexOf(this.classe_img) > -1){
tmp.push(document.getElementsByTagName('*')[i]);
}
i++;
}
}
var j=tmp.length;
while(j--){
if(tmp[j].filters){
tmp[j].style.width = tmp[j].style.width || tmp[j].offsetWidth+'px';
tmp[j].style.filter = 'alpha(opacity=100)';
tmp[j].opaque = tmp[j].filters[0];
this.coeff = 1;
}
else{
tmp[j].opaque = tmp[j].style;
}
}
return tmp;
}
Fondu.prototype.change = function(sens){
if(this.encours){
return false;
}
var prevObj = this.collection[this.courant];
this.encours = true;
if(sens){
this.courant++;
if(this.courant>this.total){
this.courant = 0;
}
}
else{
this.courant--;
if(this.courant<0){
this.courant = this.total;
}
}
var nextObj = this.collection[this.courant];
nextObj.style.zIndex = 50;
var tmpOp = 100;
var that = this;
var timer = setInterval(function(){
if(tmpOp<0){
clearInterval(timer);
timer = null;
prevObj.opaque.opacity = 0;
nextObj.style.zIndex = 100;
prevObj.style.zIndex = 0;
prevObj.opaque.opacity = 100 / that.coeff;
that.encours = false;
}
else{
prevObj.opaque.opacity = tmpOp / that.coeff;
tmpOp -= 5;
}
}, 25);
}

Categories