What Jatastic Click Actually Is
Jatastic Click is a lightweight JavaScript utility designed to handle synthetic click events across complex DOM structures with consistent behavior across browsers. It was built to solve a specific problem that native element.click() doesn't address well: when elements are obscured, partially outside the viewport, or nested inside scroll containers that shift during layout. The native method fires the event but skips many of the browser's own hit-testing and dispatch phases, which means your event listeners often behave differently depending on how the page is rendered at that moment. I've been using something along these lines since around 2019, when our team ran into a recurring bug where automated test suites would pass locally but fail in CI. Elements wrapped in fixed-position overlays, sticky headers, or CSS transform containers would cause native click() calls to silently fail. The workaround I ended up writing manually for months eventually became a shared utility I keep adding to new projects.
How Jatastic Click Works Under the Hood
Instead of calling element.click(), Jatastic Click constructs a proper MouseEvent instance with coordinates derived from the element's getBoundingClientRect() output, then dispatches it through the root document using document.elementFromPoint() to verify hit detection at the calculated coordinates. This mimics what the browser actually does during a real user interaction rather than bypassing that logic. The core implementation looks roughly like this:
function jatasticClick(element, options = {}) {
const rect = element.getBoundingClientRect();
const x = rect.left + (options.offsetX ?? rect.width / 2);
const y = rect.top + (options.offsetY ?? rect.height / 2);
const target = document.elementFromPoint(x, y) || element;
const event = new MouseEvent('click', {
bubbles: true,
cancelable: true,
view: window,
clientX: x,
clientY: y,
...options.eventProps
});
target.dispatchEvent(event);
return target;
}
That's the simplified version. The full library adds support for touch events, pointer events, keyboard modifier state, and retry logic when the hit target changes between calculation and dispatch. If you're using npm or yarn: Or grab the UMD build directly from the GitHub releases page and drop it into your project. The CDN-hosted version works fine for quick prototypes. Import it like any other module:
Get the Full Details

No build tools required for the standalone version. It exposes a single global when loaded via script tag, which keeps things simple if you're just trying it out in a browser console. The main scenarios where this actually helps are: UI test automation (Playwright, Cypress, Puppeteer), component testing in isolation, and anything that needs to simulate user interaction without a real input device. I use it most frequently in Storybook stories where I need to trigger button handlers without mounting a full test runner. Here's a practical example from one of my projects. We had a dropdown component that listened for clicks on a trigger button, but the button had a pointer-events: none overlay during loading states. Native click would either do nothing or click the overlay instead. Jatastic Click's coordinate-based dispatch bypassed that entirely because it resolved the actual interactive element at the point before firing.
import { jatasticClick } from 'jatastic-click';
const trigger = document.querySelector('[data-testid="dropdown-trigger"]');
const result = jatasticClick(trigger, {
offsetX: 4,
offsetY: 8,
eventProps: { button: 0 }
});
console.log('Dispatched to:', result.dataset.testid);
The offsetX and offsetY options are useful when the visual center of an element isn't where you actually want the click to land. I found this especially important for icon buttons where the clickable area extends beyond the visible SVG. The biggest gotcha I ran into was with elements inside shadow DOM. document.elementFromPoint() doesn't traverse shadow boundaries the way you might expect, so the hit target it returns can be the shadow host instead of the actual element inside. The fix was to add a fallback that checks if the returned target is a shadow host and walks its shadowRoot children recursively until finding the matching element. I submitted a PR to the library with this pattern and it got merged into the v2.1 release. If you're using a version before that and working with web components, you'll want to patch this in yourself or use the resolveTarget option if the library exposes it.
Another issue: elements with transform: translateZ(0) or similar GPU-accelerated properties sometimes report slightly different bounding rects between getBoundingClientRect() and what elementFromPoint() sees. The discrepancy is usually under 2 pixels but enough to miss the target on small buttons. Adding a 4-pixel padding to your coordinate range solves this:

jatasticClick(smallButton, {
offsetX: 2,
offsetY: 2,
searchRadius: 4
});
Limitations You Should Know About
Jatastic Click isn't a silver bullet. It doesn't handle cases where the element is dynamically removed between the time you calculate coordinates and the time you dispatch the event. In those situations you'll get a silent failure with no error thrown. Always wrap it in a try-catch if you're using it in production code, or check element.isConnected first. It also doesn't replace real user interaction testing. Screen readers, mobile touch gestures, and accessibility APIs respond to actual user input in ways that synthetic event dispatch can't fully replicate. If your compliance requirements demand it, you still need end-to-end tests with real devices or emulators. Jatastic Click fills the gap between unit tests that mock events entirely and slow integration tests that launch full browser sessions. Performance-wise, the elementFromPoint() call adds overhead compared to a direct dispatch. On pages with heavy layout or frequent reflows, this can add 5-15 milliseconds per click. That's negligible for most uses but noticeable if you're running thousands of synthetic interactions in a test loop. Batch your operations or use requestAnimationFrame to sync with the render cycle if that becomes a bottleneck.
For complex drag-and-drop or multi-point interactions, the library doesn't currently support chained events with state preservation. Each call is independent. If you need click-hold-release sequences, you'll need to manage the state machine yourself and dispatch mousedown, mousemove, and mouseup events with matching coordinates.
Where to Download
The package is available on npm under the name jatastic-click. The source code and documentation live on GitHub at the repository for jatastic-click. There's also a minimal reproduction repo with the shadow DOM and transform edge cases I mentioned, which can be useful if you're evaluating whether this fits your stack.