Callback on owl carousel AFTER fully drawn on page - javascript

Imagine you have a single item carousel that sits at the absolute top of the page. Under that, you have the rest of your content. As the carousel is nested arbitrarily deep in the page, you've set it up to be absolutely positioned. But now you need to know how high the carousel is so you can put a top margin on the rest of the content so it does not overlap. This is what I am doing.
$('#carousel').owlCarousel({
items: 1,
onInitialized: adjustStretchHeader,
onResized: adjustStretchHeader
});
function adjustStretchHeader () {
setTimeout(function () {
return $('.page > .container')
.css('margin-top', $('.page > .stretch-header')
.height() + 15);
}, 250);
}
On the initialisation of the carousel and on resize events, I am getting it to get the carousel's height and update the top margin. The problem is that, without having a delay, the initialized event is triggering before the carousel is fully drawn on the page, so the height is unreliable.
With a delay, I'm able to get it properly. But this is obviously a hack, and I cannot guarantee that on slower devices, the carousel will have been drawn in time.
I cannot see any other useful events in the documentation.
Demonstration fiddle

You can use jQuery's get() method to grab the height of the images (even when they're hidden). This is independent of owlCarousel so don't have to wait for it to finish loading!
var images = $('#carousel img');
var firstImgHeight = images.get(0).height;
$('.page > .container').css('margin-top', firstImgHeight + 15);

Looking through the docs and the demos, I found this page: http://www.owlcarousel.owlgraphic.com/demos/events.html
It shows that the onRefreshed event is called after onInitialized and after the images have loaded. You can use that.
$('#carousel').owlCarousel({
items: 1,
onRefreshed: adjustStretchHeader
});
function adjustStretchHeader () {
return $('.page > .container')
.css('margin-top', $('.page > .stretch-header')
.height() + 15);
}
Fiddle
Although, I agree that the Docs aren't great at explaining the flow of the events. This still feels like a hack to me.

After initialization of carousel, DOMs of images have width but they don't have height until images are actually loaded. Try to use (width)*(aspect ratio) instead of height.
function adjustStretchHeader() {
return $('.page > .container')
.css('margin-top', $('.page > .stretch-header')
.width() * aspectratio + 15);
}

Related

jQuery height change not working in Safari

I have a JS feature on the following site that is working just fine in Firefox but not in Safari: http://rossbolger.com/kids/light-stories/
The feature slides out a grid of thumbnails called #image-thumbs when the mouse hovers over the container called #hit-area. It works (at least in Firefox) by first changing #image_thumbs height from '48px' to 'auto', the height is then measured using jQuery's height(). This height is stored in a variable and then using jQuery's css() it is given back to the #image-thumbs when the mouse is over.
The code on the site looks a little something like this:
// Thumbnails Nav Reveal and Hide Scripts
var thumbs_height = 1,
thumbs = $('#image-thumbs'),
thumbs_original_height = thumbs.css('height');
// Slide Up Thumbs
(function revealThumbs() {
// On hover let the thumbs become their full height
$('#image-thumbs #hit-area').hover(function(){ // Mouse over
// Get the unrestricted height of the thumbs
thumbs.css('height', 'auto');
thumbs_height = thumbs.height();
// then put it back to what it was so we can animate it using CSS3 transition
thumbs.css('height', 'inherit');
// delay 0.1s before triggering the change in height (time for the calculations to complete)
setTimeout( function() { thumbs.css('height', thumbs_height ) }, 100 );
}, function(){ // Mouse out
hideThumbs();
});
})();
// Hide thumbs
function hideThumbs(){
thumbs.css('height', thumbs_original_height );
};
The reason for measuring the unrestricted height and passing it back as a pixel value, rather than simply setting the height to 'auto', is to create a sliding effect via CSS3 (i.e. transition: height 0.5s). The transition only takes place if the affected attribute goes from one numeric value to another.
Thanks for any help bug testing this. I haven't even looked at other browsers yet.
All the best,
Laurence
Okay, so I've worked it out...
In the javascript document (scripts.js on the site) there was a function higher up the page calling the hideThumbs() function. So it wasn't working because the variables in hideThumbs() hadn't been declared at that point. Funny that it should still work in Firefox and IE9!
I've moved all this code to a point before that other function and the problem is now resolved. So far I've only done this locally. I'll update the site in the link above later.

