Repository: swup/swup
Pull request #609 (2023-01-26 14:04:55)

Allow ignoring visits by original event
**Description**

- Pass the original `event` that triggered a visit into the `ignoreVisit` callback
- Allow canceling a visit by, e.g.
  - the mouse coordinates of a link click
  - the submitter element of a form submission
- I needed this recently to filter out button-click submissions from enter-key submissions
- The feature was requested by users a few times: having access to the original click event in a callback

**Questions**

- Does this need better types? Using `Event` seems future-proof, but not sure.

**Checks**

- [x] The PR is submitted to the `master` branch
- [x] The code was linted before pushing (`npm run lint`)
- [x] All tests are passing (`npm run test`)
- [x] New or updated tests are included
- [ ] The documentation was updated as required



Patch:
diff --git a/src/Swup.ts b/src/Swup.ts
index 57cf74e..a2b4842 100644
--- a/src/Swup.ts
+++ b/src/Swup.ts
@@ -47,7 +47,7 @@ export type Options = {
 	requestHeaders: Record<string, string>;
 	plugins: Plugin[];
 	skipPopStateHandling: (event: any) => boolean;
-	ignoreVisit: (url: string, { el }: { el?: Element }) => boolean;
+	ignoreVisit: (url: string, { el, event }: { el?: Element; event?: Event }) => boolean;
 	resolveUrl: (url: string) => string;
 };
 
@@ -122,7 +122,7 @@ export default class Swup {
 		animationSelector: '[class*="transition-"]',
 		cache: true,
 		containers: ['#swup'],
-		ignoreVisit: (url, { el } = {}) => !!el?.closest('[data-no-swup]'),
+		ignoreVisit: (url, { el, event } = {}) => !!el?.closest('[data-no-swup]'),
 		linkSelector: 'a[href]',
 		plugins: [],
 		resolveUrl: (url) => url,
@@ -215,7 +215,7 @@ export default class Swup {
 		document.documentElement.classList.remove('swup-enabled');
 	}
 
-	shouldIgnoreVisit(href: string, { el }: { el?: Element } = {}) {
+	shouldIgnoreVisit(href: string, { el, event }: { el?: Element; event?: Event } = {}) {
 		const { origin, url, hash } = Location.fromUrl(href);
 
 		// Ignore if the new origin doesn't match the current one
@@ -229,7 +229,7 @@ export default class Swup {
 		}
 
 		// Ignore if the visit should be ignored as per user options
-		if (this.options.ignoreVisit(url + hash, { el })) {
+		if (this.options.ignoreVisit(url + hash, { el, event })) {
 			return true;
 		}
 
@@ -242,7 +242,7 @@ export default class Swup {
 		const { href, url, hash } = Location.fromElement(linkEl as HTMLAnchorElement);
 
 		// Exit early if the link should be ignored
-		if (this.shouldIgnoreVisit(href, { el: linkEl })) {
+		if (this.shouldIgnoreVisit(href, { el: linkEl, event })) {
 			return;
 		}
 
@@ -320,7 +320,7 @@ export default class Swup {
 		const href = event.state?.url ?? location.href;
 
 		// Exit early if the link should be ignored
-		if (this.shouldIgnoreVisit(href)) {
+		if (this.shouldIgnoreVisit(href, { event })) {
 			return;
 		}
 


Test patch:
diff --git a/src/__test__/index.test.ts b/src/__test__/index.test.ts
index e3b2a8e..5a3e776 100644
--- a/src/__test__/index.test.ts
+++ b/src/__test__/index.test.ts
@@ -1,3 +1,5 @@
+import delegate from 'delegate-it';
+
 import pckg from '../../package.json';
 import Swup, { Options, Plugin } from '../index';
 
@@ -58,18 +60,31 @@ describe('ignoreVisit', () => {
 	it('should be called with relative URL', () => {
 		const ignoreVisit = jest.fn(() => true);
 		const swup = new Swup({ ignoreVisit });
-
-		swup.shouldIgnoreVisit(baseUrl + '/path/?query#hash');
+		swup.shouldIgnoreVisit(`${baseUrl}/path/?query#hash`);
 
 		expect(ignoreVisit.mock.calls).toHaveLength(1);
-		expect(ignoreVisit.mock.lastCall).toBeDefined();
 		expect((ignoreVisit.mock.lastCall as any)[0]).toEqual('/path/?query#hash');
 	});
 
-	it('should be called from loadPage method', () => {
+	it('should have access to element and event params', () => {
+		const el = document.createElement('a');
+		el.href = `${baseUrl}/path/?query#hash`;
+		const event = new MouseEvent('click') as delegate.Event<MouseEvent>;
+		event.delegateTarget = el;
+
 		const ignoreVisit = jest.fn(() => true);
 		const swup = new Swup({ ignoreVisit });
+		swup.linkClickHandler(event);
+
+		expect(ignoreVisit.mock.calls).toHaveLength(1);
+		expect((ignoreVisit.mock.lastCall as any)[1]).toEqual(
+			expect.objectContaining({ el, event })
+		);
+	});
 
+	it('should be called from loadPage method', () => {
+		const ignoreVisit = jest.fn(() => true);
+		const swup = new Swup({ ignoreVisit });
 		swup.loadPage({ url: '/path/' });
 
 		expect(ignoreVisit.mock.calls).toHaveLength(1);