jQuery: custom horizontal accordion width issue - javascript

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/

Related

Callback on owl carousel AFTER fully drawn on page

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);
}

CSS Zoom interfering with jQuery scrollTop

I'm creating a Website that, on Mobile Browsers, zooms out, as it's a fixed width website. Instead of using the viewport meta tags I've taken to the following:
html, body {
zoom: 0.8;
}
This works great, but the problem is with my jQuery. I'm using simple jQuery code for a one-page website smooth scrolling, shown below:
/* Start Navigation */
jQuery('nav ul.menu li').click(function(e) {
if(!scrollingAnim) {
var page = jQuery(this).attr('data-page');
updateMenu(page);
scrollingAnim = true;
jQuery('html, body').stop().animate({
scrollTop: (jQuery(page).offset().top - 70)
}, 2000, 'swing', function() {
scrollingAnim = false;
});
}
e.preventDefault();
return false;
});
/* End Navigation */
This used to work fine, but for obvious reasons will not work with my zoom. I've tried multiplying it by the zoom level, but it doesn't work. Is there anything you'd suggest?
jQuery's .offset().top is changing with the change of the window scroll when you have CSS zoom involved...
Take a look at my answer here https://stackoverflow.com/a/21048208/1090395
Hope it helps
Faced with similar problem related to zoom and offset().Top. Tried to play with values in JS console and found this formula:
var scrollTopAnimateTo = ($(anchor).offset().top - $(".header").height()) * zoom + $(window).scrollTop() * (1 - zoom);
where zoom < 1.

How to follow jQuery animated div across screen?

I'm using jQuery.crSpline to animate a graphic along a curved path. I'm pretty happy with the result.
However, the full canvas size is intentionally pretty wide - definitely larger than most screens - so the graphic will run out of viewport space pretty quickly and animate off the screen.
Instead, I'd like browser viewport to follow or centre on the image so that it stays 'in shot'.
How would I go about this with jQuery? Is scrollTop an option?
I've created a jsFiddle demo, based on the crSpline demo source, but with a wide minX setting.
NB: I did first attempt this with YUI3 and Loktar offered a canvas based solution, however I'm not using YUI & canvas any longer.
Is this what you had in mind? http://jsfiddle.net/gNdwD/33/. It seems a little choppy but its a rough first attempt.
It doesn't seem like crSpline exposes any coordinates on the animated element so we have to periodically observe them and adjust the viewport accordingly:
setInterval(function() {
var mover = $('#mover'),
posX = mover.position().left,
posY = mover.position().top;
$(window)
.scrollLeft(posX - $(window).width() / 2)
.scrollTop(posY - $(window).height() / 2);
}, 10);
I suspect the choppiness happens because our setInterval is out of sync with the $.animate on the mover. You can fix that by running two animations: one on the mover and one on the scrollTop and scrollLeft of a wrapper div. You can simultaneously apply two $.animates like this.
There exists an option for step function in jQuery animate,which is run on every step of the animation.
See second version of function parameters here :
http://api.jquery.com/animate/
.animate( properties, options )
propertiesA map of CSS properties that the animation will move toward.
optionsA map of additional options to pass to the method. Supported keys:
duration: A string or number determining how long the animation will run.
easing: A string indicating which easing function to use for the transition.
complete: A function to call once the animation is complete.
step: A function to be called after each step of the animation.
queue: A Boolean indicating whether to place the animation in the effects queue. If false, the animation will begin immediately. As of jQuery 1.7, the queue option can also accept a string, in which case the animation is added to the queue represented by that string.
specialEasing: A map of one or more of the CSS properties defined by the properties argument and their corresponding easing functions (added 1.4).
See this fiddle based on your code, which calls step function to adjust viewport :
http://jsfiddle.net/gNdwD/35/
$('<div id="mover" />')
.appendTo($(document.body))
.animate({ crSpline: spline },{
duration: 20000,
step: function() { /* THE STEP FUNCTION TO ADJUST VIEWPORT */
var mover = $('#mover'),
posX = mover.position().left;
posY = mover.position().top;
$(window)
.scrollLeft(posX - $(window).width() / 2)
.scrollTop(posY - $(window).height() / 2);
} ,
complete:function () {
// Re-run the demo with a new spline after we're done
window.setTimeout(function() {
DEMO.run();
}, 5000);
}
});

animated scroll is very buggy

So I've been trying to figure this out for awhile and can't seem to get it.
I use a navigation consisting of circles (see website below) and when the user clicks on one, it forwards him/her to the corresponding slide.
When you click around, it will sometimes slide all the way back to the beginning of the window (margin-left = 0). If it doesn't do it at first, just click around for a second or two and you'll eventually see it.
http://dan.stargroupdev.com/
Here's the code that's buggy:
$("#footer-slidenav .links a").click(function () {
// Get nav index
var slidenum = $(this).attr("id").slice(3);
// Setup slide selector with string to avoid issues
var slidetext = ".slide:eq(" + slidenum + ")";
slidenum = $(slidetext).offset().left;
console.log("Top: " + slidenum);
var offset2 = 0;
// Find window offset to center slide if screen is bigger than 1000px (size of one slide)
if (($(window).width() - 1000) / 2 > 0) {
offset2 = ($(window).width() - 1000) / 2;
}
// Slide window to slide # that was clicked
$("html:not(:animated), body:not(:animated)").animate({
scrollLeft: slidenum
}, 1000, function () {
console.log("Middle: " + slidenum);
// Callback to center slide and give a nice little animated touch
slidenum = $(slidetext).offset().left;
console.log("Bottom: " + slidenum);
$("html:not(:animated), body:not(:animated)").animate({
scrollLeft: (slidenum - offset2)
}, "fast");
});
return false;
});
I tried things like $("html:not(:animated), body:not(:animated)") along with a few other similar possible solutions, but the bug is still there.
Any advice would be great and I'm more than happy to entertain any ideas you guys might have.
Thanks.
Turns out I had a leftover piece of code in another JS file. Sorry for wasting your time guys, that's why it was getting messed up.
I appreciate your answers though.
Ill propose an entirely new solution(mostly new)
$("#footer-slidenav .links a").click(function () {
var slidenum = $(this).attr("id").slice(3);
var slidetext = ".slide:eq(" + slidenum + ")";
offset = $(slidetext).position().left;
console.log(offset );
$("body").animate({
scrollLeft: offset
});
});
Could you give it a try?
my suggestion is to test the site without the class front on body. The script http://dan.stargroupdev.com/sites/all/themes/starsite/js/starsite.js use this class with result to create the unwanted behavior I think. If I remove it with firebug it works ok for me. Check it and give feedback to see if this is true.
First of all can I suggest Arial Flesler's scrollTo plugin? It's awesome and works like a charm.
You can use offsets to center the final position.
If you want some kind of easing then you can use them to make the animation look more natural. What about an elastic animation?
I've been using this plugin for over one year, without finding any problem.
Also, remember to .stop() the scrolled/animated element.

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