Sequentially fade in divs on coming into view with jQuery - javascript

I'm currently using jquery.inview to detect when certain elements are fully visible in the browser. I have this working correctly like so:
$('.exclusive').bind('inview',function(e, isInView, visiblePartX, visiblePartY) {
var elem = $(this);
if (elem.data('inviewtimer')) {
clearTimeout(elem.data('inviewtimer'));
elem.removeData('inviewtimer');
}
if (isInView) {
elem.data('inviewtimer', setTimeout(function() {
if (visiblePartY == 'top') {
elem.data('seenTop', true);
} else if (visiblePartY == 'bottom') {
elem.data('seenBottom', true);
} else {
elem.data('seenTop', true);
elem.data('seenBottom', true);
}
if (elem.data('seenTop') && elem.data('seenBottom')) {
elem.animate({ 'opacity' : 1}, 1000)
elem.unbind('inview');
}
}, 1000))
}
});
However, I want to amend this code slightly, so that when there are multiple matched elements in view, these are faded in sequentially with a slight delay between each. And of course when the user moves the viewport to bring more elements into view it will continue to do the same. Can anyone point me in the right direction?
Thanks,
Chris

You can delay fading in of particular elements like this:
var divs = $('div'); // replace with your selector
$.each(divs, function(i, item) {
setTimeout(function() {
$(item).fadeIn(1000);
}, 1000 * i);
});​
Check the live DEMO.

Related

Scroll to previous div

based on this thread I added a scroll up to next div, like this:
var f = jQuery('.p');
var nxt = f;
jQuery(".next").click(function() {
if (nxt.next('.scroller').length > 0) {
nxt = nxt.next('.scroller');
} else {
nxt = f;
}
jQuery('html,body').animate({
scrollTop: nxt.offset().top
},
'slow');
});
var f = jQuery('.p');
var prev = f;
jQuery(".previous").click(function() {
if (prev.prev('.scroller').length > 0) {
prev = prev.prev('.scroller');
} else {
prev = f;
}
jQuery('html,body').animate({
scrollTop: prev.offset().top
},
'slow');
});
So this scrolls up and down very nicely.
The problem though, is that when the user scrolls, the script doesn't notice it. That is, the user scrolls from div1 to div4, when the user click on my "next"-button, he or she gets scrolled to div2. How can I solve this?
I checked into this but I cannot combine it with the above. There must be an easier way, right?
Any help much appreciated!
Oh... I think I might have solved it myself like this:
var jQuerycurrentElement = jQuery(".scroller").first();
jQuery(".next").click(function () {
var jQuerynextElement = jQuerycurrentElement.next(".scroller");
// Check if next element actually exists
if(jQuerynextElement.length) {
// If yes, update:
// 1. $currentElement
// 2. Scroll position
jQuerycurrentElement = jQuerynextElement;
jQuery('html, body').stop(true).animate({
scrollTop: jQuerynextElement.offset().top
}, 100);
}
return false;
});
jQuery(".previous").click(function () {
var jQueryprevElement = jQuerycurrentElement.prev(".scroller");
// Check if previous element actually exists
if(jQueryprevElement.length) {
// If yes, update:
// 1. $currentElement
// 2. Scroll position
jQuerycurrentElement = jQueryprevElement;
jQuery('html, body').stop(true).animate({
scrollTop: jQueryprevElement.offset().top
}, 100);
}
return false;
});
The above is based on this.
The only problem here is that when parts of a div is scrolled into view, the next and previous buttons sometimes behave strange. For example, when being between div2 and div3 and div 3 is most visible, the previous click can take the user back to div1, which feels not so logical. Can we adjust this somehow? I suppose I would have to do something with the offset but I am unsure.

Accordion Functionality