Cloning content to a slider

I'm working on a responsive design, and would like some of my content in a slider when it's below a certain size. However, I would like to avoid rendering the data twice, so the idea is to clone the content and append it to the slider on (document).ready(), then initialise it. That way, I can show my content in the normal fashion in landscape mode, and in a slider in portrait mode (thus saving space).
var w = $(window).width();
if (w < 769) {
$("#container").children(".content").each(function () {
$(this).clone().appendTo("#slider ul").wrap("li");
});
initialiseSlider(); // Nothing special about this
}
The content is cloned to the slider container, but for some reason, it doesn't "slide". I'm thinking it may be because the slider is initialised before it has any content, because if I hardcode it, it works fine. Does that make sense? Any ideas on how I should fix this?
So actually, it might have been a whole different problem, possibly related to me having two of the same slider on the page.
var w = $(window).width();
if (w < 769) {
$('#programmeList').children('article').each(function () {
var data = $(this).clone().html();
$('#slider ul').append('<li>' + data + '</li>')
});
initialiseSlider();
}
Why they interfered, I don't know, as they had unique IDs and wasn't rendered at the same time anyway, but using append() instead of appendTo() helped.

How to resize Facebook Canvas app (iFrame) correctly?

I need to adjust canvas size after updating content of a page. I can do it explicitly by
FB.Canvas.setSize({ width: 760, height: 1480 });
however, it doesn't work without parameters, i.e. .setSize().
Also, I can adjust the height by
FB.Canvas.setAutoResize(true);
but only increasing - it doesn't reduce the height when content is reduced.
The following lines do not work:
FB.Arbiter.inform("setSize", FB.Canvas._computeContentSize());
FB.Canvas.setSize(FB.Canvas._computeContentSize());
How can one make it working?
Some more comments on the subject:
http://developers.facebook.com/blog/post/93
http://developers.facebook.com/docs/reference/javascript/fb.canvas.setautoresize
Related:
facebook-api: what's Facebook Connect cross-domain receiver URL?
facebook-app: how can i change the height of a resizeable iframe application?
Document height grows on resize when setting canvas dimensions dynamically
How do you control the size of your Facebook Canvas apps?
Don't write your own function for timed Auto-resize. Fb has one:
FB.Canvas.setAutoResize();
If you know when you need to resize it use
FB.Canvas.setSize()
So, if you want to make is smaller when you click a link, stick it in a function and then call that function later like:
function sizeChangeCallback() {
FB.Canvas.setSize();
}
//start some function or on click event - here i used jquery
$("#something").click(function() {
sizeChangeCallback()
});
You don't need to set an explicit height, but sometimes you can have trouble with dynamic xfbml elements like the comments plugin. Try using an event subscribe callback:
FB.Event.subscribe('xfbml.render', function(response) {
FB.Canvas.setAutoResize();
});
http://developers.facebook.com/docs/reference/javascript/FB.Canvas.setSize/
http://developers.facebook.com/docs/reference/javascript/FB.Canvas.setAutoResize/
http://developers.facebook.com/docs/reference/javascript/FB.Event.subscribe/
I have managed to make .setSize() working by delaying its execution (as suggested on various forums):
window.setTimeout(function() {
FB.Canvas.setSize();
}, 250);
If you have XFBLM elements, especially fb:comments, then you need to parse the page before setting its size
window.setTimeout(function() {
FB.XFBML.parse();
FB.Canvas.setSize();
}, 250);
Note, that you need to run this script after your content block, otherwise increase the time interval (in ms) allowing webserver and client browser building the content before resizing canvas.
This approach only increases the height of your page and doesn't decrease it. You can achieve decreasing manually by firing the following line before the code above
FB.Canvas.setSize({height: 480}); // min height - .setSize() won't reduce it
however, there is a drawback - the page will be blinking due to the double resizing.
Also, one suggests running .setSize in several time intervals to account delayed content:
FB.Array.forEach([300, 600, 1000, 2000], function(delay) {
setTimeout(function() {
FB.XFBML.parse();
FB.Canvas.setSize();
}, delay)
});
In this case, the page and XFBML elements become quite blinky.
We have pages in our iframe that append content to the page and then allow the user to refresh the content within the page in place. In these cases, we saw the same thing, where the content would not shrink when appropriate.
FB.Canvas.setSize() calls _computeContentSize, which is shown below:
_computeContentSize: function() {
var body = document.body,
docElement = document.documentElement,
right = 0,
bottom = Math.max(
Math.max(body.offsetHeight, body.scrollHeight) +
body.offsetTop,
Math.max(docElement.offsetHeight, docElement.scrollHeight) +
docElement.offsetTop);
if (body.offsetWidth < body.scrollWidth) {
right = body.scrollWidth + body.offsetLeft;
} else {
FB.Array.forEach(body.childNodes, function(child) {
var childRight = child.offsetWidth + child.offsetLeft;
if (childRight > right) {
right = childRight;
}
});
}
if (docElement.clientLeft > 0) {
right += (docElement.clientLeft * 2);
}
if (docElement.clientTop > 0) {
bottom += (docElement.clientTop * 2);
}
return {height: bottom, width: right};
},
The problematic line is here:
bottom = Math.max(
Math.max(body.offsetHeight, body.scrollHeight) +
body.offsetTop,
Math.max(docElement.offsetHeight, docElement.scrollHeight) +
docElement.offsetTop);
Even if the content inside your iframe has shrunk, the value of body.offsetHeight will not shrink.
To solve this, we made a custom version of the computeContentSize function that only consults the docElement for height, like so:
function rfComputeContentSize() {
var body = document.body,
docElement = document.documentElement,
right = 0,
bottom = Math.max(docElement.offsetHeight, docElement.scrollHeight) + docElement.offsetTop;
if (body.offsetWidth < body.scrollWidth) {
right = body.scrollWidth + body.offsetLeft;
} else {
FB.Array.forEach(body.childNodes, function(child) {
var childRight = child.offsetWidth + child.offsetLeft;
if (childRight > right) {
right = childRight;
}
});
}
if (docElement.clientLeft > 0) {
right += (docElement.clientLeft * 2);
}
if (docElement.clientTop > 0) {
bottom += (docElement.clientTop * 2);
}
return {height: bottom, width: right};
}
Anytime we want to resize and know that the content could shrink we'll use the custom function to pass content to setSize (e.g. FB.Canvas.setSize(rfComputeContentSize())), and anytime we know that the content will only grow, we'll use the standard FB.Canvas.setSize() function.
Note that we were using setAutoGrow() and I didn't check, but am assuming that it uses the same function to determine size. We disabled our call to setAutoGrow() and will have to be vigilant about calling setSize() at approrpriate times.
Logged this bug with Facebook: https://developers.facebook.com/bugs/228704057203827
We ran into an issue where their FB.Canvas functions didn't work, although everything was properly initialized, because they’re using ‘IFRAME’ instead of iframe in their cross-domain JS setup, hence no XHTML compatibility.
We fixed this by prototyping our own createElement function and overriding theirs.
Just put this right after you include Facebooks all.js:
document.__createElement = document.createElement;
document.createElement = function(tagName) {
return document.__createElement(tagName.toLowerCase());
}
bit of excess functionality, but this article explains why you cant do that dynamically and provides the easiest solution...
http://www.twistermc.com/36764/shrink-facebook-tabs/
The answer is simple:
Shrink it really small, then after a pause use AutoGrow to make it the correct size... Alice in Wonderland style.
I say easiest because from a dev and user point of view this slows things down by about 1/4 second, and the user gets a tiny flash of the scrollbars...
Bobby's answer is very good. Only thing I would add to it is that some people have had success getting the page to shrink by doing the following:
FB.Canvas.setSize({ width: 760, height: 10 });
And then follow it with:
FB.Canvas.setAutoResize();
You don't have to use 10, just something smaller than the minimum size of your page. Then the FB.Canvas.setAutoResize() should go ahead and make it the proper height again and continue to update. The only time you need to call FB.Canvas.setSize({ width: 760, height: 10 }); again would be if the content shrunk again.
The danger of doing this is that if the FB.Canvas.setAutoResize(); doesn't work, you could have content that is cut off.
None of this worked for me. Make sure to type this before the closing tag.
<div id="fb-root"></div>
<script type="text/javascript">
window.fbAsyncInit = function() {
//Der folgende Code ändert die Grösse des iFrames alle 100ms
FB.Canvas.setAutoResize(100);
};
(function() {
var e = document.createElement('script');
e.async = true;
e.src = document.location.protocol +
'//connect.facebook.net/en_US/all.js';
document.getElementById('fb-root').appendChild(e);
}());
</script>
I tried to use FB.Canvas.setSize(e); to resize my app, but it did not resize at all.
I took a look in the Facebook Javascript and found out, that FB.Canvas.setSize calls the FB.Arbiter.inform('setSize',e) function.
If I call the FB.Arbiter.inform('setSize',{ width: 760, height: 1480 }) function right from my code, the canvas resizes correctly.
This has worked for me:
<script type='text/javascript'>
window.onload=function() {
FB.Canvas.setSize({width:760,height:document.body.offsetHeight});
}
</script>
Note that you don't need FB.init unless you are using it for other reasons.
When resizing to the minimum height do not forget to make it a little bit bigger than it is, to account for horizontal scrolling bar and some extra pixels that some browsers add, otherwise you will end-up with vertical scroll.
Also I do not recommend to use body to count height.
This worked for me :
window.fbAsyncInit = function()
{FB.Canvas.setSize({height:document.getElementById('m_table').offsetHeight+40});}
where m_table is table ID with all my content (you can use div instead).

