Greasy Fork is available in English.

Hacker News Single click opener

Adds an [l+c] link that opens the url and the comments page in new tabs in one click. Inspired by Reddit Enhancement Suite (/r/Enhancement).

  1. // ==UserScript==
  2. // @name Hacker News Single click opener
  3. // @namespace selfdocumentingcode
  4. // @version 0.6
  5. // @description Adds an [l+c] link that opens the url and the comments page in new tabs in one click. Inspired by Reddit Enhancement Suite (/r/Enhancement).
  6. // @author selfdocumentingcode@github
  7. // @match https://news.ycombinator.com/*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=ycombinator.com
  9. // @grant GM_openInTab
  10. // @license MIT
  11. // ==/UserScript==
  12.  
  13. (function () {
  14. 'use strict';
  15.  
  16. console.log(window);
  17.  
  18. const pageHas4Tables = document.querySelectorAll('table').length === 4;
  19.  
  20. if (!pageHas4Tables) return;
  21.  
  22. const pageHasTableWithLinks =
  23. document.querySelectorAll('table > tbody >tr.athing > td.title > span.titleline > a').length > 0;
  24.  
  25. const pageHasTableWithCommentLinks =
  26. document.querySelectorAll('table > tbody > tr > td.subtext > span.subline > a[href^="item"]').length > 0;
  27.  
  28. const isRelevantPage = pageHasTableWithLinks && pageHasTableWithCommentLinks;
  29.  
  30. if (!isRelevantPage) return;
  31.  
  32. const tableWithLinks = document.querySelectorAll('table')[2];
  33.  
  34. const tableRows = tableWithLinks.querySelectorAll('tbody > tr');
  35.  
  36. for (let i = 0; i < tableRows.length; i++) {
  37. const tr = tableRows[i];
  38.  
  39. // Table rows are a mix of links, comments, spacers, etc.
  40. // Link rows have the class 'athing' for some reason
  41. if (!tr.classList.contains('athing')) continue;
  42.  
  43. const linkUrl = tr.querySelector('td.title > span.titleline > a')?.href;
  44.  
  45. if (!linkUrl) continue;
  46.  
  47. // Row with link to comments follows link row
  48. const commentsTr = tableRows[i + 1];
  49.  
  50. // This row and the next can now be skipped
  51. i += 2;
  52.  
  53. const subtextContainer = commentsTr.querySelector('td.subtext > span.subline');
  54.  
  55. const isJobsLink = !subtextContainer || subtextContainer.children.length === 1; // There might be a better way to test, but this fits
  56.  
  57. // Don't want to show l+c on job posts
  58. if (isJobsLink) continue;
  59.  
  60. const commentsUrl = subtextContainer.querySelector('span.age > a')?.href;
  61.  
  62. subtextContainer.appendChild(document.createTextNode(' | '));
  63.  
  64. const isExternalLink = linkUrl.indexOf('news.ycombinator.com') < 0;
  65.  
  66. const lPlusCLink = document.createElement('a');
  67. lPlusCLink.href = 'javascript:void(0)';
  68. lPlusCLink.text = isExternalLink ? '[l+c]' : '[l=c]';
  69.  
  70. lPlusCLink.onclick = () => {
  71. if (isExternalLink) {
  72. GM_openInTab(linkUrl);
  73. }
  74. GM_openInTab(commentsUrl);
  75. };
  76.  
  77. subtextContainer.appendChild(lPlusCLink);
  78. }
  79. })();