I've put together an accordion script that works quite nicely (haven't cross-browser tested) and allows for lots of content inside each drawer to be accessed and visible on screen. A lot of times accordions open and cause issues with positioning after opening. Anyway, the code I'm using has a toggle active function and a scroll function being called on click.
function toggleActive(link){ // Set anchor to active
if ( $(link).hasClass("active") ) {
$(link).removeClass("active");
} else {
$(link).addClass("active");
};
};
function scrollToElement(selector, time, verticalOffset) { // param 1 = id, param 2 = speed
time = typeof(time) != 'undefined' ? time : 1000;
verticalOffset = typeof(verticalOffset) != 'undefined' ? verticalOffset : 0;
element = $(selector);
offset = element.offset();
offsetTop = offset.top + verticalOffset;
$('html, body').animate({scrollTop: offsetTop }, time);
}
$('#accordion a').click(function(e) {
var link = '#' + event.target.id
$(".tab-content").slideUp();
$(".tab").removeClass("active");
toggleActive(link);
$(link).next().slideToggle("fast");
setTimeout(function() {
scrollToElement($(link), 500);
}, 500);
e.preventDefault();
});
So when clicked, all of the tabs are closed and made inactive, then the targeted "drawer" is opened and made active. If for any reason you click an already "active" drawer, it runs through the script again. What I'd like to do is place an IF statement that determines if what you just clicked is already open, and then simply close that drawer. Thanks in advance. I don't know why this is causing me headaches.
JSFiddle
As I understand you need another function as below:
function isAlreadyActive(link)
{
if ( $(link).hasClass("active") ) {
$(link).removeClass("active");
return true;
} else {
return false;
};
}
And you should call that function in your click event. This function will check if the link already active, if so just deactivates it and changes as you want.
$('#accordion a').click(function(e) {
var link = '#' + event.target.id
/* if it is already active, just deactivate it and exit*/
if(isAlreadyActive(link)){
return false;
}
$(".tab-content").slideUp();
$(".tab").removeClass("active");
toggleActive(link);
$(link).next().slideToggle("fast");
setTimeout(function() {
scrollToElement($(link), 500);
}, 500);
e.preventDefault();
});
I hope this helps.

Kendo UI Tooltip Will Sometimes Remain Visible When it should Disappear

I have been playing around with this for quite some time, and I do not know what is wrong. When I have a few links in a row, and keep fluttering my mouse cursor over them quickly every so often a tooltip will remain visible when it should go away (it is visible even after the cursor is no longer on the link).
I believe my code is logically valid, can someone else see if they know why a tooltip here and there would remain visible?
For a link of this type:
Link
Here is the code:
function tooltip(e) {
var ticketType = j$(e).data("ticket-type");
var ticketID = j$(e).data("ticket-id");
j$.post("/Some/Url/", { "ticketID":ticketID, "ticketType":ticketType },
function(r) {
var title = r["tt"];
var tooltip = j$(e).kendoTooltip( { content: title, position: "top" } ).data("kendoTooltip");
}).always(function() {
if (j$(e).is(":hover")) { j$(e).data("kendoTooltip").show(); }
else { j$(e).data("kendoTooltip").hide(); }
});
j$(e).hover(function() {},
// Handler for when the pointer is leaving an element
function(e) {
if (j$(e.target).data("kendoTooltip") != undefined) {
j$(e.target).data("kendoTooltip").hide();
.log(e.target.innerHTML + ": was hidden.");
}
}
);
}
I think the problem is that sometimes you mouseout before ajax post returns, therefore the tooltip is shown after you leave a link. As well as hiding on mouseout, how about setting a data attribute on the target link so that the AJAX return can check the attribute before showing the tooltip:
function tooltip(e) {
j$(e).data("hover", "true"); //turn on hover data-attribute
var ticketType = j$(e).data("ticket-type");
var ticketID = j$(e).data("ticket-id");
j$.post("/Some/Url/", { "ticketID":ticketID, "ticketType":ticketType },
function(r) {
var title = r["tt"];
var tooltip = j$(e).kendoTooltip( { content: title, position: "top" } ).data("kendoTooltip");
}).always(function() {
if (j$(e).data("hover") == "true") { j$(e).data("kendoTooltip").show(); }
else { j$(e).data("kendoTooltip").hide(); }
});
j$(e).hover(function() {},
// Handler for when the pointer is leaving an element
function(e) {
j$(e).data("hover", "false"); //turn offhover data-attribute
if (j$(e.target).data("kendoTooltip") != undefined) {
j$(e.target).data("kendoTooltip").hide();
.log(e.target.innerHTML + ": was hidden.");
}
}
);
}
DEMO
NOTE: demo uses a setTimeout to fake an ajax call

