I built a slider that moves left or right if some items are hidden. Obviously this needs to work responsively, so I am using a resize (smartresize) function to check when the browser is resized. It works, but after resizing when you click more (right arrow) it takes 2-5 seconds to actually calculate what is hidden and then execute.
Can anyone explain to me why this is happening, and how to possibly fix it?
Thanks!
$(window).smartresize(function () {
var cont = $('#nav-sub-menu-2 .container');
var ul = $('#nav-sub-menu-2 ul');
var li = $('#nav-sub-menu-2 ul li');
var amount = li.length;
var width = li.width();
var contWidth = cont.width();
var ulWidth = width * amount;
var remainder = ulWidth - contWidth;
ul.width(ulWidth);
if(remainder <= 0) {
$('.more, .less').fadeOut();
} else {
$('.more').fadeIn();
}
$('.more').click(function() {
ul.animate({ 'right' : remainder });
$(this).fadeOut();
$(".less").fadeIn();
});
$('.less').click(function() {
ul.animate({ 'right' : 0 });
$(this).fadeOut();
$(".more").fadeIn();
});
}).smartresize();
It could be because it is recalculating the screen size at every interval as you are resizing...
Try using a debouncer to delay the function calls until everything's settled.
/* Debounce Resize */
function debouncer( func , timeout ) {
var timeoutID , timeout = timeout || 200;
return function () {
var scope = this , args = arguments;
clearTimeout( timeoutID );
timeoutID = setTimeout( function () {
func.apply( scope , Array.prototype.slice.call( args ) );
} , timeout );
}
}
$( window ).resize( debouncer( function ( e ) {
/* Function */
}));
Related
I am trying to add a class to an element when it is in the viewport. I have achieved this however it causes serious issues to the performance of my site when I scroll.
I currently have this JavaScript:
//Cache reference to window and animation items
var $animation_elements = $('.animation-element');
var $window = $(window);
$window.on('scroll resize', check_if_in_view);
$window.trigger('scroll');
function check_if_in_view() {
var window_height = $window.height();
var window_top_position = $window.scrollTop();
var window_bottom_position = (window_top_position + window_height);
$.each($animation_elements, function() {
var $element = $(this);
var element_height = $element.outerHeight();
var element_top_position = $element.offset().top;
var element_bottom_position = (element_top_position + element_height);
//check to see if this current container is within viewport
if ((element_bottom_position >= window_top_position) &&
(element_top_position <= window_bottom_position)) {
$element.addClass('in-view');
} else {
$element.removeClass('in-view');
}
});
}
So as you can see the check_if_in_view() function seems to be constantly firing as the page is being scrolled and I believe this might be the reason why the performance might be so bad.
Is there a more efficient way of adding a class when scrolling the page that wont cause performance issues on my site?
Use setTimeout to delay calling the function every time a scroll event is fired. In the following code (which I borrowed from Codrops), a flag is set to call the function every 60 milliseconds in the case of continous scrolling.
function Scroller(el) {
this.elements = Array.prototype.slice.call( el );
this._init();
}
Scroller.prototype = {
_init : function() {
//this flag prevents that the function _scrollPage is called
//every time the 'scroll' event is fired
this.didScroll = false;
window.addEventListener( 'scroll', this._scrollHandler.bind(this), false );
},
_scrollHandler : function() {
if( !this.didScroll ) {
this.didScroll = true;
setTimeout( function() { this._scrollPage(); }, 60 );
}
},
_scrollPage : function() {
this.elements.forEach( function( el, i ) {
if( inViewport(el) ) {
classie.add( el, 'i-am-in-the-viewport' );
}
else {
classie.remove( el, 'i-am-in-the-viewport' );
}
});
this.didScroll = false;
}
};
To use it call new Scroller( document.getElementsByClassName('elements-to-watch') );.
Check out the complete code on Codrops to see the implementation of the inViewPort() function. Classie.js is used to handle the assignation of class names.
Don't be afraid to ask for clarification if there's something you don't get!
I am trying to create an image gallery in HTML, CSS. And to use JavaScript to animate the transition. I've managed to get that working, so I decided I wanted a loop within it so that when it reached the last image (4th) it will loop - transition back to the first, and carry on with the sequence until it got to the end again.
Download to look at the site to get an idea of my problem - https://drive.google.com/folderview?id=0B8HDvQ3oZFi6MG5GLTBFWGNmZkU&usp=sharing
I've tried many ways of doing this, I've got it to loop back fine. My entire code:
var imagenum = 0;
var currentimg = 1;
var maxwidth = 0;
$(document).ready(function() {
var Div1 = $("#ip").offset().top;
var Div3 = $("#flash").offset().top;
$(window).scroll(function() {
if($(window).scrollTop() < Div3) {
$("#ip").fadeIn(400);
}
else if($(window).scrollTop() > Div3) {
$("#ip").fadeOut(400);
}});
});
$(document).ready(function() {
$( '.G-li' ).each(function() {
imagenum++;
maxwidth += 830;
});
$( '.G-ul' ).css('width' , maxwidth + 'px');
$( '.rightbtn-inner' ).click(function(){
moveLeft();
});
$( '.leftbtn-inner' ).click(function(){
moveRight();
});
timer();
loop();
});
function moveLeft() {
if( currentimg < imagenum ) {
$( '.G-ul' ).animate( {'marginLeft' : '-=820px' } , 1000 , 'swing' );
currentimg = currentimg + 1;
}
}
function moveRight() {
if( currentimg > 1 ) {
$( '.G-ul' ).animate( {'marginLeft' : '+=820px' } , 1000 , 'swing' );
currentimg = currentimg - 1;
}
}
function timer() {
setInterval(moveLeft, 10000);
}
function loop() {
if( currentimg = imagenum ) {
setInterval(loopbk, 10000);
currentimg = 1; // I did the reset here
}
function loopbk() {
$( '.G-ul' ).animate( {'marginLeft' : '+=2460px' } , 1000 , 'swing' );
/* currentimg = 1; // and tried reset here */
}
}
To make it carry on with the sequence as normal I have to reset the variable currentimg to 1 for this to work. But if i do currenting = 1 in the function loop() it goes to the last image and carries on for another until looping back.
Or if I place the reset in the function loopbk(), it loops perfectly until the 2nd image and then proceeds to do the loop resulting in going left too far.
Can someone play around with this and help me as from my eyes this should work, but it doesn't - and I've been trying to solve this for a very long time.
Thank you to anyone that can help.
Andy.
I'm trying to resize 3 panes inside a container , when resized, they shouldn't exceed the width of container (do not get out of it)..
But in this snippet http://jsfiddle.net/KXsrd/ , when I resize the second one, it successfully decreases the next element or increases depending on the direction. but sometimes it causes the next sibling to drop below the container and although the next sibling decreases its size when I expand one , it keeps 'shaking' ..
Here is the main function:
$(function () {
$('.col').each(function (e, u) {
$(u).data({ow: $(u).width()});
})
$(".col").resizable(
{
handles: 'e',
start: function (e) {
},
resize: function (e, ui) {
var el = ui.element;
var parent = el.parent();
var next = el.next();
var siblings = el.siblings();
var siblingsWidth = (function () {
var totalWidth = 0;
_.each(siblings, function (el) {
totalWidth += $(el).width();
});
return totalWidth;
})();
var currentSize = ui.size;
if (currentSize.width > $(el).data('ow')) {
next.css({width: '-=' + (currentSize.width - $(el).data('ow'))});
} else {
next.css({width: '+=' + ( $(el).data('ow') - currentSize.width )});
}
if (currentSize.width + siblingsWidth >= $(parent).width()) {
$(el).width($(parent).width() - siblingsWidth)
}
$(el).data({ow: currentSize.width});
},
stop: function (e, ui) {
}
});
});
Any help would be much appreciated ..
(PS. I tried many plugins for resizable panes. but they all work on 2 panes only)
I have a gallery of three Grids with images. The grid sizes changes depending on the screen size, and I have achieved that using Media-Query - ie, on desktop the grid's width will be 33% to make three columns view next to each other, and on tablet it will be 50% to make two columns view, and on phone it will be a 100% for each grid making one column view.
The reason I did this is to create a tiled gallery with images of different heights - and if I did it the normal way it will generate White-empty-spaces when floating.
So to fix this problem, and with the help of few members on this website, we have created a JavaScrip function that will MOVE all of the images that are inside Grid3 equally to Grid1 & Grid2 when screen size is tablet, so we get rid of the third grid making a view of fine two columns. Everything is working great!
Now, the problem is - on Chrome & IE - The function is being fired before its time for some reason that I need your help to help me find it! Please try it your self here: [http://90.195.175.51:93/portfolio.html][2]
Slowly on Chrome or IE - (try it on Firefox as well) - try to re-size the window from large to small, you will notice that BEFORE the top header changes to be a responsive Header (which indicate that you are on a small screen) the images have been sent to Grid1 and Grid 2! but a few px before the time. As on the function it says to fire it on <770.
Hope my question is clear enough for you to help me solve this issue which is stopping me from launching my website. Thanks.
Here is the JavaScrip:
//Gallery Grid System//
var testimonial = $(".testimonial, .galleryItem", "#grid3");
(function () {
$(document).ready(GalleryGrid);
$(window).resize(GalleryGrid);
})(jQuery);
function GalleryGrid() {
var grid3 = $('#grid3');
var width = $(window).width();
if (width < 1030 && width > 770) {
var grid1 = $('#grid1');
var grid2 = $('#grid2');
for (var i = 0; i < testimonial.length; i++) {
if (i < testimonial.length / 2) {
grid1.append(testimonial[i]);
} else {
grid2.append(testimonial[i]);
}
}
} else {
grid3.append(testimonial);
}
}
Note: The following is the whole page with all the functions:
$(document).ready(function () {
//Prevent clicking on .active links
$('.active').click(function (a) {
a.preventDefault();
});
//Allow :active on touch screens
document.addEventListener("touchstart", function () {}, true);
//Hide toolbar by default
window.addEventListener('load', function () {
setTimeout(scrollTo, 0, 0, 0);
}, false);
//Scroll-up button
$(window).scroll(function () {
if ($(this).scrollTop() > 100) {
$('.scrollup').fadeIn();
} else {
$('.scrollup').fadeOut();
}
});
$('.scrollup').click(function () {
$("html, body").animate({
scrollTop: 0
}, 600);
return false;
});
//StickyBox
$(function () {
$.fn.scrollBottom = function () {
return $(document).height() - this.scrollTop() - this.height();
};
var $StickyBox = $('.detailsBox');
var $window = $(window);
$window.bind("scroll resize", function () {
var gap = $window.height() - $StickyBox.height() - 10;
var footer = 288 - $window.scrollBottom();
var scrollTop = $window.scrollTop();
$StickyBox.css({
top: 'auto',
bottom: 'auto'
});
if ($window.width() <= 770) {
return;
$StickyBox.css({
top: '0',
bottom: 'auto'
});
}
if (scrollTop < 50) {
$StickyBox.css({
bottom: "auto"
});
} else if (footer > gap - 100) {
$StickyBox.css({
top: "auto",
bottom: footer + "px"
});
} else {
$StickyBox.css({
top: 80,
bottom: "auto"
});
}
});
});
//Change items location depending on the width of the screen//
$(function () { //Load Ready
function myFunction() {
var insert = $(window).width() <= 770 ? 'insertBefore' : 'insertAfter';
$('#home-sectionB img')[insert]($('#home-sectionB div'));
$('#home-sectionD img')[insert]($('#home-sectionD div'));
}
myFunction(); //For When Load
$(window).resize(myFunction); //For When Resize
});
//Contact Form//
$(".input").addClass('notSelected');
$(".input").focus(function () {
$(this).addClass('selected');
});
$(".input").focusout(function () {
$(this).removeClass('selected');
});
$(document).ready(function () {
GalleryGrid();
$(window).resize(GalleryGrid);
});
//Gallery Grid System//
var testimonial = $(".testimonial, .galleryItem", "#grid3");
(function () {
$(document).ready(GalleryGrid);
$(window).resize(GalleryGrid);
})(jQuery);
function GalleryGrid() {
var grid3 = $('#grid3');
var width = $(window).width();
if (width < 1030 && width > 770) {
var grid1 = $('#grid1');
var grid2 = $('#grid2');
for (var i = 0; i < testimonial.length; i++) {
if (i < testimonial.length / 2) {
grid1.append(testimonial[i]);
} else {
grid2.append(testimonial[i]);
}
}
} else {
grid3.append(testimonial);
}
}
//Testimonials Animation//
$(".testimonial").hover(function () {
$(".testimonial").addClass('testimonialNotActive');
$(this).removeClass('testimonialNotActive').addClass('testimonialActive');
},
function () {
$(".testimonial").removeClass('testimonialNotActive');
$(this).removeClass('testimonialActive');
});
//Portfolio Gallery Filter//
(function () {
var $portfolioGallerySection = $('#portfolio-sectionB'),
$filterbuttons = $('#portfolio-sectionA a');
$filterbuttons.on('click', function () {
var filter = $(this).data('filter');
$filterbuttons.removeClass('portfolio-sectionAClicked');
$(this).addClass('portfolio-sectionAClicked');
$portfolioGallerySection.attr('class', filter);
$('.galleryItem').removeClass('selectedFilter');
$('.galleryItem.' + filter).addClass('selectedFilter');
});
}());
});
Your problem is that CSS media queries and jQuery's $(window).width() do not always align.
function getCSSWidth() {
var e = window, a = 'inner';
if (!('innerWidth' in window )) {
a = 'client';
e = document.documentElement || document.body;
}
return e[ a+'Width' ];
}
Use this instead of $(window).width()
modified from http://andylangton.co.uk/articles/javascript/get-viewport-size-javascript/
I think this could solve your problem (but I'm not quite sure)
//Put that before the document ready event
(function($,sr){
// debouncing function from John Hann
// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
var debounce = function (func, threshold, execAsap) {
var timeout;
return function debounced () {
var obj = this, args = arguments;
function delayed () {
if (!execAsap)
func.apply(obj, args);
timeout = null;
};
if (timeout)
clearTimeout(timeout);
else if (execAsap)
func.apply(obj, args);
timeout = setTimeout(delayed, threshold || 100);
};
}
// smartresize
jQuery.fn[sr] = function(fn){ return fn ? this.bind('resize', debounce(fn)) : this.trigger(sr); };
})(jQuery,'smartresize');
// Here you call GalleryGrid (replace $(window).resize(GalleryGrid) with that):
$(window).smartresize(GalleryGrid);
http://www.paulirish.com/2009/throttled-smartresize-jquery-event-handler/
The reason is your vertical scrollbar. Your content is fixed at width=1030, but when the window size is 1030, the size of the viewport is actually: window size (1030) - vertical scroll bar
Try setting
<body style="overflow:hidden">
You will see that it works correctly when the scrollbar is removed. Or try setting:
<link href="assets/css/tablets-landscape.css" rel="stylesheet" type="text/css" media="screen and (max-width : 1045px)"/>
Set max-width:1045px to make up for scrollbar, you will see that it works correctly.
Your javascript should be like this:
var width = $(window).width() + verticalscrollbarWidth;
I have built a jQuery rotator to rotate through 3 divs and loop them. I would like to add the functionality on mouse over to "freeze" the current div and then start again on mouse out.
I've thought about setting a variable to false at the start of the function and setting it true when it's on it's current frame but I've got my self a bit confused.
I've also tried to use the hover function but when using the in and out handlers, I'm confused as to how to stop, restart the animation.
function ImageRotate() {
var CurrentFeature = "#container" + featureNumber;
$(CurrentFeature).stop(false, true).delay(4500).animate({'top' : '330px'}, 3000);
var featureNumber2 = featureNumber+1;
if ( featureNumber == numberOfFeatures) {featureNumber2 = 1}
var NewFeature = "#container" + featureNumber2;
$(NewFeature).stop(false, true).delay(4500).animate({'top' : '0px'}, 3000);
var featureNumber3 = featureNumber-1;
if ( featureNumber == 1) {featureNumber3 = numberOfFeatures};
var OldFeature = "#container" + featureNumber3;
$(OldFeature).stop(false, true).delay(4500).css('top' , '-330px');
setTimeout('if (featureNumber == numberOfFeatures){featureNumber = 1} else {featureNumber++}; ImageRotate2()', 7500)};
Any help would be greatly appreciated!!
Thanks, Matt
If you were to add this code:
var timerId = null;
function startRotation() {
if (timerId) {
return;
}
timerId = setInterval('if (featureNumber == numberOfFeatures){featureNumber = 1} else {featureNumber++}; ImageRotate2()', 7500);
}
function stopRotation() {
if (!timerId) {
return;
}
clearInterval(timerId);
timerId = null;
}
and replace the last line of your code block with a simple call to startRotation();, then you could call stopRotation and startRotation when the mouse hovers over/leaves your element:
$('your-element-selector').hover(stopRotation, startRotation);
It's not clear what you are trying to do with the three divs without seeing the HTML and more code, so I think a basic example might help you better (demo).
HTML
<div class="test">image: <span></span></div>
Script
$(document).ready(function(){
var indx = 0, loop, numberOfFeatures = 5;
function imageRotate(){
indx++;
if (indx > numberOfFeatures) { indx = 1; }
$('.test span').text(indx);
loop = setTimeout( imageRotate , 1000 );
}
imageRotate();
$('.test').hover(function(){
clearTimeout(loop);
}, function(){
imageRotate();
});
})
changed things up a little bit, here is how I ended up doing it. `
var animRun = false;
var rotateHover = false;
function startRotation() {
rotateHover = false;
ImageRotate();
}
function stopRotation() {
rotateHover = true;
clearTimeout();
}
function ImageRotate() {
if (rotateHover == false){
animRun = true;
var CurrentFeature = "#container" + featureNumber;
$(CurrentFeature).stop(false, true).animate({'top' : '330px'}, featureDuration, function(){animRun = false;});
var featureNumber2 = featureNumber+1;
if ( featureNumber == numberOfFeatures) {featureNumber2 = 1}
var NewFeature = "#container" + featureNumber2;
$(NewFeature).stop(false, true).animate({'top' : '0px'}, featureDuration); /* rotate slide 2 into main frame */
var featureNumber3 = featureNumber-1;
if ( featureNumber == 1) {featureNumber3 = numberOfFeatures};
var OldFeature = "#container" + featureNumber3;
$(OldFeature).stop(false, true).css('top' , '-330px'); /*bring slide 3 to the top*/
//startRotation();
setTimeout('if (featureNumber == numberOfFeatures){featureNumber = 1} else {featureNumber++}; if (rotateHover == false){ImageRotate2()};', featureDelay);
};
};