Browser extension to hide view-reducing screen junk from websites.

content_script.js 3.6 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import browser from 'webextension-polyfill'
  2. import throttle from 'lodash/throttle'
  3. import { onEnabledChange, isEnabled } from './perOrigin'
  4. function hideBanners(elements) {
  5. const revertSteps = []
  6. /* Hide all elements with position:fixed */
  7. const fixedElements = elements
  8. .filter(el => (
  9. getComputedStyle(el).position === 'fixed'
  10. && el.style.opacity !== '0' // ignore if we already hid this one
  11. ))
  12. fixedElements.forEach(el => {
  13. const originalVisibility = el.style.visibility
  14. const originalOpacity = el.style.opacity
  15. const originalTransition = el.style.transition
  16. el.style.transition = 'opacity 1s'
  17. el.style.opacity = '0'
  18. const hiderTimeoutId = window.setTimeout(() => {
  19. el.style.visibility = 'hidden'
  20. }, 1000)
  21. revertSteps.push(() => {
  22. window.clearTimeout(hiderTimeoutId)
  23. const reverterTimeoutId = window.setTimeout(() => {
  24. el.style.transition = originalTransition
  25. }, 1000)
  26. el.style.visibility = originalVisibility
  27. el.style.opacity = originalOpacity
  28. })
  29. })
  30. /* Stop position:sticky elements from sticking. */
  31. const stickyElements = elements
  32. .filter(el => getComputedStyle(el).position === 'sticky')
  33. stickyElements.forEach(el => {
  34. const originalPosition = el.style.position
  35. el.style.position = 'relative'
  36. revertSteps.push(() => {
  37. el.style.position = originalPosition
  38. })
  39. })
  40. return revertSteps
  41. }
  42. async function main() {
  43. let revertSteps = []
  44. // XXX We read the location only once, but it may be changed by page scripts.
  45. // Fortunately we only need the origin, which should remain the same.
  46. const url = window.location.href
  47. function run(elements) {
  48. if (enabled && scrolledDown) {
  49. // Hide everything that looks annoying. Remember the steps to revert this.
  50. if (elements === undefined) {
  51. elements = Array.from(document.querySelectorAll('*'))
  52. }
  53. revertSteps = hideBanners(elements).concat(revertSteps)
  54. } else {
  55. // Unhide everything we have hidden.
  56. if (revertSteps.length > 0) {
  57. revertSteps.forEach(step => step())
  58. revertSteps = []
  59. }
  60. }
  61. }
  62. // Run whenever the extension is enabled/disabled for this domain.
  63. let enabled = await isEnabled(url)
  64. onEnabledChange(url, newValue => { enabled = newValue; run() })
  65. // Run whenever crossing the scroll threshold.
  66. let scrolledDown = window.scrollY > 20
  67. window.addEventListener('scroll', () => {
  68. // We add a hysteresis of 20px.
  69. if (window.scrollY > 20 && !scrolledDown) {
  70. scrolledDown = true
  71. run()
  72. } else if (window.scrollY <= 0 && scrolledDown) {
  73. scrolledDown = false
  74. run()
  75. }
  76. })
  77. // Run on suspicious page mutations.
  78. const observer = new MutationObserver(mutationList => {
  79. let elements = []
  80. mutationList.forEach(mutationRecord => {
  81. if (mutationRecord.type === 'attributes') {
  82. const modifiedElement = mutationRecord.target
  83. elements.push(modifiedElement)
  84. if (mutationRecord.attributeName === 'class') {
  85. // A change in class is likely to influence style of the element's offspring
  86. const children = Array.from(modifiedElement.querySelectorAll('*'))
  87. elements = elements.concat(children)
  88. }
  89. } else if (mutationRecord.type === 'childList') {
  90. const newElements = [...mutationRecord.addedNodes]
  91. .filter(node => node instanceof Element)
  92. elements = elements.concat(newElements)
  93. }
  94. })
  95. run(elements)
  96. })
  97. observer.observe(document, {
  98. subtree: true,
  99. childList: true,
  100. attributes: true,
  101. attributeFilter: ['class', 'style'],
  102. })
  103. // Run once now.
  104. run()
  105. }
  106. main()