Most developers rely on hover events to trigger prefetching, but by then, valuable milliseconds have been wasted.
This library analyzes cursor trajectories in real-time to predict where users are heading, and triggers actions 80- 150ms before they even hover, resulting a significant improvement in perceived performance.
1. Install ForesightJS and import it into your web project.
# Yarn $ yarn add js.foresight # NPM $ npm install js.foresight # PNPM $ pnpm install js.foresight
import { ForesightManager } from "foresightjs" 2. Basic usage
// Optional: Initialize once at app startup for custom global settings.
// If you're fine with defaults, you can skip this.
ForesightManager.initialize({
debug: false, // Set true for visual debugger
defaultHitSlop: { top: 30, left: 30, bottom: 80, right: 30 }, // Default invisible margin
// trajectoryPredictionTime: 80, // How far (ms) to predict ahead
});
// Get the element you want to track
const myButton = document.getElementById("my-button");
if (myButton) {
// Register the element
const { isTouchDevice, unregister } = ForesightManager.instance.register({
element: myButton,
callback: () => {
console.log("ForesightJS: User likely to interact with my-button. Prefetching now!");
// Your prefetching logic or other action here
},
hitSlop: 20, // Optional: Element-specific hit area expansion in pixels.
// Can be a number (all sides) or an object {top, left, bottom, right}.
name: "My Special Button", // Useful for debugging
unregisterOnCallback: true, // Default: true. Unregisters after first callback. Set false for multiple triggers.
});
// For touch devices, ForesightJS won't predict. Use isTouchDevice for fallback.
if (isTouchDevice) {
console.log("This is a touch device. Implement alternative prefetching/interaction logic.");
// e.g., myButton.addEventListener('touchstart', () => { /* prefetch on touch */ });
}
// IMPORTANT: Clean up when the element is no longer needed (e.g., component unmounts)
// If unregisterOnCallback is true, this happens automatically after the first callback.
// Otherwise, or if you need to unregister before callback, call unregister().
// For example, in a React component:
// useEffect(() => {
// // ... registration logic ...
// return () => unregister();
// }, []);
} 3. Global options:
debug (boolean, default: false): Toggles the visual debugger. Invaluable for setup.enableMousePrediction (boolean, default: true): Main switch for prediction.positionHistorySize (number, default: 8): How many mouse positions to track for velocity.trajectoryPredictionTime (number, default: 80): Milliseconds to predict into the future.defaultHitSlop (number | Rect, default: {top:0, left:0, right:0, bottom:0}): Default invisible padding around elements.resizeScrollThrottleDelay (number, default: 50): Throttle for recalculating bounds on resize/scroll (ms).ForesightManager.initialize({
enableMousePrediction: true,
positionHistorySize: 8,
trajectoryPredictionTime: 80,
defaultHitSlop: 10,
resizeScrollThrottleDelay: 50,
debug: false,
debuggerSettings: {
isControlPanelDefaultMinimized: false,
},
}) 4. Element-Specific register() Parameters:
element (HTMLElement, Required): The DOM element.callback (function, Required): Executed on predicted interaction.hitSlop (number | Rect, Optional): Overrides defaultHitSlop for this element. This is key for tuning. A small button might need a larger relative hitSlop than a big banner.name (string, Optional): Descriptive name for debug mode.unregisterOnCallback (boolean, default: true): If true, unregister() is called automatically after the callback executes once. Set to false if you need the callback to fire multiple times for the same element.ForesightManager.instance.register({
element: myElement,
callback: prefetchData,
hitSlop: { top: 10, left: 50, right: 50, bottom: 100 }, // Element-specific hit area
name: "Product Card", // Useful in debug mode
unregisterOnCallback: false, // Allow callback to run multiple times
}) Traditional Hover-Based Prefetching (e.g., onmouseover):
Viewport-Based Prefetching (e.g., Next.js <Link> default, IntersectionObserver for links):
quicklink or the default behavior in some frameworks prefetch resources for all links that enter the viewport.mousedown or touchstart, or sometimes on a brief hover. It’s very effective for making clicks feel instant.instant.page if instant.page is configured for mousedown, with ForesightJS handling the “approach” and instant.page handling the “commitment.”Q: How much overhead does ForesightJS add? Is it bad for performance?
A: It’s designed to be lightweight. The main work happens on mousemove, which can be frequent. However, calculations are generally simple (vector math on a small history). The resizeScrollThrottleDelay also helps. In my experience, the performance gain from earlier prefetching and smoother perceived interactions usually outweighs the minor computational cost. As always, profile in your specific app.
Q: What’s the best way to determine hitSlop values?
A: Start with the debug: true mode. This visualizes the hit areas. For critical interactive elements, a hitSlop of 10-50px might be a good starting point, depending on element size and density of your UI. For larger, more isolated elements, you might use a larger hitSlop. It’s an iterative process: observe user interaction patterns (or your own) and adjust.
Q: So, ForesightJS doesn’t do anything for touch devices?
A: Correct. It focuses on mouse movement. The register() method returns isTouchDevice, which you should use to implement a fallback. For touch, common strategies include prefetching on touchstart for links, or using IntersectionObserver for viewport-based preloading. The ForesightJS docs have examples for Next.js and React Router integrations that show how to handle this.
Q: Can I use ForesightJS with React, Vue, Angular, etc.?
A: Absolutely. It’s framework-agnostic since it operates on DOM elements. In React, you’d typically register an element in a useEffect hook (getting a ref to the DOM element) and return the unregister function from the useEffect for cleanup. Similar patterns apply to Vue (mounted/beforeUnmount) and Angular (ngOnInit/ngOnDestroy).
Q: What if the user’s mouse movements are chaotic or they just wiggle the mouse over an area?
A: The prediction is based on a recent, short trajectory. If the mouse movement is truly random without clear direction towards an element, it’s less likely to trigger a callback unless the cursor happens to pass through a hit area with sufficient projected velocity. The trajectoryPredictionTime and positionHistorySize settings influence its sensitivity. If unregisterOnCallback is false, rapid movements over an element could trigger the callback multiple times if intent is repeatedly predicted.
Q: Is there a risk of triggering too many callbacks if unregisterOnCallback is false?
A: Yes, potentially. If a user hovers near an element or moves their mouse back and forth over its “approach path,” the callback could fire multiple times. This is why unregisterOnCallback: true is the default and suitable for most prefetching scenarios (prefetch once). If you set it to false, ensure your callback is idempotent or designed to handle multiple calls gracefully.
The post Predict User Intent Based On Mouse Movements – ForesightJS appeared first on CSS Script.
All eight episodes of Ted Season 2 debut on March 5 on Peacock. As soon…
In the ever-evolving world of malvertising, where bad actors continually refine their techniques, a new…
Retired Concord Circuit Court Judge Gerard Boyle has been nominated to be the next settlement…
Salisbury residents will be voting on a number of issues and candidates on March 10,…
Christopher Ellms Jr. received a 4-1 vote from the executive council on Wednesday to become…
Merrimack Valley voters will cast their ballots on March 5 in four School Board races,…
This website uses cookies.