High-performance mobile scrolling library with smooth scrolling, momentum, bounce effects, and extensive plugin support.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Comprehensive event system for tracking scroll state, user interactions, and lifecycle events. Better Scroll uses a custom event emitter pattern that allows you to listen for various scroll-related events throughout the interaction lifecycle.
Registers an event listener for the specified event type.
/**
* Register an event listener
* @param type - Event type name
* @param fn - Event handler function
* @param context - Context for 'this' in handler (default: BScroll instance)
*/
on(type: string, fn: Function, context?: any): void;Registers a one-time event listener that automatically removes itself after first execution.
/**
* Register a one-time event listener
* @param type - Event type name
* @param fn - Event handler function
* @param context - Context for 'this' in handler (default: BScroll instance)
*/
once(type: string, fn: Function, context?: any): void;Removes an event listener for the specified event type.
/**
* Remove an event listener
* @param type - Event type name
* @param fn - Event handler function to remove
*/
off(type: string, fn: Function): void;Usage Examples:
import BScroll from "better-scroll";
const scroll = new BScroll('.wrapper');
// Register event listeners
scroll.on('scroll', handleScroll);
scroll.on('scrollEnd', handleScrollEnd);
// One-time listener
scroll.once('scrollStart', () => {
console.log('First scroll started!');
});
// Remove listener
scroll.off('scroll', handleScroll);
function handleScroll(position) {
console.log(`Scrolling to: ${position.x}, ${position.y}`);
}
function handleScrollEnd(position) {
console.log(`Scroll ended at: ${position.x}, ${position.y}`);
}Fired during scrolling with current position coordinates.
// Event: 'scroll'
// Payload: {x: number, y: number}
// Fired: During scrolling movement (frequency depends on probeType)Usage:
scroll.on('scroll', (position) => {
console.log(`Current position: ${position.x}, ${position.y}`);
// Update scroll indicator
updateScrollIndicator(position.y);
// Parallax effects
updateParallaxElements(position.y);
});Fired when scrolling begins, before any movement occurs.
// Event: 'scrollStart'
// Payload: none
// Fired: When user starts scroll gestureFired before scrolling starts, earliest in the scroll lifecycle.
// Event: 'beforeScrollStart'
// Payload: none
// Fired: Before scroll gesture processing beginsFired when scrolling animation completes and comes to rest.
// Event: 'scrollEnd'
// Payload: {x: number, y: number}
// Fired: When scrolling animation finishesUsage Example:
scroll.on('scrollStart', () => {
// Hide UI elements during scroll
hideFloatingButton();
});
scroll.on('scrollEnd', (position) => {
// Show UI elements when scroll stops
showFloatingButton();
// Save scroll position
saveScrollPosition(position);
// Load more content if near bottom
if (isNearBottom(position.y)) {
loadMoreContent();
}
});Fired when user lifts finger/releases mouse, regardless of whether scrolling will continue.
// Event: 'touchEnd'
// Payload: {x: number, y: number}
// Fired: On touch/mouse up eventFired when user performs a quick flick gesture.
// Event: 'flick'
// Payload: none
// Fired: When flick gesture is detected (based on flickLimitTime and flickLimitDistance)Fired when a scroll operation is canceled (e.g., when a click is detected instead of scroll).
// Event: 'scrollCancel'
// Payload: none
// Fired: When scroll is canceled due to click detectionUsage Examples:
// Handle touch end for custom logic
scroll.on('touchEnd', (position) => {
// Check if user scrolled past certain point
if (position.y < -triggerThreshold) {
triggerRefresh();
}
});
// Handle flick gestures
scroll.on('flick', () => {
// Custom flick behavior
console.log('User performed a flick gesture');
});Fired after the refresh() method completes recalculation.
// Event: 'refresh'
// Payload: none
// Fired: After refresh() method completesFired when the BScroll instance is destroyed.
// Event: 'destroy'
// Payload: none
// Fired: When destroy() method is calledUsage Examples:
scroll.on('refresh', () => {
// Update custom UI after dimensions recalculated
updateCustomScrollbar();
});
scroll.on('destroy', () => {
// Clean up custom resources
cleanupCustomElements();
removeCustomEventListeners();
});When pull-to-refresh is enabled, additional events are available:
// Event: 'pullingDown'
// Payload: none
// Fired: When pull-down threshold is reached
// Required: options.pullDownRefresh = true
// Event: 'pullingUp'
// Payload: none
// Fired: When pull-up threshold is reached
// Required: options.pullUpLoad = trueUsage:
const scroll = new BScroll('.wrapper', {
pullDownRefresh: {
threshold: 50,
stop: 20
},
pullUpLoad: {
threshold: 50
}
});
scroll.on('pullingDown', () => {
// Show loading indicator and refresh data
showRefreshLoader();
fetchNewData().then(() => {
scroll.finishPullDown();
});
});
scroll.on('pullingUp', () => {
// Load more data
showLoadMoreIndicator();
fetchMoreData().then(() => {
scroll.finishPullUp();
});
});The frequency of scroll events depends on the probeType option:
interface ProbeTypeConfiguration {
probeType: 0 | 1 | 2 | 3;
}
// probeType: 0 - No scroll events during animation
// probeType: 1 - Non-real-time scroll events (debounced)
// probeType: 2 - Real-time scroll events during momentum only
// probeType: 3 - Real-time scroll events always (including animations)Usage Examples:
// For basic scroll position tracking
const scroll = new BScroll('.wrapper', {
probeType: 1
});
// For smooth scroll indicators
const scroll = new BScroll('.wrapper', {
probeType: 2
});
// For real-time parallax effects
const scroll = new BScroll('.wrapper', {
probeType: 3
});class ScrollTracker {
constructor(scrollInstance) {
this.scroll = scrollInstance;
this.setupEventListeners();
}
setupEventListeners() {
this.scroll.on('scroll', this.updatePosition.bind(this));
this.scroll.on('scrollEnd', this.savePosition.bind(this));
}
updatePosition(pos) {
// Update scroll indicator
const percentage = Math.abs(pos.y) / Math.abs(this.scroll.maxScrollY);
this.updateScrollbar(percentage);
}
savePosition(pos) {
// Persist scroll position
localStorage.setItem('scrollPosition', JSON.stringify(pos));
}
}class InfiniteScroll {
constructor(scrollInstance, threshold = 100) {
this.scroll = scrollInstance;
this.threshold = threshold;
this.loading = false;
this.scroll.on('scroll', this.checkLoadMore.bind(this));
}
checkLoadMore(pos) {
if (this.loading) return;
const bottom = Math.abs(pos.y);
const maxScroll = Math.abs(this.scroll.maxScrollY);
if (bottom + this.threshold >= maxScroll) {
this.loadMore();
}
}
async loadMore() {
this.loading = true;
try {
await this.fetchMoreData();
this.scroll.refresh(); // Recalculate boundaries
} finally {
this.loading = false;
}
}
}const scroll = new BScroll('.wrapper', {
probeType: 3
});
scroll.on('scroll', (pos) => {
// Parallax background
const parallaxElement = document.querySelector('.parallax-bg');
const parallaxSpeed = 0.5;
const translateY = pos.y * parallaxSpeed;
parallaxElement.style.transform = `translateY(${translateY}px)`;
// Fade header based on scroll
const header = document.querySelector('.header');
const opacity = Math.max(0, 1 - Math.abs(pos.y) / 200);
header.style.opacity = opacity;
});