This function detects whether or not an element is visible on the screen. When a user scrolls to a "load" element I want to automatically request more posts to be displayed (AJAX). However, at the moment I have the following code:
function isScrolledIntoView(elem)
{
var $elem = $(elem);
var $window = $(window);
var docViewTop = $window.scrollTop();
var docViewBottom = docViewTop + $window.height();
var elemTop = $elem.offset().top;
var elemBottom = elemTop + $elem.height();
return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}
var scrolledCounter = 0;
setInterval(function(){
var scroll = isScrolledIntoView(".button.load-more");
if(scroll==true){
scrolledCounter++;
loadMorePosts(scrolledCounter);
}
},500);
It works fine, but if the element is constantly in view (as it would be for slower-ish connections loading the information), it then loads more every 500ms. I'm wondering what method would be better than setInterval (?) to accomplish what I want to do.
i.e.
If the user scrolls to the load element, function loadMorePosts is called just once, then if it's not visible anymore, re-allow the function to be called again, such that if it's visible again the function is called once more again.
You can use $(window).scroll() method. It will occur every time users scrolls the page.
I have added an isScrolling variable to prevent firing loadMorePosts more than once in a period.
var isScrolling = false;
$(window).scroll(function(){
var scroll = isScrolledIntoView(".button.load-more");
if (scroll==true && !isScrolling)
{
isScrolling = true; // Block this function
scrolledCounter++;
loadMorePosts(scrolledCounter);
setTimeout(function() { isScrolling = false; }, 500); // Unblock the function after 0.5s
}
});
JSFiddle DEMO (without isScrolling): http://jsfiddle.net/0wbf9dn2/
Related
Creating a website with a timer that counts up once the div is scrolled into view. The problem I'm having is that scrolling away starts the timer again, and I'd like the final value that the timer reaches to remain until the page is reloaded.
I've tried using 'event.handled' and '$.Callbacks("once")' but neither seem to work.
Any guidance would be appreciated - code below!
$(allInView);
$(window).scroll(allInView);
function isScrolledIntoView(elem) {
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = $(elem).offset().top;
var elemBottom = elemTop + $(elem).height();
return elemBottom <= docViewBottom && elemTop >= docViewTop;
}
function allInView() {
if (isScrolledIntoView($("#column4"))) {
/// when 'column4' IS being 'viewed', the timers then begin
jQuery(function ($) {
$(".timer").countTo({
from: 0,
to: 183,
speed: 1000,
refreshInterval: 50,
onComplete: function (value) {
console.debug(this);
},
});
});
}
}
You could use a variable to check if the timer is already started.
let lockTimer = false;
function allInView () {
// Exits conditions to avoid to restart the timer
if (lockTimer || !isScrolledIntoView(...)) {
return;
}
lockTimer = true; // block timer launch
JQuery(...);
}
I'm trying to check if element crossed bottom edge of viewport. If it did, I want to add class start to this element. The problem is that when condition is satisfied class adds to all h2 elements.
Here is my code:
$.fn.checkAnimation = function() {
var context = this;
function isElementInViewport(elem) {
var $elem = context;
// Get the scroll position of the page.
var viewportTop = $(window).scrollTop();
var viewportBottom = viewportTop + $(window).height();
// Get the position of the element on the page.
var elemTop = Math.round( $elem.offset().top );
var elemBottom = elemTop + $elem.height();
return (elemTop < viewportBottom);
}
// Check if it's time to start the animation.
function checkAnimation() {
console.log(isElementInViewport($elem));
var $elem = context;
// If the animation has already been started
if ($elem.hasClass('start')) return;
if (isElementInViewport($elem)) {
// Start the animation
context.addClass('start');
}
}
checkAnimation();
return this;
};
$(window).on('scroll scrollstart touchmove orientationchange resize', function(){
$('h2').checkAnimation();
});
You'll need to change your checkAnimation jQuery plugin to loop through all elements in the jQuery object and process them individually or call your function like this
$('h2').each(function(){
$(this).checkAnimation();
}
Here is what I mean by processing the elements individually inside the plugin:
$.fn.checkAnimation = function() {
function isElementInViewport($elem) {
var viewportTop = $(window).scrollTop();
var viewportBottom = viewportTop + $(window).height();
var elemTop = Math.round( $elem.offset().top );
var elemBottom = elemTop + $elem.height();
return (elemTop < viewportBottom);
}
function checkAnimation() {
var $elem = $(this);
if ($elem.hasClass('start')) return;
if (isElementInViewport($elem)) {
$elem.addClass('start');
}
}
return this.each(checkAnimation);
};
If you use this version of the plugin you can call it like this:
$('h2').checkAnimation();
It will add the class only to the element that matches the condition not to all the element in the jQuery object you've called the function on.
Should be $elem.addClass('start'); instead and remove the var $elem = context; statement like :
function checkAnimation() {
console.log(isElementInViewport($elem));
// If the animation has already been started
if ($elem.hasClass('start')) return;
if (isElementInViewport($elem)) {
// Start the animation
$elem.addClass('start');
}
}
Hope this helps.
this inside a jQuery plugin is the jQuery object that contains the whole collection of elements represented by the previous selector/filter.
In order to treat each element in the collection as an individual instance you need to loop through the initial this.
Very basic pattern:
$.fn.pluginName = function(options){
// return original collection as jQuery to allow chaining
// loop over collection to access individual elements
return this.each(function(i, elem){
// do something with each element instance
$(elem).doSomething(); // elem === this also
});
}
I have the follwing code which animates some divs thru adding an animation class to them when they come into browser window.
My only problem comes with the divs that are "already" visible in the screen after page load, once I do a little scroll they animate.
What is the best way to exclude the divs that are "already" visible in the browser window after page load?
function isElementInViewport(elem) {
var $elem = $(elem);
// Get the scroll position of the page.
var scrollElem = ((navigator.userAgent.toLowerCase().indexOf('webkit') != -1) ? 'body' : 'html');
var viewportTop = $(scrollElem).scrollTop();
var viewportBottom = viewportTop + $(window).height();
// Get the position of the element on the page.
var elemTop = Math.round( $elem.offset().top );
var elemBottom = elemTop + $elem.height();
return ((elemTop < viewportBottom) && (elemBottom > viewportTop));
}
function checkAnimation_aec() {
var $elem = $('.aec');
// If the animation has already been started
if ($elem.hasClass('icon_start')) return;
if (isElementInViewport($elem)) {
// Start the animation
$elem.addClass('icon_start');
}
}
// Capture scroll events & run the functions
$(document).ready(function(){
$(window).scroll(function(){
checkAnimation_aec();
});
});
Thank you in advance :)
function isElementInViewport(elem) {
var $elem = $(elem);
// Get the scroll position of the page.
var scrollElem = ((navigator.userAgent.toLowerCase().indexOf('webkit') != -1) ? 'body' : 'html');
var viewportTop = $(scrollElem).scrollTop();
var viewportBottom = viewportTop + $(window).height();
// Get the position of the element on the page.
var elemTop = Math.round( $elem.offset().top );
var elemBottom = elemTop + $elem.height();
return ((elemTop < viewportBottom) && (elemBottom > viewportTop));
}
function checkAnimation_aec() {
var $elem = $('.aec');
// If the animation has already been started
if ($elem.hasClass('icon_start')) return;
if (isElementInViewport($elem)) {
// Start the animation
$elem.addClass('icon_start');
}
}
// Capture scroll events & run the functions
$(document).ready(function(){
//after document is ready, run the animation element is visible.
checkAnimation_aec();
$(window).scroll(function(){
checkAnimation_aec();
});
});
So I've had some assistance to create a javascript element which spins from "0" upto "190" however, this is triggered when the element is in view - so I thought - it turns out the number is ONLY increased when you scroll.... For example:
Script:
var totalShipped = 190;
var shippedDisplay = 0;
var shippedStep = totalShipped / (2 * 1000 / 100); // Animation duration 2 sec
$(allInView);
$(window).scroll(allInView);
function isScrolledIntoView(elem) {
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = $(elem).offset().top;
var elemBottom = elemTop + $(elem).height();
return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop));
}
function allInView() {
if (shippedDisplay > totalShipped)
shippedDisplay = totalShipped;
document.getElementById("shipped").innerHTML = Math.round(shippedDisplay);
if (shippedDisplay < totalShipped) {
shippedDisplay += shippedStep;
setTimeout(animateShipped, 100);
}
}
animateShipped();
HTML:
<span id="shipped"></span>
What I want is for the element to automatically spin from 0 - 190 ONLY when the element is in the view of the browser and NOT actioned by scrolling....
As it is right now:
https://gyazo.com/6cd38177c44e97f0fa4f2b4d05ece5c3
Ideally I want all the number to be spinning, but let's focus on the "190" for now, when in view of the browser - any tips or suggestions would GREATLY be appreciated.
What I would suggest is set a timer on page load and use this Jquery Visible plugin to check if your element is in view before incrementing.
Hope that helps.
Assuming your isScrolledIntoView(elem) function is working you can do something like this:
window.setInterval(function(){
if(isScrolledIntoView($('#shipped'))){
allInView();
}
}, 100);
What it will do is checking every 100ms if your span is visible and then calling your allInView() function which then will animate the spinning.
If you do not want any overhead if the element is not visible you can deactivate the interval accordingly:
var interval = null;
function checkForAnimation(){
if(isScrolledIntoView($('#shipped'))){
interval = window.setInterval(function(){
allInView();
}, 100);
}
else{
clearInterval(interval);
}
}
$(window).scroll(checkForAnimation);
You can use setInterval(allInView(), 2);
I have a one page site with fixed navigation and using a scroll script, very similar to this: http://www.ivanjevremovic.in.rs/live/temptation/single/orange/index-cycle-slider.html
What I'm looking for is a way to check what section is viewable in the window to set the active state on the nav when using the browsers scroll bar, any ideas?
Here are all the variables you'll need...
var $myElt = $('.myElement'); // whatever element you want to check
var $window = $(window); // the window jQuery element
var myTop = $myElt.offset().top; // the top (y) location of your element
var windowTop = $window.scrollTop(); // the top of the window
var windowBottom = windowTop + $window.height(); // the bottom of the window
Then to make sure your element is within the window's range...
if (myTop > windowTop && myTop < windowBottom) {
// element is in the window
} else {
// element is NOT in the window
// maybe use this to scroll...
// $('html, body').animate({scrollTop: myTop}, 300);
}
jQuery reference:
http://api.jquery.com/offset/
http://api.jquery.com/height/
http://api.jquery.com/scrollTop/
Use $('#element').offset().top; to detect element top side.
$(window).scrollTop(); to detect current scroll position.
And $(window).height(); to detect current window height.
And after that steps you actually need only something easy math calculations.
function isScrolledIntoView(elem)
{
var docViewTop = $(window).scrollTop();
var docViewBottom = docViewTop + $(window).height();
var elemTop = $(elem).offset().top;
var elemBottom = elemTop + $(elem).height();
return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom));
}
source: Check if element is visible after scrolling
see the following lazyload plugin:
http://plugins.jquery.com/files/jquery.lazyload.js__6.txt
the section which starts with the comment "return the status of the item relative to the current view" checks to see if an element is visible in the viewport.
If you are using jQuery just try to check the document position
$('html').position().top;
for example:
$(document).bind("scroll", checkLink);
function checkLink(){
/* Position will checked out after 1 sec when user finish scrolling */
var s = setTimeout(function(){
var docHeight = $('html').position().top;
var allLinks = $('.navigation a');
if ( docHeight < 0 && docHeight <= -1000 ) {
allLinks.removeClass('active');
$('a.firstlink').addClass('active');
} else
if ( docHeight < -1000 && docHeight <= -2000 ) {
allLinks.removeClass('active');
$('a.secondlink').addClass('active');
} else { /* .... */ }
$(document).bind("scroll", checkLink);
}, 1000);
$(document).unbind('scroll');
}
but guys in your example haven't held on this for a long time :) they just toggle classes on click
$('#navigation').localScroll();
$('#navigation li a').click( function () {
$('#navigation li a').removeClass("active");
$(this).addClass("active");
});
2022 answer - you don't have to use jQuery anymore for this
Now it is possible to use plain javascript with IntersectionObserver.
The problem with the other answers are that they fire off too many times.
For example you could to this:
var observer = new IntersectionObserver(function(entries) {
if(entries[0].isIntersecting === true) {
console.log('Element is in the window');
} else {
console.log("Element is not in the window");
}
});
observer.observe(document.querySelector(".myObject"));