jQuery: custom horizontal accordion width issue

Im trying to create a custom horizontal accordion style showcase. In terms of actual functionality, i have the framework (which can be seen here):
http://www.jsfiddle.net/adrianjacob/UdUus/
However my main bug bear (and the clients) is that if you look at the right hand side, there is always slight movement/flickering as the widths animate up and down.
Ideally I want it to appear smooth so only the opening/closing lists have movement.
ANy advice would be much appreciated.
A.
Use the animate function's step (it's not well documented)... I've updated the demo
var panels = $('#promo li');
panels.hoverIntent(
function() {
if (!$(this).is('.expanded') && !panels.is(':animated')) {
$(this).animate({
width: 200
}, {
// width is the calculated width, ani is the animation object
step: function(width, ani) {
var w = Math.floor(width);
// use 250 so we end up with 50 as the reduced size
$('.expanded').css('width', (250 - w) + 'px');
$(ani.elem).css('width', (200 - w) + 'px');
},
duration: 500,
complete: function() {
panels.removeClass('expanded');
$(this).addClass('expanded');
}
});
}
}, function() {});
A similar method is used in the Kwicks plugin.
You could try this plugin which may have figured out the bug. The example provided was too showy to actually tell.
Have you played around with jQuery UI easings?
You can also try to stop the event just when the div is opening.
The website with explanation is: http://api.jquery.com/stop/

