Collapse HackerNews Parent Comments

Adds vertical bars to the left of the comments, enabling you to easily collapse the parent comments. It also can leave only a specified number of comments expanded and auto-collapse the rest.

目前为 2021-11-23 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Collapse HackerNews Parent Comments
  3. // @description Adds vertical bars to the left of the comments, enabling you to easily collapse the parent comments. It also can leave only a specified number of comments expanded and auto-collapse the rest.
  4. // @author BLBC (github.com/hjk789, greasyfork.org/users/679182-hjk789)
  5. // @copyright 2020+, BLBC (github.com/hjk789, greasyfork.org/users/679182-hjk789)
  6. // @version 1.2.5
  7. // @homepage https://github.com/hjk789/Creations/tree/master/JavaScript/Userscripts/Collapse-HackerNews-Parent-Comments
  8. // @license https://github.com/hjk789/Creations/tree/master/JavaScript/Userscripts/Collapse-HackerNews-Parent-Comments#license
  9. // @grant none
  10. // @include https://news.ycombinator.com/item*
  11. // @namespace https://greasyfork.org/users/679182
  12. // ==/UserScript==
  13.  
  14.  
  15. //--------------- Settings -----------------
  16.  
  17. const autoCollapse = false // Whether all comments, other than the number of comments below, should be auto-collapsed.
  18. // If set to false, all comments will be left expanded and the settings below have no effect.
  19. const numberOfRoots = 5
  20. const numberOfReplies = 3
  21. const numberOfRepliesOfReplies = 1
  22.  
  23. //------------------------------------------
  24.  
  25.  
  26. const fadeOnHoverStyle = document.createElement("style")
  27. fadeOnHoverStyle.innerHTML = ".verticalBar:hover { background-color: gray !important; }"
  28. document.body.appendChild(fadeOnHoverStyle)
  29.  
  30. // HackerNews puts a 1x1 image before each comment and sets it's width according to each comment depth. Each level of depth adds 40px of width to this
  31. // image, starting from 0 which are the root comments. The ones with a 14px width are flagged comments and the "More comments" link.
  32. // This userscript was first created before HN implemented the root/parent/prev/next links, and at that time the layout didn't have any easier way of identifying
  33. // the hierarchy of the comments (it was just a list of comments pushed to the right), so that's the only way I had found to achieve this at that time.
  34.  
  35. const spacingImgs = document.querySelectorAll(".ind img[height='1']:not([width='14'])")
  36.  
  37. let root = 0
  38. let i = spacingImgs.length-1 // It's required to loop backwards, otherwise the hidden comments reappear when collapsed.
  39. let commentHier = []
  40.  
  41. const collapseAll = setInterval(function() // An interval of 1ms is being used to prevent the page from freezing until it finishes. Also, it creates a cool effect when
  42. { // the comments are being collapsed. It does make it take a few more seconds to finish in comment-heavy posts (150+) though.
  43. let commentContainer = spacingImgs[i].parentElement.parentElement.parentElement.parentElement.parentElement.parentElement
  44. commentContainer.firstChild.style = "border-top: 5px transparent solid" // To visually separate each vertical bar.
  45. spacingImgs[i].parentElement.style = "position: relative"
  46.  
  47. const clicky = commentContainer.querySelectorAll(".clicky:not(.togg)") // HN added a scrolling animation to the hierarchy links, which breaks the script.
  48. for (let i=0; i < clicky.length; i++) // The animation is only applied to elements with the class "clicky".
  49. clicky[i].className = clicky[i].className.replace("clicky","") // This removes the clicky class from every hierarchy link of the comment.
  50.  
  51. if (autoCollapse && !commentContainer.classList.contains("coll")) // Collapse only if it's not collapsed yet. This is for signed-in users, as HN remembers which comments were collapsed.
  52. commentContainer.querySelector(".togg").click()
  53.  
  54. i--
  55.  
  56. if (i == -1) // When finished collapsing all comments, now it's time to add the bars.
  57. {
  58. clearInterval(collapseAll)
  59.  
  60. for (i=0; i < spacingImgs.length; i++)
  61. {
  62. const level = spacingImgs[i].width / 40
  63. commentContainer = spacingImgs[i].parentElement.parentElement.parentElement.parentElement.parentElement.parentElement
  64. const commentToggle = commentContainer.querySelector(".togg")
  65.  
  66.  
  67. // Store the current hierarchy in an array
  68. commentHier[level] = commentToggle
  69.  
  70.  
  71. let divs = []
  72. for (let j = spacingImgs[i].width; j >= 0; j -= 40) // Start adding the vertical bar from the current depth and go backwards.
  73. {
  74. // Create the vertical bar
  75.  
  76. const div = document.createElement("div")
  77. div.className = "verticalBar"
  78. div.commentHier = commentHier[j/40] // Store in an attribute of the element this comment's parent respective to the level of the vertical bar, for easy access.
  79. div.onclick = function(e)
  80. {
  81. e.target.commentHier.click() // When a vertical bar is clicked, collapse the respective parent comment.
  82.  
  83. // Click the "next" link of the parent comment when it's out of view.
  84. if (e.target.commentHier.getBoundingClientRect().y < 0)
  85. e.target.commentHier.previousElementSibling.lastChild.click()
  86.  
  87. }
  88.  
  89. let style = "left: " + (-5 + j) + "px; width: 12px; background-color: lightgray; position: absolute; z-index: 99; transition: 0.15s; "
  90.  
  91. // Make it so that the vertical bars are only separated when followed by comments of same level of depth
  92.  
  93. if (j == spacingImgs[i].width && spacingImgs[i-1] != null && spacingImgs[i].width <= spacingImgs[i-1].width)
  94. style += "top: 5px; height: calc(100% + 8px); "
  95. else
  96. style += "top: 0px; height: calc(100% + 13px); "
  97.  
  98. div.style = style
  99.  
  100. divs.push(div)
  101. }
  102.  
  103. for (let j = divs.length - 1; j >= 0; j--)
  104. spacingImgs[i].parentElement.appendChild(divs[j])
  105. }
  106.  
  107. if (autoCollapse) // When finished collapsing and adding the vertical bars to all comments, now it's time to expand only a few of the first comments.
  108. {
  109. let sub40, sub80
  110.  
  111. for (i=0; i < spacingImgs.length; i++)
  112. {
  113. commentToggle = spacingImgs[i].parentElement.parentElement.querySelector(".togg")
  114.  
  115. if (spacingImgs[i].width == 0) // If it's a root comment.
  116. {
  117. root++
  118. if (root == numberOfRoots + 1) // If there's already <numberOfRoots> comments expanded, then stop expanding.
  119. break
  120.  
  121. commentToggle.click()
  122. sub40 = 0
  123. sub80 = 0
  124. }
  125. else if (spacingImgs[i].width == 40 && sub40 < numberOfReplies) // If it's a reply to the root comment, only expand up to <numberOfReplies>.
  126. {
  127. commentToggle.click()
  128. sub40++
  129. sub80 = 0
  130. }
  131. else if (spacingImgs[i].width == 80 && sub80 < numberOfRepliesOfReplies) // If it's a reply to the reply, only expand up to <numberOfRepliesOfReplies>.
  132. {
  133. commentToggle.click()
  134. sub80++
  135. }
  136. }
  137. }
  138. }
  139.  
  140. }, 1)
  141.