This is a convenience user script. It prevents page reloading when following links to answers that happen to be present on the same page, and to their comments; instead, it simply scrolls to the link target on the current page. A tiny warning (perhaps not significant to most people): when following links to comments in the <code>/posts/comments/<i>CommentId</i></code> form, the script may incur additional HTTP(S) traffic; this happens when following a link to a comment that has not (yet) been loaded on the current page, to check if it’s attached to a post that *is* present on the current page. Since this is a HEAD request, which consists only of HTTP headers, I estimate it to be no more than 2 kB per click (excluding TLS overhead). In the worst case, when linking to a comment for a post not present on the page, this may trigger the HEAD request, and then perform a full page reload anyway; in the middling case, when the post *is* present, full comments for that post will be loaded. To avoid this additional traffic, prefer linking to comments in a form that includes the <code>#comment<i>CommentId</i>_<i>PostId</i></code> anchor, which includes both the comment ID and the post ID to which the comment is attached. ```lang-js // ==UserScript== // @name Stack Exchange: Faster Answer Links // @grant none // @run-at document-start // @match https://*.stackexchange.com/* // @match https://*.superuser.com/* // @match https://*.stackoverflow.com/* // @match https://*.mathoverflow.net/* // @match https://*.serverfault.com/* // @match https://*.askubuntu.com/* // @match https://stackapps.com/* // @exclude https://chat.stackexchange.com/* // @exclude https://api.stackexchange.com/* // @exclude https://data.stackexchange.com/* // @exclude https://openid.stackexchange.com/* // @exclude https://contests.stackoverflow.com/* // @exclude /^https?:\/\/winterbash\d{4,}\.stackexchange\.com\// // ==/UserScript== // attaching to window because jQuery attaches *its* delegated // event handlers to document, and we need to run *after* those window.addEventListener('click', ev => { if (ev.defaultPrevented) return; const target = ev.target.closest(':any-link'); // ignore non-link clicks if (!target) return; // ignore target="_blank" links (especially on review pages) if (target.target) return; // ignore clicks with modifier keys; we presume // those would not result in normal navigation if (ev.ctrlKey || ev.altKey || ev.shiftKey || ev.metaKey) return; // ignore href="#" pseudo-links // (.getAttribute because .href returns the resolved absolute URL) if (target.getAttribute('href') === '#') return; const parseURL = u => { let m; // catch both HTTPS and older plaintext-HTTP links if (u.host !== location.host || !/^https?:$/.test(u.protocol)) return null; // /posts/comments/<comment_id> if (m = /^\/posts\/comments\/(\d+)(?=\/|$)/u.exec(u.pathname)) { return { commentId: m[1], commentPostId: null }; } // #comment<comment_id>_<post_id> if (/^\/(q|a|questions)\//u.test(u.pathname) && (m = /^#comment(\d+)_(\d+)$/u.exec(u.hash))) { return { commentId: m[1], commentPostId: m[2] }; } // /a/<answer_id> // /questions/<question_id>/<question_title>/<answer_id> if (m = /^\/(?:a|questions\/(\d+)\/[^\/]+)\/(\d+)(?=\/|$)/u.exec(u.pathname)) { return { questionId: m[1] ?? null, answerId: m[2] }; } // /q/<question_id> // /questions/<question_id>/<question_title> if (m = /^\/(?:q|questions)\/(\d+)(?=\/|$)/u.exec(u.pathname)) { const questionId = m[1]; if (m = /^#(?:answer-)?(\d+)$/u.exec(u.hash)) return { questionId, answerId: m[1] }; if (!u.hash || u.hash === '#question') return { questionId, answerId: null }; } return null; }; const insteadGoTo = target => { ev.preventDefault(); location.href = target; }; const isThereQuestion = postId => !!document.querySelector(`#question[data-questionid="${postId}"]`); const isThereAnswer = postId => !!document.getElementById(`answer-${postId}`); const isTherePost = postId => !!document.querySelector(`#answer-${postId}, #question[data-questionid="${postId}"]`) const u = new URL(target.href); const linkTarget = parseURL(u); if (!linkTarget) return; if (linkTarget.commentId != null) { if (linkTarget.commentPostId != null) { const { commentPostId, commentId } = linkTarget; if (isTherePost(commentPostId)) return insteadGoTo(`#comment${commentId}_${commentPostId}`); } else { let node; // when rerouting comment links, use the magic fragment id // (processed by Stack Exchange JS) instead of the proper // element id (natively supported in the browser) // because the latter does not work all that well // (probably something to do with 'display: contents;') // additionally, a magic fragment id will work for comments // that have not been loaded yet if (node = document.querySelector(`#comment-${linkTarget.commentId}`)) { // happy path: comment already loaded, just navigate to it const commentId = node.dataset.commentId; const commentPostId = node.closest('.comments[data-post-id]').dataset.postId; return insteadGoTo(`#comment${commentId}_${commentPostId}`); } // if there are no hidden comments on the current page, don't bother, // just do a full page reload if (!document.querySelector('.js-post-comments-component .js-show-link:not(.dno)')) { return; } // prevent default now; if fetching the target fails, // we will just redirect manually later ev.preventDefault(); (async () => { target.style.cursor = 'progress'; try { const response = await fetch( new URL(`/posts/comments/${linkTarget.commentId}`, location), { method: 'HEAD' } ); const redirectTarget = response.redirected && parseURL(new URL(response.url)); if (redirectTarget) { // the fallbacks are here because response.url of fetch requests // made from Greasemonkey for some reason omit the fragment // identifier const commentId = redirectTarget.commentId ?? linkTarget.commentId; const commentPostId = redirectTarget.commentPostId ?? redirectTarget.answerId ?? redirectTarget.questionId; if (isTherePost(commentPostId)) { // mediocre path: post is present on the page, and navigating // to the magic link should trigger loading full comments location.href = `#comment${commentId}_${commentPostId}`; return; } // sad path: post not present on the page at all, we have to follow the link console.warn( `[FAL] Failed to follow ${u.href}: post #${commentPostId} not found on the page`); } else { console.warn( `[FAL] Failed to resolve ${u.href}: response is `, response); } location.href = u.href; } finally { target.style.cursor = ''; } })().catch(e => { location.href = u.href; console.warn(`[FAL] Failed to resolve ${u.href}: error `, e); }); } } else if (linkTarget.answerId != null) { // checking question id is probably not necessary, and may // even be harmful if an answer is moved between questions if (isThereAnswer(linkTarget.answerId)) return insteadGoTo(`#answer-${linkTarget.answerId}`); } else if (linkTarget.questionId != null) { // a link to the current question is unusual, but possible; we handle this too // however, we make an exception for the link in the header, // sometimes clicked in lieu of refreshing the page if (target.matches('#question-header :any-link')) return; if (isThereQuestion(linkTarget.questionId)) return insteadGoTo('#question'); } else { console.warn(`[FAL] URL ${u} parsed, but not handled:`, linkTarget); } }, false); ```