Jquery Cycle + Firefox Squishing Images

I am running jQuery Cycle for an image gallery. View the link: Here
My problem is that the images are getting squished when viewed in firefox. The problem disappears when I re-load the page. This leads me to believe that the Javascript is triggering before all the images are loaded (usually the first image works fine and the rest are squished.)
A hard re-fresh reproduces the problem.
I've wrapped everything in a $(document).ready(function(){ }); but it still happens.
Additional Info: If I specify the width and height of the image, everything works fine. However there are hundreds of images all at different sizes..
I'm pretty frustrated with this problem. Any ideas/help is greatly appreciated!
Here is my code:
$(document).ready(function(){
//function onBefore(curr,next,opts) {
// var $slide = jQuery(next);
// var w = $slide.outerWidth();
// var h = $slide.outerHeight();
// $slide.css({
// marginTop: (482 - h) / 2,
// marginLeft: (560 - w) / 2
// });
//};
// Decare the function that center the images...
function onBefore(curr,next,opts) {
var $slide = jQuery(next);
var w = $slide.outerWidth();
var h = $slide.outerHeight();
$slide.css({
marginTop: (480 - h) / 2,
marginLeft: (560 - w) / 2
});
};
$(document).ready(function() {
$('#slideshow').cycle({
fx: 'fade',
next: '#next',
pause: 0,
speed: 500,
before: onBefore,
prev: '#prev',
pause: '#pause',
pager: '.thumbs',
pagerClick:function(zeroBasedSlideIndex, slideElement) {$(slideElement).find('div.cover').hide();},
pagerAnchorBuilder: function(idx, slide) {
var src = $('img',slide).attr('src');
//Change height of thumbnail here
return '<li><img src="' + slide.src + '" height="90" /></li>';
}
});});});
There is a much simpler and cleaner solution that I used to solve this problem than what has already been proposed:
Using jQuery, you need to use $(window).load instead of $(document).ready for your particular situation. To fix the issue, change this:
$(document).ready(function() {
$('#slideshow').cycle({
/* ... */
});
});
To this:
$(window).load(function() {
$('#slideshow').cycle({
/* ... */
});
});
Why does this work? Because window.onload fires after all referenced images on the page are loaded (See https://developer.mozilla.org/en/DOM/window.onload, and .load() - jQuery API), which is the desired behavior in your situation. $(document).ready, better known as "DOM Ready", will fire before images have loaded. This is typically the desired behavior, but in your situation it's too early.
I had the same problem when working on a site several months ago (linked below). If you're starting cycle in $(document).ready(), here's what happens when a client browses to your page:
1) The client's browser sends a request for each img element. Those requests take variable amounts of time to fulfill.
2) Before the image requests are completed, cycle starts. Cycle works by hiding all but the first image in the slide show: it sets visibility:hidden and display:none on each of its images.
The problem is that Firefox fixes the img element's size once and for all at the point the display style is set to none. So if the image hasn't finished loading, its height and width style attributes are small (I'm not sure exactly what they correspond to - perhaps the size of Firefox's image placeholder). When cycle shows the image by setting its style attribute to display:block, it uses whatever dimensions it had at the time it was hidden.
I solved this by changing my code so that it doesn't start the cycle plugin until all the images are finished loading. To do that, I initialize a counter variable to the number of images I'm cycling, then bind a load event to each image like this:
var imagesRemaining = 12; // 12 is just the number of images in the slideshow div
$(document).ready(function() {
$('#slideshow > img').bind('load', function(e) {
imagesRemaining = imagesRemaining - 1;
if (imagesRemaining == 0) {
// I'm doing some other stuff when initializing cycle
startCycle();
// My images all start with visibility:hidden so they don't show
// before cycle hides them in a 'stack', so ...
$('#slideshow > img').css('visibility', 'visible');
}
});
});
function onBefore(curr, next, opts) { // Your code here ... }
function startCycle() {
$('#slideshow').cycle({ ... // your initialization here });
}
You can see it in action by viewing the galleries on this site in Firefox. I'm building the gallery pages dynamically, so it's structured a bit differently than your page, but you can see more details if you poke around with Firebug.
I'd also like to add that it seems adding a width and height attribute solves this problem.
Ok i know its probably an awfull way of calling load but i just coulnd bind my cycle code to .load for some reason it just don't work so i called the whole Cycle initializer inside the ...
i couldn't force the sizes since i'm cycling through li containing dynamic images and data
its probably flawed at some extend but for those as desperated as me...
Josh, your solution has just saved me a headache, thank you very much!
I think i've amended it slightly in order to handle pages where you don't know the total number of images. It seems to be working fine for me, if anyone can see any flaws, please point them out - i'm still learning.
$(document).ready(function () {
$('#slideshow > img').each(
function go() {
$(this).bind('load', function (e) {
projects();
$('#slideshow > img').css('visibility', 'visible');
});
});
});
function projects() {
$('#slideshow').cycle({
fx: 'scrollHorz',
speed: 300,
timeout: 0,
next: '#next ',
prev: '#prev ',
after: onAfter,
nowrap: 1,
autostop: 1
});
}
If you're using a database to populate the slideshow you could try accessing the image dimensions from the image itself.
For example, using django you can use
width="{{ xxx.image.width }}px" height="{{ xxx.image.height }}px"
in your img tag.
You can use a solution similar to making youtube videos responsive. You need to know the ratio of your width to height, and add that as padding-bottom to the cycling div. For my 1024X680 photos, I used 680/1024 = 66.4%
In your case, I believe
#slideshow{
padding-bottom:66.4%;
}
will show the image unshrunk. I have no idea what the actual height and width values you are working with, so substitute your own. I had to use this solution when the $(window).load solution proved maddeningly ineffective -- so now I use both.
This is better than setting the dimensions of the image, because it's slides into a fluid, responsive enviroment.

Categories

Resources