jquery .hover() with else if statement

I want to put a little delay for onmouseout event for a group of sub items in a drop down menu. But I don't want to use css transitions.
I set it with .hover() and setTimeout method but I wanted to put it only for a specific elements in menu - in this case just for sub items so I used if else statement for them. I have no idea why this if else statement does't work.
Here is my javascript code:
var selectors =
{
element: '.main-menu li:has(ul)'
}
var opacityWorkaround = function ($element, value) {
$element.css('opacity', value);
};
var getAnimationValues = function (visible) {
var result = {
visibility: visible
};
result.opacity = visible === 'visible' ? 1 : 0;
};
var mouseActionHandler = function ($element, visible, opacityValue) {
$element
.stop()
.css("visibility", 'visible')
.animate(getAnimationValues(visible),
3000,
function () {
$(this).css("visibility", visible);
opacityWorkaround($(this), opacityValue);
});
};
var onMouseIn = function () {
var $submenu = $(this).children("ul:first");
if ($submenu) {
mouseActionHandler($submenu, 'visible', 1);
}
};
var onMouseOut = function () {
var $submenu = $(this).children("ul:first");
var $global = $('.global').children('ul');
if ($submenu) {
mouseActionHandler($submenu, 'hidden', 0);
} else if ($global) {
setTimeout(function() {
mouseActionHandler($global, 'hidden', 0);
},1500);
}
};
$(selectors.element).hover(onMouseIn, onMouseOut);
I put 1500ms delay and the $global variable is referring to sub items in menu that I want to make disapear with that delay. I wanted to achieve this when user move mouse cursor out of 'some items >' tab.
Here is my fiddle example.
http://jsfiddle.net/PNz9F/1/
Thanks in advance for any help!
In the example you have in your question $submenu always has a value so the else if statement is never run. You can check for a class instead.
var timeout;
var $submenu = $(this).children("ul:first");
var $global = $('.global').children('ul');
if ($(this).hasClass('menu-item')) {
mouseActionHandler($submenu, 'hidden', 0);
mouseActionHandler($global, 'hidden', 0);
clearTimeout(timeout);
} else if ($(this).hasClass('global')) {
timeout = setTimeout(function() {
mouseActionHandler($global, 'hidden', 0);
},1500);
}
you should be able to just use the :hover selector in your code to check whether the user is hovering over the element or not and run code accordingly

Trying to get my slideshow plugin to infinitely loop (by going back to the first state)

