also looking at this
fix(router): a click the page already handled is not a navigation
#1948
2 files
+177
-1
| @@ -1355,6 +1355,21 @@ else { | ||
| 1355 | 1355 | // mode where every page is part of the same SPA shell. |
| 1356 | 1356 | document.addEventListener('click',function(e){ |
| 1357 | 1357 | if(e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||e.button!==0)return; |
| 1358 | // A handler on the page has already decided this click is not a | |
| 1359 | // navigation, so the router must not perform one - the same contract the | |
| 1360 | // browser honours for a plain anchor. | |
| 1361 | // | |
| 1362 | // This listener used to run in the CAPTURE phase, which broke that twice | |
| 1363 | // over: it saw the click before the anchor did, so defaultPrevented was | |
| 1364 | // necessarily false by the time it was read here, and the stopPropagation | |
| 1365 | // below then stopped the event ever reaching the element - so a page's own | |
| 1366 | // click handler on an intercepted link never ran AT ALL. Progressive | |
| 1367 | // enhancement on an anchor was impossible; the only way out was to swap it | |
| 1368 | // for a button in script (stacksjs/stacks#2393). | |
| 1369 | // | |
| 1370 | // Bubbling instead puts the page's handlers first, which is where a | |
| 1371 | // decision about the page's own markup belongs. | |
| 1372 | if(e.defaultPrevented)return; | |
| 1358 | 1373 | if(!e.target||!e.target.closest)return; |
| 1359 | 1374 | var link=e.target.closest('[data-stx-link]'); |
| 1360 | 1375 | // A marked link still has to clear the opt-out set. It used to skip it |
| @@ -1372,7 +1387,7 @@ else { | ||
| 1372 | 1387 | e.stopPropagation(); |
| 1373 | 1388 | log('[router] navigating to:',href); |
| 1374 | 1389 | navigate(withCurrentLocale(href)); |
| 1375 | },true); | |
| 1390 | }); | |
| 1376 | 1391 | |
| 1377 | 1392 | // ── Form submission ── |
| 1378 | 1393 | // A submit IS a navigation, and the router only ever watched clicks. So any |