A jQuery UI widget for building simple, scroll-based pages and interactions.
Key features include:
- 100% style agnostic. Just a collection of often-used scroll-based patterns.
- Can can you existing DOM or use an array of objects to create markup.
- 16+ jQueryUI-style events/callbacks for various application state events.
- Focus and blur event when an individual story becomes active or inactive.
- Items can be grouped into categories, with event dispatched as categories change.
- Items filterable by user-specified tags.
- Items aware of their in-viewport status.
- Programmatically animated scroll to any item.
- Throttled scroll events and minimal DOM usage.
Any recent version of:
- jQuery
- jQuery UI (core, widget and optionally effects core for custom easings)
- Underscore
- Basic instantiation via existing page elements
- Basic instantiation using custom item selector
- Pass data in via data attributes
- Basic instantiation via array of objects
- Programmatically scroll up and down
- Item active event
- Move the trigger point
- Using categories
- Lazy load images on enterviewport
Download the production version or the development version.
In your web page:
<script src="jquery.js"></script>
<script src="jquery-ui.js"></script>
<script src="underscore.js"></script>
<script src="dist/scrollstory.min.js"></script>
<script>
jQuery(function($) {
$('#container').ScrollStory();
});
</script>
<div id="container">
<div class="story">...</div>
...
</div>
$('#container').ScrollStory();
In its most basic form, ScrollStory takes a container element and searches for '.story' child elements. Internally, ScrollStory turns those elements into 'item' objects and assigns them lots of default properties, like its 'index' position in the list, 'topOffset' (point from the top at which is becomes active), it's 'inViewport' status (true or false), and whether it has a custom 'scrollOffet' (or point on page it triggers active, different from the other items). These are covered in detail below.
In addition to object properties, ScrollStory modifies the DOM in a few ways:
- A class of 'storyScroll_story' is added to every item
- A class of 'active' is added to the currently active item
- A class of 'scrollStory_active' is added to the container if any item is active.
<div id="container">
<div class="story" data-bgcolor="#0000ff">...</div>
...
</div>
$('#container').ScrollStory();
Data can be dynamically added to individual story items by adding it as data attributes. Combined with ScrollStory's API methods, some very dynamic applications can be built.
<div id="container"></div>
$('#container').ScrollStory({
content: [{hed:'Headline1'}, {hed, 'Headline2'}]
itembuild: function(ev, data) {
data.item.el.html('<h2>'+data.item.domData.hed+'</h2>');
}
});
The widget can be built with an array of data objects, which will be used to generate all the ScrollStory elements. To be useful, you'll most likely need to use an 'itembuild' callback or event to append your data any way you see fit inside a generated item element.
contentSelector
throttleType
scrollSensitivity
triggerOffset
preOffetActivation
keyboard
scrollOffset
autoActivateFirst
delayFirstActivationToOffset
speed
scrollRate
easing
checkViewportVisibility
verboseItemClasses
throttleTypeOptions
Type: String
Default value: '.story'
$('#container').ScrollStory({
contentSelector: '.story'
});
A jQuery selector to find story items within your widget.
Type: String
Default value: 'debounce'
$('#container').ScrollStory({
throttleType: 'debounce' // debounce or throttle
});
Set the throttle -- or rate-limiting -- method used when testing items' active state. These are wrappers around Underscore's throttle and debounce functions. Use 'throttle' to trigger active state on the leading edge of the scroll event. Use 'debounce' trigger on trailing edge.
Type: Number
Default value: 100
$('#container').ScrollStory({
scrollSensitivity: 100
});
How often in milliseconds to check for the active item during a scroll.
Example of a lower scroll sensitivity
Type: Number
Default value: 0
$('#container').ScrollStory({
triggerOffset: 0
});
The trigger offset is the distance from the top of the page use to determine which item is active.
Example of trigger point farther down the page
Type: Boolean
Default value: true
$('#container').ScrollStory({
preOffsetActivation: true
});
By default, ScrollStory activates the item closest to the trigger offset, indifferent to whether that item is above or below the line. If set to false, the widget will no longer allow items to be active 'pre' the triggerOffset point. Generally, a value of true gives a very natural feel.
Type: Boolean
Default value: true
$('#container').ScrollStory({
keyboard: true
});
Enable left and right arrow keys to move between story items.
Type: Number
Default value: 0
$('#container').ScrollStory({
scrollOffset: 0
});
When programatically scrolled, the position from the top the item is scrolled to.
Type: Boolean
Default value: true
$('#container').ScrollStory({
autoActivateFirst: true
});
Automatically activate the first item on page load, regardless of its position relative to the offset and the 'preOffsetActivation' setting. Common case: you want to disable 'preOffsetActivation' to ensure late scroll activations but need the first item to be enabled on load. With 'preOffsetActivation:true', this is ignored.
Type: Boolean
Default value: true
$('#container').ScrollStory({
delayFirstActivationToOffset: 0
});
If 'autoActivateFirst:false' and 'preOffsetActivation:true', app logic would dictate the first item would activate after a 1px scroll. Usually, we want to delay that first activation until the first item is to the offset, but maintain the activation behavior of other items. By default, we delay the activation on first item. Set to false otherwise. No effect if 'autoActivateFirst' is true or 'preOffsetActivation' is false.
Type: Number
Default value: 800
$('#container').ScrollStory({
speed: 800
});
Automated scroll speed in ms. Set to 0 to remove animation.
Type: String
Default value: 'dynamic'
$('#container').ScrollStory({
scrollRate: 'dynamic' // 'dynamic' or 'fixed'
});
The rate of scroll for programatic scrolls. 'fixed' means travel the full distance over 'speed' time, regardless of distance. 'dynamic' means the speed is a guide for the target travel time. Longer distances will take longer, and shorter distance will take less time. This is meant to have a more natural feel. Tip: you'll want a higher speed if you use 'dynamic' than you would for 'fixed'.
Type: String
Default value: 'swing'
$('#container').ScrollStory({
easing: 'swing'
});
The easing type for programatic scrolls. If jQuery effects core is included in your jQuery UI build, all jQuery UI easings are available: http://api.jqueryui.com/easings/. Otherwise, you'll only have jQuery's built-in 'swing' and 'linear.' Tip: 'swing' and 'easeOutQuad' have a natural feel.
Type: Boolean
Default value: false
$('#container').ScrollStory({
checkViewportVisibility: false
});
Whether to keep track of which individual elements are in the viewport. This is can be CPU intensive, so is turned off by default. It is checked at the 'scrollSensitivity 'rate.
When enabled, events are triggered for items entering and leaving the viewport, and class of 'inViewport' is added and removed from those items' markup.
Regardless of 'checkViewportVisibility' setting, the getItemsInViewport() method will alway return the items in the viewport.
Type: Boolean
Default value: false
$('#container').ScrollStory({
verboseItemClasses: false
});
Add css classes to items to reflect their order from the active item. Class 'order0' for the active item. 'class-1', for the item above, continuing on through 'class-2' to 'class-N', and class 'order1' through 'orderN' for the items below.
Type: Boolean\Object
Default value: null
$('#container').ScrollStory({
throttleTypeOptions: null
});
Options to pass to Underscore's throttle or debounce for scroll. Type/functionality dependent on 'throttleType'
At its core, ScrollStory simply manages an array of 'item' objects, keeping track of various properties and states. The entire object is user accessible, but generally should be thought of in two parts:
- The object root, which ScrollStory uses to maintain state.
- The domData object within the 'item' for user data.
The 'item' object looks like this:
{
active: true, // is this item, exclusively, the current item?
category: undefined, // optionally, the single category does this belong to?
tags: Array[1], // optionally, an array of tags associated with this item
domData: Object, // User-specifed data, either via data-* attributes or the object used when instantiating from an array of objects.
el: ot.fn.ot.init[1], // the jQuery object that contains the item's markup
filtered: false, // is this item filtered from the list? by default, that includes no styling, only logic to exclude it from being set to active.
id: "scrollStory_story_0", // user assigned or auto-generated.
inViewport: true, // is this item in the viewport?
index: 0, // its index in the list array of ojects
nextItem: Object, // reference to the item immediately after this one
previousItem: false, // reference to the item immediately after this one
topOffset: 322, // distance in pixels from this item's trigger offset
scrollOffset: false, // a scroll offset that differs from the global offset
triggerOffset: false, // a trigger offset that differs from the global offset
height: 1035,
width: 676
}
Most of ScrollStory's functionality can be used via the widget's callbacks and events. For details on how jQuery UI events work, see their documentation.
The events are:
indexchange
itemblur
itemfilter
enterviewport
exitviewport
itembuild
categorychange
active
inactive
aboveoffset
belowoffset
scroll
Fired when an item gains 'focus', which can happen from a scroll-based activation (most commonly), or externally via this.index(), or this.scrollTo*().
$('#container').ScrollStory({
indexchange: function(ev, data) {
var item = data.item; // most events put the affected item at data.item. This is the newly activated item.
}
})
Fired when an item loses 'focus'.
$('#container').ScrollStory({
itemblur: function(ev, data) {
var item = data.item; // newly un-activated item
}
})
Fired when an item is filtered, which means it is no longer considered ScrollStory determines which item is active. Intended to be combined with visual changes or hidind so you can visually filter the item from the list.
$('#container').ScrollStory({
itemfilter: function(ev, data) {
var item = data.item;
})
Fired when an item is unfiltered.
$('#container').ScrollStory({
itemunfilter: function(ev, data) {
var item = data.item;
}
})
Fired when an item enters the visible portion of the screen. This is great for triggering things like lazy loads.
$('#container').ScrollStory({
enterviewport: function(ev, data) {
var item = data.item;
})
Fired when an item leaves the visible portion of the screen.
$('#container').ScrollStory({
exitviewport: function(ev, data) {
var item = data.item;
}
})
Fired when the widget is made aware of an individual item during instantiation. This is a good time to add additional properties to the object. If you're passing in data to build the DOM via the 'content' property, you should append HTML to the item now, as the item hasn't yet been added to the page and the render will be faster.
$('#container').ScrollStory({
content: items, // array of objects that'll be passed into item.domData.
itembuild: function(ev, data) {
var item = data.item;
item.el.html('<p>My new content!</p>');
}
})
Fired when new active item is in a different category than previously active item.
$('#container').ScrollStory({
categorychange: function(ev, data) {
var category = data.category;
var previousCategory: data.previousCategory;
}
})
Fired when the widget changes states from have no active item to an active item. Depending on instantiation options, this may or not be on instantiation. 'autoActivateFirst' and 'delayFirstActivationToOffset' may delay this event until a certain scroll position has been reached.
$('#container').ScrollStory({
active: function(ev) {
}
})
Fired when the widget changes states from an active item to not having an active item.
$('#container').ScrollStory({
inactive: function(ev) {
}
})
Scrolling above global scroll offset
$('#container').ScrollStory({
aboveoffset: function(ev) {
}
})
Scrolling below global scroll offset
$('#container').ScrollStory({
inactive: function(ev) {
}
})
Throttled scroll event. The current active element is passed in.
$('#container').ScrollStory({
scroll: function(ev, data) {
var activeItem = data.item;
}
})
ScrollStory exposes many methods for interacting with the widget. The API is available, like other jQuery UI widgets, by accessing it via its namespace and name on the element you instantiate on. You should probably cache the object like so:
// save to an object
var scrollStory = $('#container').ScrollStory({
itembuild: function(ev,data){
data.item.el.html('<p>hi</p>');
}
}).data('sjwScrollStory');
// use a method
scrollStory.index(3);
The primary methods include:
- scrollStory.
isActive()
- scrollStory.
updateOffsets()
- scrollStory.
index()
- scrollStory.
next()
- scrollStory.
previous()
- scrollStory.
scrollToItem()
- scrollStory.
scrollToIndex()
- scrollStory.
getItems()
- scrollStory.
getItemsInViewport()
- scrollStory.
getItemsByCategory()
- scrollStory.
getFilteredItems()
- scrollStory.
getUnfilteredItems()
- scrollStory.
getItemById()
- scrollStory.
getItemByIndex()
- scrollStory.
getTopItem()
- scrollStory.
getTopItemId()
- scrollStory.
getNextItem()
- scrollStory.
getPreviousItem()
- scrollStory.
getLength()
- scrollStory.
getCategoryIds()
- scrollStory.
getActiveCategory()
- scrollStory.
scrollToCategory()
- scrollStory.
scrollToCategoryIndex()
- scrollStory.
getTags()
- scrollStory.
getItemsByTag()
- scrollStory.
getItemsBy()
- scrollStory.
filter()
- scrollStory.
unfilter()
- scrollStory.
filterByTag()
- scrollStory.
unfilterByTag()
- scrollStory.
filterBy()
- scrollStory.
unfilterAllItems()
scrollStory.isActive()
: Whether or not any of the items are active. If so, the entire widget is considered to be 'active.'
scrollStory.updateOffsets()
: Update the widget's awareness of each item's distance to the trigger. This method is called internally after instantiation and automatically on window resize. It should also be called externally anytime DOM changes affect your items' position on the page, like when filtering changes the size of an element.
scrollStory.index()
: Get or set the current index of the active item. On set, also scroll to that item.
Arguments
- index (optional Number) - The zero-based index you want to activate.
scrollStory.next()
: Convenience method to navigate to the item after the active one.
scrollStory.previous()
: Convenience method to navigate to the item before the active one.
scrollStory.scrollToItem()
: Given an item.id
, scroll to it.
Arguments
- id (String) - The item.id to scroll to
- opts (optional Object) - Allows you to pass in the
easing
type,speed
andscrollOffset
for the scroll, overriding the global and item-specific settings already established. - cb (optional Function) - Callback to execute after scroll.
scrollStory.scrollToIndex()
: Given a zero-based index, scroll to it.
Arguments
- index (Number) - The index to scroll to
- opts (optional Object) - Allows you to pass in the
easing
type,speed
andscrollOffset
for the scroll, overriding the global and item-specific settings already established. - cb (optional Function) - Callback to execute after scroll.
scrollStory.getItems()
: Return an array of all item objects.
scrollStory.getItemsInViewport()
: Return an array of all item objects currently visible on the screen.
scrollStory.getItemsByCategory()
: Return an array of all item objects in the given category.
Arguments
- slug (String) - The category slug
scrollStory.getFilteredItems()
: Return an array of all item objects whose filtered state has been set to true.
scrollStory.getUnfilteredItems()
: Return an array of all item objects whose filtered state has been not been set to true.
scrollStory.getItemById()
: Given an item.id
, return its data.
Arguments
- id (String) - The item.id
scrollStory.getItemByIndex()
: Given an item's zero-based index, return its data.
Arguments
- index (Number) - Zero-based index.
scrollStory.getTopItem()
: Return the active item
object. False it there isn't one.
scrollStory.getTopItemId()
: Return the active item.id
. False it there isn't one.
scrollStory.getNextItem()
: Return the item
object for item after the active on. False it there isn't one.
scrollStory.getPreviousItem()
: Return the item
object for item before the active on. False it there isn't one.
scrollStory.getLength()
: Return the number of items.
scrollStory.getCategoryIds()
: Return array of category slugs.
scrollStory.getActiveCategory()
: Return the slug of the active category.
scrollStory.scrollToCategory()
: Scroll to the first item in given category
Arguments
- id (String) - Category slug
scrollStory.scrollToCategoryIndex()
: Scroll to the first item in the category, which are tracked by the order in which they appear in items, at the index specified. getCategoryIds
lists the categories in order.
Arguments
- index (Number) - The index to scroll to
scrollStory.getTags()
: Return array of tag slugs.
scrollStory.getItemsByTag()
: Return an array of item objects that contain the given tag.
Arguments
- tags (String/Array) - The slug or array of slugs
- all (optional Boolean) - false (default) if item must can contain any given tag. true if item must contain all tags.
scrollStory.getItemsBy()
: Return an array of item objects that pass an aribitrary truth test.
Arguments
- tags (Function) - The function to check all items against
Example
scrollStory.getItemsBy(function(item){
return item.domData.slug=='josh_williams';
});
scrollStory.filter()
: Given an item or item id, change its state to filtered.
Arguments
- item (Object/String) -
item
oritem.id
scrollStory.unfilter()
: Given an item or item id, change its state to unfiltered.
Arguments
- item (Object/String) -
item
oritem.id
scrollStory.filterByTag()
: Given a tag slug, change all matching items' state to filtered.
Arguments
- tag (String/Array) - The slug or array of slugs to filter
- all (optional Boolean) - false (default) if item must can contain any given tag. true if item must contain all tags.
scrollStory.unfilterByTag()
: Given a tag slug, change all matching items' state to unfiltered.
Arguments
- tag (String/Array) - The slug or array of slugs to filter
- all (optional Boolean) - false (default) if item must can contain any given tag. true if item must contain all tags.
scrollStory.filterItemsBy()
: Filter items that pass an abritrary truth test.
Arguments
- tags (Function) - The function to check all items against
Example
scrollStory.filterItemsBy(function(item){
return item.domData.slug=='josh_williams';
});
scrollStory.unfilterAllItems()
: Change all items' state to unfiltered.
0.0.1
- Initial Release