Infinite scrollbar in Javascript - javascript

I am writing a book viewer that dynamically loads content as the user scrolls down to it. The first problem I'm having is how to set up the scrollbar. Since we only store paragraphs in our database with no size information, I can only guess how long the document is. Based upon such a guess, I may want to create a scrollable window that appears 10,000 pixels long and loads paragraphs as needed.
The simplest way I can think is to actually create a DIV that is 10,000 pixels long and absolutely position an embedded div at the scroll position showing my content.
Is there a way to totally control the scrollbar under Javascript (setting its min, max, position, relative thumb size) or do I go the simple way mentioned above or is there another way to do this?
I am using ExtJS framework but it does not seem to offer any direct help there, though V4 does include an infinite grid.

The other answers to this question simply extended the content as the user reached the bottom, this is not what I was after. I need to display a fully sized, but sparsely populated, scrollable region. The solution I came up with was pretty simple:
I created a scrollable DIV (call it book) with a set of inner DIVs for each paragraph in the book. I need to do it by paragraph as this has special meaning in our application, otherwise, you'd probably do it by page. The paragraph DIVs have a default size based upon a guess of how big they are.
When the book DIV is scrolled, the javascript calculates which paragraph DIVs are now visible. Those that are visible but are not populated are bundled together. A web service is then used to populate the required paragraph DIVs and accurately size them.
The algorithm I use bundles some surrounding (not not-visible) paragraphs to give some read ahead and chunking efficiencies. A lazy loader reads further paragraphs once the screen has been updated to further aid smooth scrolling.
This turned out too be very simple. The hard work is in the chunking algorithms and adding a lazy reader.

