Faster Answer Links

Trending 2 years ago

This is simply a convenience personification script. It prevents page reloading erstwhile pursuing links to answers that hap to beryllium coming connected nan aforesaid page, and to their comments; instead, it simply scrolls to nan nexus target connected nan existent page.

A mini informing (perhaps not important to astir people): erstwhile pursuing links to comments successful nan /posts/comments/CommentId form, nan book whitethorn incur further HTTP(S) traffic; this happens erstwhile pursuing a nexus to a remark that has not (yet) been loaded connected nan existent page, to cheque if it’s attached to a station that is coming connected nan existent page. Since this is simply a HEAD request, which consists only of HTTP headers, I estimate it to beryllium nary much than 2 kB per click (excluding TLS overhead). In nan worst case, erstwhile linking to a remark for a station not coming connected nan page, this whitethorn trigger nan HEAD request, and past execute a afloat page reload anyway; successful nan middling case, erstwhile nan station is present, afloat comments for that station will beryllium loaded. To debar this further traffic, for illustration linking to comments successful a shape that includes nan #commentCommentId_PostId anchor, which includes some nan remark ID and nan station ID to which nan remark is attached.

// ==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 model because jQuery attaches *its* delegated // arena handlers to document, and we request to tally *after* those window.addEventListener('click', ev => { if (ev.defaultPrevented) return; const target = ev.target.closest(':any-link'); // disregard non-link clicks if (!target) return; // disregard target="_blank" links (especially connected reappraisal pages) if (target.target) return; // disregard clicks pinch modifier keys; we presume // those would not consequence successful normal navigation if (ev.ctrlKey || ev.altKey || ev.shiftKey || ev.metaKey) return; // disregard href="#" pseudo-links // (.getAttribute because .href returns nan resolved absolute URL) if (target.getAttribute('href') === '#') return; const parseURL = u => { fto m; // drawback some 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 = caller 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}`); } other { fto node; // erstwhile rerouting remark links, usage nan magic part id // (processed by Stack Exchange JS) alternatively of nan proper // constituent id (natively supported successful nan browser) // because nan second does not activity each that well // (probably thing to do pinch 'display: contents;') // additionally, a magic part id will activity for comments // that person not been loaded yet if (node = document.querySelector(`#comment-${linkTarget.commentId}`)) { // happy path: remark already loaded, conscionable navigate to it const commentId = node.dataset.commentId; const commentPostId = node.closest('.comments[data-post-id]').dataset.postId; return insteadGoTo(`#comment${commentId}_${commentPostId}`); } // if location are nary hidden comments connected nan existent page, don't bother, // conscionable do a afloat page reload if (!document.querySelector('.js-post-comments-component .js-show-link:not(.dno)')) { return; } // forestall default now; if fetching nan target fails, // we will conscionable redirect manually later ev.preventDefault(); (async () => { target.style.cursor = 'progress'; effort { const consequence = await fetch( caller URL(`/posts/comments/${linkTarget.commentId}`, location), { method: 'HEAD' } ); const redirectTarget = response.redirected && parseURL(new URL(response.url)); if (redirectTarget) { // nan fallbacks are present because response.url of fetch requests // made from Greasemonkey for immoderate logic omit nan fragment // identifier const commentId = redirectTarget.commentId ?? linkTarget.commentId; const commentPostId = redirectTarget.commentPostId ?? redirectTarget.answerId ?? redirectTarget.questionId; if (isTherePost(commentPostId)) { // mediocre path: station is coming connected nan page, and navigating // to nan magic nexus should trigger loading afloat comments location.href = `#comment${commentId}_${commentPostId}`; return; } // sad path: station not coming connected nan page astatine all, we person to travel nan link console.warn( `[FAL] Failed to travel ${u.href}: station #${commentPostId} not recovered connected nan page`); } other { console.warn( `[FAL] Failed to resoluteness ${u.href}: consequence is `, response); } location.href = u.href; } yet { target.style.cursor = ''; } })().catch(e => { location.href = u.href; console.warn(`[FAL] Failed to resoluteness ${u.href}: correction `, e); }); } } other if (linkTarget.answerId != null) { // checking mobility id is astir apt not necessary, and may // moreover beryllium harmful if an reply is moved betwixt questions if (isThereAnswer(linkTarget.answerId)) return insteadGoTo(`#answer-${linkTarget.answerId}`); } other if (linkTarget.questionId != null) { // a nexus to nan existent mobility is unusual, but possible; we grip this too // however, we make an objection for nan nexus successful nan header, // sometimes clicked successful lieu of refreshing nan page if (target.matches('#question-header :any-link')) return; if (isThereQuestion(linkTarget.questionId)) return insteadGoTo('#question'); } other { console.warn(`[FAL] URL ${u} parsed, but not handled:`, linkTarget); } }, false);
More
close