I wrote a slideshow plugin, but for some reason maybe because I've been working on it all day, I can't figure out exactly how to get it to go back to state one, once it's reached the very last state when it's on auto mode.
I'm thinking it's an architectual issue at this point, because basically I'm attaching the amount to scroll left to (negatively) for each panel (a panel contains 4 images which is what is currently shown to the user). The first tab should get: 0, the second 680, the third, 1360, etc. This is just done by calculating the width of the 4 images plus the padding.
I have it on a setTimeout(function(){}) currently to automatically move it which works pretty well (unless you also click tabs, but that's another issue). I just want to make it so when it's at the last state (numTabs - 1), to animate and move its state back to the first one.
Code:
(function($) {
var methods = {
init: function(options) {
var settings = $.extend({
'speed': '1000',
'interval': '1000',
'auto': 'on'
}, options);
return this.each(function() {
var $wrapper = $(this);
var $sliderContainer = $wrapper.find('.js-slider-container');
$sliderContainer.hide().fadeIn();
var $tabs = $wrapper.find('.js-slider-tabs li a');
var numTabs = $tabs.size();
var innerWidth = $wrapper.find('.js-slider-container').width();
var $elements = $wrapper.find('.js-slider-container a');
var $firstElement = $elements.first();
var containerHeight = $firstElement.height();
$sliderContainer.height(containerHeight);
// Loop through each list element in `.js-slider-tabs` and add the
// distance to move for each "panel". A panel in this example is 4 images
$tabs.each(function(i) {
// Set amount to scroll for each tab
if (i === 1) {
$(this).attr('data-to-move', innerWidth + 20); // 20 is the padding between elements
} else {
$(this).attr('data-to-move', innerWidth * (i) + (i * 20));
}
});
// If they hovered on the panel, add paused to the data attribute
$('.js-slider-container').hover(function() {
$sliderContainer.attr('data-paused', true);
}, function() {
$sliderContainer.attr('data-paused', false);
});
// Start the auto slide
if (settings.auto === 'on') {
methods.auto($tabs, settings, $sliderContainer);
}
$tabs.click(function() {
var $tab = $(this);
var $panelNum = $(this).attr('data-slider-panel');
var $amountToMove = $(this).attr('data-to-move');
// Remove the active class of the `li` if it contains it
$tabs.each(function() {
var $tab = $(this);
if ($tab.parent().hasClass('active')) {
$tab.parent().removeClass('active');
}
});
// Add active state to current tab
$tab.parent().addClass('active');
// Animate to panel position
methods.animate($amountToMove, settings);
return false;
});
});
},
auto: function($tabs, settings, $sliderContainer) {
$tabs.each(function(i) {
var $amountToMove = $(this).attr('data-to-move');
setTimeout(function() {
methods.animate($amountToMove, settings, i, $sliderContainer);
}, i * settings.interval);
});
},
animate: function($amountToMove, settings, i, $sliderContainer) {
// Animate
$('.js-slider-tabs li').eq(i - 1).removeClass('active');
$('.js-slider-tabs li').eq(i).addClass('active');
$('#js-to-move').animate({
'left': -$amountToMove
}, settings.speed, 'linear', function() {});
}
};
$.fn.slider = function(method) {
if (methods[method]) {
return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof method === 'object' || !method) {
return methods.init.apply(this, arguments);
} else {
return false;
}
};
})(jQuery);
$(window).ready(function() {
$('.js-slider').slider({
'speed': '10000',
'interval': '10000',
'auto': 'on'
});
});​
The auto and animate methods are where the magic happens. The parameters speed is how fast it's animated and interval is how often, currently set at 10 seconds.
Can anyone help me figure out how to get this to "infinitely loop", if you will?
Here is a JSFiddle
It would probably be better to let go of the .each() and setTimeout() combo and use just setInterval() instead. Using .each() naturally limits your loop to the length of your collection, so it's better to use a looping mechanism that's not, and that you can break at any point you choose.
Besides, you can readily identify the current visible element by just checking for .active, from what I can see.
You'd probably need something like this:
setInterval(function () {
// do this check here.
// it saves you a function call and having to pass in $sliderContainer
if ($sliderContainer.attr('data-paused') === 'true') { return; }
// you really need to just pass in the settings object.
// the current element you can identify (as mentioned),
// and $amountToMove is derivable from that.
methods.animate(settings);
}, i * settings.interval);
// ...
// cache your slider tabs outside of the function
// and just form a closure on that to speed up your manips
var slidertabs = $('.js-slider-tabs');
animate : function (settings) {
// identify the current tab
var current = slidertabs.find('li.active'),
// and then do some magic to determine the next element in the loop
next = current.next().length >= 0 ?
current.next() :
slidertabs.find('li:eq(0)')
;
current.removeClass('active');
next.addClass('active');
// do your stuff
};
The code is not optimized, but I hope you see where I'm getting at here.

Categories