Here is a number of ways:
Masonry Infinite Scroll http://desandro.com/demo/masonry/docs/infinite-scroll.html
Cpde Sample:
$wall.infinitescroll({
navSelector : '#page_nav', // selector for the paged navigation
nextSelector : '#page_nav a', // selector for the NEXT link (to page 2)
itemSelector : '.box', // selector for all items you'll retrieve
loadingImg : 'img/loader.gif',
donetext : 'No more pages to load.',
debug: false,
errorCallback: function() {
// fade out the error message after 2 seconds
$('#infscr-loading').animate({opacity: .8},2000).fadeOut('normal');
}
},
// call masonry as a callback
function( newElements ) {
$(this).masonry({ appendedContent: $( newElements ) });
}
);
AJAXian Way (No Plugin) http://ajaxian.com/archives/implementing-infinite-scrolling-with-jquery
Code:
//Scroll Detection
$(window).scroll(function(){
if ($(window).scrollTop() == $(document).height() - $(window).height()){
lastPostFunc();
}
});
//Loading More content
function lastPostFunc()
{
$(’div#lastPostsLoader’).html(’<img src="bigLoader.gif"/>’);
$.post("scroll.asp?action=getLastPosts&lastID=" + $(".wrdLatest:last").attr("id"),
function(data){
if (data != "") {
$(".wrdLatest:last").after(data);
}
$(’div#lastPostsLoader’).empty();
});
};
Infinite Scroll jQuery Plugin (Originally WordPress Plugin) http://www.infinite-scroll.com/infinite-scroll-jquery-plugin/
Sample Code:
// infinitescroll() is called on the element that surrounds
// the items you will be loading more of
$('#content').infinitescroll({
navSelector : "div.navigation",
// selector for the paged navigation (it will be hidden)
nextSelector : "div.navigation a:first",
// selector for the NEXT link (to page 2)
itemSelector : "#content div.post"
// selector for all items you'll retrieve
});
All options
// usage:
// $(elem).infinitescroll(options,[callback]);
// infinitescroll() is called on the element that surrounds
// the items you will be loading more of
$('#content').infinitescroll({
navSelector : "div.navigation",
// selector for the paged navigation (it will be hidden)
nextSelector : "div.navigation a:first",
// selector for the NEXT link (to page 2)
itemSelector : "#content div.post",
// selector for all items you'll retrieve
debug : true,
// enable debug messaging ( to console.log )
loadingImg : "/img/loading.gif",
// loading image.
// default: "http://www.infinite-scroll.com/loading.gif"
loadingText : "Loading new posts...",
// text accompanying loading image
// default: "<em>Loading the next set of posts...</em>"
animate : true,
// boolean, if the page will do an animated scroll when new content loads
// default: false
extraScrollPx: 50,
// number of additonal pixels that the page will scroll
// (in addition to the height of the loading div)
// animate must be true for this to matter
// default: 150
donetext : "I think we've hit the end, Jim" ,
// text displayed when all items have been retrieved
// default: "<em>Congratulations, you've reached the end of the internet.</em>"
bufferPx : 40,
// increase this number if you want infscroll to fire quicker
// (a high number means a user will not see the loading message)
// new in 1.2
// default: 40
errorCallback: function(){},
// called when a requested page 404's or when there is no more content
// new in 1.2
localMode : true
// enable an overflow:auto box to have the same functionality
// demo: http://paulirish.com/demo/infscr
// instead of watching the entire window scrolling the element this plugin
// was called on will be watched
// new in 1.2
// default: false
},function(arrayOfNewElems){
// optional callback when new content is successfully loaded in.
// keyword `this` will refer to the new DOM content that was just added.
// as of 1.5, `this` matches the element you called the plugin on (e.g. #content)
// all the new elements that were found are passed in as an array
});
// load all post divs from page 2 into an off-DOM div
$('<div/>').load('/page/2/ #content div.post',function(){
$(this).appendTo('#content'); // once they're loaded, append them to our content area
});
Load Content While Scrolling With jQuery (Another No PLugin Sample) http://www.webresourcesdepot.com/load-content-while-scrolling-with-jquery/
Code:
function lastPostFunc()
{
$('div#lastPostsLoader').html('<img src="bigLoader.gif">');
$.post("scroll.asp?action=getLastPosts&lastID=" + $(".wrdLatest:last").attr("id"),
function(data){
if (data != "") {
$(".wrdLatest:last").after(data);
}
$('div#lastPostsLoader').empty();
});
};
$(window).scroll(function(){
if ($(window).scrollTop() == $(document).height() - $(window).height()){
lastPostFunc();
}
});

Try this infinite scroller I found on Smashing Magazine this morning:
http://markholton.com/posts/17-infiniscroll-jquery-plugin-released

Related

Destroying InfiniteScroll on AJAX page change

We have a website that is running jQuery Infinite Scroll Plugin. The plugin is no longer maintained but it is the only one that really serves our purpose properly. However, the problem I have is that our site is ajax based. On page change the pg-changed trigger is fired against the window, which allows us to check if the Infinite Scroll container is there and enable Infinite Scroll. If the Infinite Scroll container isn't there but $.infscr exists, we will attempt to destroy the previous instance.
The problem I am having is that when changing to another page, it doesn't seem to be getting destroyed properly and sometimes AJAX calls will be made, along with the infscr loading bar displaying. Here is the code I am using to instantiate and destroy the plugin:
$(window).on('pg-changed', function () {
// delete our infinite scroll
if(typeof $.infscr !== 'undefined') {
$('.snap-inner, .infscr').data('infinitescroll', null);
$('.snap-inner, .infscr').infinitescroll('unbind');
$('.snap-inner, .infscr').infinitescroll('destroy');
$('#infscr-loading').remove();
$.infscr.data('infinitescroll', null);
$.infscr.infinitescroll('unbind');
$.infscr.infinitescroll('destroy');
delete $.infscr;
}
// setup our infinite scroll
if($('.infscr').length) {
$.infscr = $('.infscr').infinitescroll({
// define our navigation selectors
navSelector : 'div.infscr-navigation',
nextSelector : 'div.infscr-navigation A:first',
itemSelector : '.infscr-item',
// allow scrolling an overflowed element
behavior : 'local',
bufferPx : 120,
binder : $('.snap-inner'),
dataType : 'html',
loading : {
msg : null,
selector : '.snap-content',
img : 'data:image/gif;base64,TRIMMED',
msgText : '<span class="infscr-loading">Loading...</span>',
}
}, function (arrayOfNewElems) {
// render background images on our new elements
$(this).renderBgImages();
});
}
});
I really hope you can help with this as it has become quite a problem now, firing on scroll, making AJAX calls and displaying the loading bar.
I guess the problem might be that the plugin binds events on elements external to itself. It's hard to say without further debugging (is the plugin deleted or does it fail to do so?). If it does, why would it fail to unbind the events? etc.
When you say, 'on another page', I suppose you are not loading a whole new page (which would 'reset' the javascript.
Though, depending on wether this could work for you, you might want to try unbinding ALL events on the document, and starting fresh.
$(document).add('*').off();
should do so. If it does, you could try to pinpoint what elements/events you need to unbind manually. Let me know if this works or not.

How can I enable/disable(in real time) javascript plugins on a webpage?

I need to enable/disable .js plugins in real time. When user re-sizes his browser window, desktop version becomes mobile. The thing is I need to disable some javascript (fullpage.js) and add some css.
And there is no problems if you reload the page or use full-screen window on your pc, from start to the end. The question is : how can I disable fullpage.js on width < 800 ?
I've tried this things :
Added this function to body onresize event (no result without reloading) :
function checkWidth() {
if($(window).width() <= 760 ){
$('#menu-header-button').click(function(){
$('#navigation').toggleClass('navbar-v');
$('#logo-mobile').toggleClass('visible');
$('#menu-header-button').addClass('disabled');
setTimeout(function(){
$('#menu-header-button').removeClass('disabled');
}, 500);
});
} else if($(window).width() > 760 ){
$('#fullpage').fullpage({
menu: '#menu',
anchors: ['firstPage', 'secondPage', '3rdPage', '4thPage'],
css3: true,
afterLoad: function(anchorLink, index){
if( anchorLink == 'firstPage'){
$('#navigation').removeClass('navbar-v');
}
},
onLeave: function(index, nextIndex, direction){
setTimeout(function(){
$('#navigation').addClass('navbar-v');
}, 500);
}
});
}
}
This was working only sometimes :
$(window).onresize= checkWidth();
function checkWidth() {
if($(window).width() == 761 ){
location.reload(true);
}
//...
}
This wasn't working at all :
$(window).onresize= checkWidth();
function delayCheckWidth(){
setTimeout(function(){
checkWidth();
}, 50); //I thought some delay time can be useful here
}
function checkWidth() {
if($(window).width() == 761 ){
location.reload(true);
}
//...
}
Related themes i checked out :
Throttling
How to disable / Enable plugins?
Enable / Disable JavaScript code
How to disable / Enable plugins?
Enable and disable jquery knob dynamically
Disable and enable jQuery context menu
Lots of others.
Nothing about real time or nothing interesting/helpful .
Do you have any advises? Maybe i don't need to go this exactly this way ?
Have you tried the responsive option of fullPage.js?
Not sure if that's what you want or your really need to destroy fullPage.js completely.
From fullPage.js documentation:
responsive: (default 0) A normal scroll (autoScrolling:false) will be used under the defined width in pixels. A class fp-responsive is added to the plugin's container in case the user wants to use it for his own responsive CSS. For example, if set to 900, whenever the browser's width is less than 900 the plugin will scroll like a normal site.
The normal scrolling in fullPage.js will keep sections heightas well as the events assigned to the menu, or the control arrows for the horizontal sliders, but it will scroll normally as any website. You can see a demo of that mode here.
In any case, if you really want to destroy fullPage.js for any reason, you would need to use the provided method for it.
From the docs:
$.fn.fullpage.destroy(type)
Destroys the plugin events and optinally its HTML markup and styles. Ideal to use when using AJAX to load content. ()
type: can be 'empty' or 'all'. If all is passed, the HTML markup and styles used by fullpage.js will be removed. This way the original HTML markup, the one used before any plugin modification is made, will be maintained.
//destroy any plugin event (scrolls, hashchange in the URL...)
$.fn.fullpage.destroy();
//destroy any plugin event and any plugin modification done over your original HTML markup.
$.fn.fullpage.destroy('all');

Destroy Cycle2 on window resize

I am using http://jquery.malsup.com/cycle2/api/ and I am trying to destroy cycle2 slider on window resize event when I detect mobile device. Unfortunatly it returns the following two errors:
[cycle2] slideshow must be initialized before sending commands; "destroy" ignored
[cycle2] slideshow must be initialized before sending commands; "reinit" ignored
Maybe someone could help, what am I doing wrong? Here is the code:
$(function() {
var slider = $('.slider').cycle();
condition = true;
//destroy onload under condition
if(condition){
slider.cycle('destroy');
}
//destroy on resize
$(window).on('resize',function() {
condition = true; //Will be function to recondition let´s say it's true by now
if(condition){
slider.cycle('destroy');
} else {
slider.cycle('reinit');
}
});
});
Thank you.
I know this is an old question, but I was trying to figure this out too, and after careful reading of the docs, this is what I came up with.
So I use the data attributes to set my options on my slideshow. I really like this feature.
For simplicity sake, here is my opening cycle2 div
<div data-cycle-carousel-visible="3"
data-cycle-carousel-fluid="true"
data-cycle-fx="carousel"
data-cycle-prev="#carousel-prev"
data-cycle-next="#carousel-next"
class="cycle-slideshow cycle-front-page-slideshow"
>
Notice that I did add the cycle-slideshow class so Cycle2 auto initializes, then I also added another class of cycle-front-page-slideshow just in case I have other slideshows on my site I can target just this one.
My javascript then looks like this.
function check_window_size( opts ){
// Check if the max-width of window is 899px; window.matchMedia is a native javascript function to check the window size.
var w899 = window.matchMedia( "(max-width: 899px)" );
// if it is max-width of 899px, then set the number of items in the cycle2 carousel slideshow to 2, else show 3
// to see if it matches, you would use the variable and grab the matches array; this will return true or false if window size is max-width 899px
if( w899['matches'] ) {
opts.carouselVisible = 2;
}else{
opts.carouselVisible = 3;
}
}
This is where you would target your slideshow (mine using the .cycle-front-page-slideshow class)
// Grab the cycle2 slideshow initialized from the data attributes on the DIV above
$('.cycle-front-page-slideshow').on('cycle-bootstrap', function(e, opts, API) {
// run the check_window_size function to get initial window size, just in case they are max-width 899px already
check_window_size( opts );
// When window is resized, send the options to the check_window_size function so we can manipulate it
window.onresize = function() {
check_window_size( opts );
};
});
Also note that if you want to use the Carousel functionality, you must download the cycle2 carousel transition plugin from http://jquery.malsup.com/cycle2/download/
Hope this helps out other people.
Looks like you're destroying the slider here:
if(condition){
slider.cycle('destroy');
}
You could do it like that:
$(function() {
var $W = $(window),
slider = $('.slider').cycle();
$W.on('resize',function() {
if ($W.width() < 768) // width of device
slider.cycle('destroy');
});
});

Switch between Superfish and FlexNav depending on window width

I am trying to use 2 jQuery navigation scripts on one page (Superfish for desktops and FlexNav for mobile). I am currently using matchMedia along with the polyfill by Paul Irish to respond to CSS3 media query state changes within JavaScript.
The current code is only accomplishing 50% of the overall goal. If you access the web page initially with a window size equal to or greater than 999px wide then you get Superfish and if you initially access the web page with a window size less than 999px then you get FlexNav. The problem occurs when you resize the window above or below 999px as both scripts become active.
// media query event handler
if (matchMedia) {
var mq = window.matchMedia("(min-width: 999px)");
mq.addListener(WidthChange);
WidthChange(mq);
}
// media query change
function WidthChange(mq) {
if (mq.matches) {
$("ul.sf-menu").superfish({
delay: 350,
speed: 400,
});
} else {
$("ul.flexnav").flexNav({
'animationSpeed': '250',
'transitionOpacity': true,
'buttonSelector': '.menu-button',
'hoverIntent': false
});
}
}
As much as I would like to get this working with matchMedia, I am open to all suggestions.
Update: Thanks to Stephan's suggestion I now have the following code:
jQuery(document).ready(function () {
// add destroy function for FlexNav
flexNavDestroy = function () {
$('.touch-button').off('touchstart click').remove();
$('.item-with-ul *').off('focus');
}
// media query event handler
if (matchMedia) {
var mq = window.matchMedia("(min-width: 999px)");
mq.addListener(WidthChange);
WidthChange(mq);
}
// media query change
function WidthChange(mq) {
if (mq.matches) {
if (typeof (flexNav) != "undefined") {
flexNavDestroy();
}
superfish = $("ul.sf-menu").superfish({
delay: 350,
speed: 400,
});
} else {
if (typeof (superfish) != "undefined") {
superfish.superfish('destroy');
}
flexNav = $("ul.flexnav").flexNav({
'animationSpeed': '250',
'transitionOpacity': true,
'buttonSelector': '.menu-button',
'hoverIntent': false
});
}
}
});
Remaining Issue:
The destroy function for FlexNav is only partially destroying it.
The best way would probably be to destroy the other plugin when you're activating one.
If I look in the source of Superfish there is a destroy function which does this, but flexNav doesn't have such a function. You can create one though:
flexNavDestroy = function(){
$('.touch-button').off('touchstart click').remove();
$(('.item-with-ul *').off('focus');
}
Then you could do this:
function WidthChange(mq) {
if (mq.matches) {
if(typeof(flexNav) != "undefined") {
flexNavDestroy();
}
superfish = $("ul.sf-menu").superfish({
delay: 350,
speed: 400,
});
} else {
if(typeof(superfish) != "undefined") {
superfish.superfish('destroy');
}
flexNav = $("ul.flexnav").flexNav({
'animationSpeed': '250',
'transitionOpacity': true,
'buttonSelector': '.menu-button',
'hoverIntent': false
});
}
}
UPDATE
I've looked a little bit more into FlexNav, and there's a few things I missed.
I think the styles are colliding because FlexNav sets a lot of styles by default. We can easily prevent that by using two classes: One for flexnav styling (the default .flexnav) that we can remove to hide all it's styles, and one for binding the javascript function (that will always stay there, or we can't re-attach it).
I generally like to prepend any classes that are meant as JS hooks with js-, so in my example (below) I replaces the .flexnav class on the menu with .js-flexnav. Then to activate flexnav you have to add this line just before you call $('ul.flexnav').flexNav()
$('.js-flexnav').addClass('flexnav');
In the destroy function you will have to remove the class again, which I will show shortly.
In addition, I'm not sure how Superfish does the showing and hiding, but since FlexNav collapses all submenus, it's also safe to say you should re-show them so that Superfish can do it's own thing.
The updated destroy function to reflect this:
function flexNavDestroy(){
$('.touch-button').off('touchstart click').remove();
$('.item-with-ul *').off('focus');
$('.js-flexnav').removeClass('flexnav').find('ul').show(); // removes .flexnav for styling, then shows all children ul's
}
Here's a jsFiddle that shows activating/deactivating flexNav with the new code: http://jsfiddle.net/9HndJ/
Let me know if this does the trick for you!
here is an alternative path :
once page is loaded :
cache the menu in a jquery object, clone it & instantiate both plugin one on each clone
$menucontainer= $("#menu_container");
$memufish = $menucontainer.find(".menu");
$menuflex=$menufish.clone();
$menufish.superfish().detach();
$menuflex.prependTo($menucontainer).flexnav().detach();
(they are loaded anyway so it's no big deal even if most of the time one won't be needed, it will be there & ready just in case - however test if you can instantiate on the clone without appending it to the DOM)
depending on width append / prepend the required one
$menuflex.prependTo($menucontainer);
on change width detach one reattach the other
$menufish.detach();
$menuflex.prependTo($menucontainer);
you could also work your way checking if plugin was instantiated on a width change (in order to not instantiate uselessly onload) but in any way I believe the use of clone() and detach() are very much adapted to solve easily your problem. The destroy way seems to be a hassle, lots of work (for the script as well when some user is raving with window resize) loss of time & a risk of many bugs to me ( expect more and more lag at every destroy re instantiate - with detach() no worries)
cons : will use a bit more memory overhaul
pros :
script will work less & it will be real fast to switch from one to the other
you could make a plugin from this and add other menu plugin to your app very easily without worry about conflict and how to destroy

Update a jQuery script when user switches to a new tabbed element

I'm having a hard time describing exactly what the problem is.. but the link in question is: http://www.evolutionarycollective.com/events/
You'll notice that when you load the "Calendar" tab, the calendar doesn't show up. If you resize the window, or manipulate the page in some other way, then the calendar appears. The calendar loads fine when it's not within a tab. It's being called in the header with:
<script type='text/javascript'>
jQuery(document).ready(function() {
jQuery('#calendar').fullCalendar({
events: themeforce.events
});
});
</script>
EDIT:
I believe this is the section of the script library that handles the tabs and panes.. (I'm using this with a wordpress theme that calls the tabs via a shortcode)
// jQuery tool's tab creator is not shortcode friendly, transfer the titles to the correct tabs area
jQuery('.bfi_pane').each(function(i) {
var title = jQuery(this).attr('title');
jQuery(jQuery(this).siblings('.bfi_tabs,.bfi_tabs_slide,.bfi_tabs_fade')[0]).append('<div>'+title+'</div>');
});
// Custom slide effect for tabs. slide up then down
jQuery.tools.tabs.addEffect('slide-slide',function(i, done) {
this.getPanes().slideUp(400).eq(i).delay(400).slideDown(400, done);
});
// Custom slide effect for tabs. slide up and down simultaneously
jQuery.tools.tabs.addEffect("slide", function(i, done) {
this.getPanes().slideUp(400).eq(i).slideDown(400, done);
});
// IMPORTANT: SLIDEDOWN JQUERY FIX. we need to assign the correct heights
// so that the slidedown effect doesn't JUMP
jQuery(".bfi_accordion_pane").each(function(i) {
var heightTo = jQuery(this).height();
var paddingTop = parseInt(jQuery(this).css("padding-top").replace('px', ''));
var paddingBottom = parseInt(jQuery(this).css("padding-bottom").replace('px', ''));
var marginTop = parseInt(jQuery(this).css("margin-top").replace('px', ''));
var marginBottom = parseInt(jQuery(this).css("margin-bottom").replace('px', ''));
jQuery(this).css("height", heightTo + paddingTop + paddingBottom + marginTop + marginBottom);
});
// start jQuery tool tabs. we have to do this the long way so we can make
// initialIndex work properly
jQuery(".bfi_tabs_slide").each(function(i) {
var openTab = jQuery(this).attr('rel');
jQuery(this).tabs('> div.bfi_pane', {effect: 'slide-slide', initialIndex: parseInt(openTab, 10)});
});
jQuery(".bfi_tabs_fade").each(function(i) {
var openTab = jQuery(this).attr('rel');
jQuery(this).tabs('> div.bfi_pane', {effect: 'fade', initialIndex: parseInt(openTab, 10)});
});
Is there some way I can somehow refresh the script when that tab lis loaded.. or fire off another document.ready?
Thank you!
The probable explanation is that the full calendar can't figure out its height because when initially created that tab is hidden, which will make it have zero width and height.
Just make the call to .fullCalendar() in a tab switch event handler.
Alternatively use the "off-left" method of hiding tabs, as described in the last few paragraphs of http://jqueryui.com/demos/tabs/

Categories

Resources